Merge remote-tracking branch 'origin/master'

This commit is contained in:
Neil
2020-02-18 16:25:41 +01:00
52 changed files with 916 additions and 755 deletions
+33 -1
View File
@@ -1 +1,33 @@
SeedCracker
# SeedCracker
## Installation
Download and install the [fabric mod loader](https://fabricmc.net/use/).
Then download the lastest [release](https://github.com/KaptainWutax/SeedCracker/releases) of SeedCracker and put the `.jar` file in the `%appdata%/.minecraft/mods/` folder.
## Usage
Launch minercaft with the fabric loader profile.
Explore the world you want to crack the seed and find at least 5 structures or decorators (like Monument, Temple, Mansion, Treasure, Dungeon...), in the background while you explore the world, SeedCracker will collect biomes coordinates.
Then go on the main End island to load the pillars and the seed cracking process should start and take arround one minute.
## Usefull command
renderer option: `/seed render outlines (ON/OFF/XRAY)`
seed finder option: `/seed finder category (BIOMES/ORES/OTHERS/STRUCTURES) (ON/OFF)`
## Video Tutorial
https://youtu.be/1ChmLi9og8Q
## Contributors
[KaptainWutax](https://github.com/KaptainWutax) - the mod
[neil](https://www.youtube.com/channel/UCbM3acUrR8Ku6pjgRUNPnbQ/featured) - video tutorial
[Nekzuris](https://github.com/Nekzuris) - readme
+3 -3
View File
@@ -3,12 +3,12 @@ org.gradle.jvmargs=-Xmx1G
# Fabric Properties
# check these on https://fabricmc.net/use
minecraft_version=1.14.4
yarn_mappings=1.14.4+build.15
minecraft_version=1.15
yarn_mappings=1.15+build.2
loader_version=0.7.2+build.174
# Mod Properties
mod_version = 1.0.0
mod_version = 0.0.2-alpha
maven_group = kaptainwutax
archives_base_name = seedcracker
@@ -1,95 +1,39 @@
package kaptainwutax.seedcracker;
import com.google.common.collect.Lists;
import io.netty.util.internal.ConcurrentSet;
import kaptainwutax.seedcracker.cracker.*;
import kaptainwutax.seedcracker.cracker.population.PopulationData;
import kaptainwutax.seedcracker.cracker.population.DecoratorData;
import kaptainwutax.seedcracker.cracker.structure.StructureData;
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.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.MutableIntBoundingBox;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.Biomes;
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.gen.feature.Feature;
import net.minecraft.world.gen.feature.StrongholdFeature;
import net.minecraft.world.level.LevelGeneratorType;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.minecraft.world.level.LevelProperties;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
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 List<Long> structureSeeds = null;
public Set<Long> structureSeeds = null;
public List<Integer> pillarSeeds = null;
private TimeMachine timeMachine = new TimeMachine();
private List<StructureData> structureCache = new ArrayList<>();
private List<PopulationData> populationCache = new ArrayList<>();
private List<DecoratorData> decoratorCache = new ArrayList<>();
private List<BiomeData> biomeCache = new ArrayList<>();
@Override
@Override
public void onInitialize() {
RenderQueue.get().add("hand", FinderQueue.get()::renderFinders);
DecoratorCache.get().initialize();
/*
System.out.println("FETCHING SEEDS============");
long structureSeed = 29131954246896L;
ChunkPos chunkPos = new ChunkPos(117, 23);
for(long j = 0; j < (1L << 16); j++) {
long worldSeed = (j << 48) | structureSeed;
if(initialize(worldSeed).contains(chunkPos)) {
System.out.println(worldSeed);
this.checkWorldSeed(worldSeed, chunkPos);
}
}
System.out.println("FETCHING SEEDS============");
*/
/*
System.out.println(9348141881871L ^ Rand.JAVA_LCG.multiplier);
DungeonData data = new DungeonData(new ChunkPos(18, 8), Biomes.JUNGLE, new ArrayList<>(), new ArrayList<>());
data.test(-2418316773073950375L);
try {
BufferedWriter writer = new BufferedWriter(new FileWriter("kaktoos14.txt"));
for(int i = 0; i < (1 << 16); i++) {
long worldSeed = 9368770777595L | ((long)i << 48);
BiomeLayerSampler sampler = BiomeLayers.build(worldSeed, LevelGeneratorType.DEFAULT,
BiomeSourceType.VANILLA_LAYERED.getConfig().getGeneratorSettings())[1];
if(sampler.sample(8, 8) == Biomes.DESERT) {
writer.write(worldSeed + "\n");
}
}
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}*/
}
private void checkWorldSeed(long worldSeed, ChunkPos pos) {
StrongholdFeature.Start start = new StrongholdFeature.Start(Feature.STRONGHOLD, pos.x, pos.z, Biomes.PLAINS, MutableIntBoundingBox.empty(), 0, worldSeed);
}
public static SeedCracker get() {
@@ -97,12 +41,13 @@ public class SeedCracker implements ModInitializer {
}
public void clear() {
this.hashedWorldSeed = -1;
this.worldSeeds = null;
this.structureSeeds = null;
this.pillarSeeds = null;
this.structureCache.clear();
this.biomeCache.clear();
this.populationCache.clear();
this.decoratorCache.clear();
}
public synchronized boolean onPillarData(PillarData pillarData) {
@@ -138,7 +83,7 @@ public class SeedCracker implements ModInitializer {
Log.warn("Looking for structure seeds with " + this.populationCache.size() + " population features.");
this.pillarSeeds.forEach(pillarSeed -> {
timeMachine.buildStructureSeeds(pillarSeed, this.structureCache, this.populationCache, this.structureSeeds);
timeMachine.buildStructureSeeds(pillarSeed, this.structureCache, this.decoratorCache, this.structureSeeds);
});
if(this.structureSeeds.size() > 0) {
@@ -148,13 +93,12 @@ public class SeedCracker implements ModInitializer {
}
this.structureCache.clear();
this.onPopulationData(null);
this.onDecoratorData(null);
this.onBiomeData(null);
} else if(this.structureSeeds != null && structureData != null) {
this.structureSeeds.removeIf(structureSeed -> {
ChunkRandom chunkRandom = new ChunkRandom();
chunkRandom.setStructureSeed(structureSeed, structureData.getRegionX(), structureData.getRegionZ(), structureData.getSalt());
return !structureData.test(chunkRandom);
Rand rand = new Rand(0L);
return !structureData.test(structureSeed, rand);
});
this.onBiomeData(null);
@@ -163,11 +107,11 @@ public class SeedCracker implements ModInitializer {
return added;
}
public synchronized boolean onPopulationData(PopulationData populationData) {
public synchronized boolean onDecoratorData(DecoratorData decoratorData) {
boolean added = false;
if(populationData != null && !this.populationCache.contains(populationData)) {
this.populationCache.add(populationData);
if(decoratorData != null && !this.decoratorCache.contains(decoratorData)) {
this.decoratorCache.add(decoratorData);
added = true;
}
@@ -190,16 +134,33 @@ public class SeedCracker implements ModInitializer {
for(int i = 0; i < this.structureSeeds.size(); i++) {
Log.warn("Progress " + (i * 100.0f) / this.structureSeeds.size() + "%...");
long structureSeed = this.structureSeeds.get(i);
for(long structureSeed: this.structureSeeds) {
for(long j = 0; j < (1L << 16); j++) {
long worldSeed = (j << 48) | structureSeed;
long hash = LevelProperties.sha256Hash(worldSeed);
for (long j = 0; j < (1L << 16); j++) {
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;
BiomeLayerSampler sampler = BiomeLayers.build(worldSeed, LevelGeneratorType.DEFAULT,
BiomeSourceType.VANILLA_LAYERED.getConfig().getGeneratorSettings())[1];
FakeBiomeSource fakeBiomeSource = new FakeBiomeSource(worldSeed);
for(BiomeData data : this.biomeCache) {
if (!data.test(worldSeed, sampler)) {
if(!data.test(fakeBiomeSource)) {
goodSeed = false;
break;
}
@@ -209,7 +170,7 @@ public class SeedCracker implements ModInitializer {
this.worldSeeds.add(worldSeed);
}
}
}
});
if(this.worldSeeds.size() > 0) {
Log.warn("Finished search with " + this.worldSeeds + (this.worldSeeds.size() == 1 ? " seed." : " seeds."));
@@ -218,16 +179,14 @@ public class SeedCracker implements ModInitializer {
}
} else if(this.worldSeeds != null && biomeData != null) {
this.worldSeeds.removeIf(worldSeed -> {
BiomeLayerSampler sampler = BiomeLayers.build(worldSeed, LevelGeneratorType.DEFAULT,
BiomeSourceType.VANILLA_LAYERED.getConfig().getGeneratorSettings())[1];
return !biomeData.test(worldSeed, sampler);
FakeBiomeSource fakeBiomeSource = new FakeBiomeSource(worldSeed);
return !biomeData.test(fakeBiomeSource);
});
} 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;
FakeBiomeSource fakeBiomeSource = new FakeBiomeSource(worldSeed);
if(!data.test(fakeBiomeSource))return true;
}
return false;
@@ -237,116 +196,4 @@ public class SeedCracker implements ModInitializer {
return added;
}
public static void main(String[] args) throws Exception {
for(int i = 0; i < 10000; i++) {
Rand rand = new Rand(i, false);
if(rand.nextInt(700) == 0)System.out.println(i);
}
/*Random rand = new Random(1234L);
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);
});*/
/*
System.out.println(validSeed(65867021031296932L));
BufferedReader reader = new BufferedReader(new FileReader("run/seeds.txt"));
BufferedWriter writer = new BufferedWriter(new FileWriter("run/kaktoos14.txt"));
while(reader.ready()) {
long seed = Long.parseLong(reader.readLine().split(Pattern.quote(" "))[0]);
seed ^= Rand.JAVA_LCG.multiplier;
seed -= 60007;
writer.write(seed + "===========================================\n");
for(int i = 0; i < (1 << 16); i++) {
long worldSeed = seed | ((long)i << 48);
BiomeLayerSampler sampler = BiomeLayers.build(worldSeed, LevelGeneratorType.DEFAULT,
BiomeSourceType.VANILLA_LAYERED.getConfig().getGeneratorSettings())[1];
if(sampler.sample(8, 8) == Biomes.DESERT) {
writer.write(worldSeed + "\n");
}
}
writer.flush();
}
writer.close();*/
}
private static List<ChunkPos> initialize(long worldSeed) {
BiomeLayerSampler sampler = BiomeLayers.build(worldSeed, LevelGeneratorType.DEFAULT,
BiomeSourceType.VANILLA_LAYERED.getConfig().getGeneratorSettings())[0];
List<ChunkPos> startPositions = new ArrayList<>();
List<Biome> validBiomes = Lists.newArrayList();
Iterator biomeIterator = Registry.BIOME.iterator();
while(biomeIterator.hasNext()) {
Biome biome = (Biome)biomeIterator.next();
if(biome != null && biome.hasStructureFeature(Feature.STRONGHOLD)) {
validBiomes.add(biome);
}
}
Random rand = new Random(worldSeed);
double randomRadian = rand.nextDouble() * Math.PI * 2.0D;
//Actually 128, but we don't care about all of them.
for(int i = 0; i < 3; ++i) {
double double_2 = 128.0D + (rand.nextDouble() - 0.5D) * 80.0D;
int x = (int)Math.round(Math.cos(randomRadian) * double_2);
int z = (int)Math.round(Math.sin(randomRadian) * double_2);
BlockPos locatedPos = locateBiome(sampler, (x << 4) + 8, (z << 4) + 8, 112, validBiomes, rand);
if(locatedPos != null) {
x = locatedPos.getX() >> 4;
z = locatedPos.getZ() >> 4;
}
startPositions.add(new ChunkPos(x, z));
randomRadian += (Math.PI * 2.0D) / 3.0d;
}
return startPositions;
}
public static BlockPos locateBiome(BiomeLayerSampler sampler, int x, int z, int size, List<Biome> validBiomes, Random rand) {
int int_4 = x - size >> 2;
int int_5 = z - size >> 2;
int int_6 = x + size >> 2;
int int_7 = z + size >> 2;
int int_8 = int_6 - int_4 + 1;
int int_9 = int_7 - int_5 + 1;
Biome[] biomeSample = sampler.sample(int_4, int_5, int_8, int_9);
BlockPos pos = null;
int int_10 = 0;
for(int i = 0; i < int_8 * int_9; ++i) {
int int_12 = int_4 + i % int_8 << 2;
int int_13 = int_5 + i / int_8 << 2;
if (validBiomes.contains(biomeSample[i])) {
if(pos == null || rand.nextInt(int_10 + 1) == 0) {
pos = new BlockPos(int_12, 0, int_13);
}
++int_10;
}
}
return pos;
}
}
@@ -3,7 +3,7 @@ package kaptainwutax.seedcracker.cracker;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.layer.BiomeLayerSampler;
import net.minecraft.world.biome.source.VoronoiBiomeAccessType;
public class BiomeData {
@@ -25,8 +25,20 @@ public class BiomeData {
this(x, z, Registry.BIOME.get(biomeId));
}
public boolean test(long worldSeed, BiomeLayerSampler sampler) {
return sampler.sample(this.x, this.z) == this.biome;
public boolean test(FakeBiomeSource source) {
return VoronoiBiomeAccessType.INSTANCE.getBiome(source.getHashedSeed(), this.x,0, this.z, source) == this.biome;
}
public int getX() {
return this.x;
}
public int getZ() {
return this.z;
}
public Biome getBiome() {
return this.biome;
}
@Override
@@ -39,7 +39,7 @@ public class DecoratorCache {
}
private void initializeBiomeStep(Biome biome, GenerationStep.Feature genStep) {
List<ConfiguredFeature<?>> features = biome.getFeaturesForStep(genStep);
List<ConfiguredFeature<?, ?>> features = biome.getFeaturesForStep(genStep);
for(int i = 0; i < features.size(); i++) {
FeatureConfig config = features.get(i).config;
@@ -0,0 +1,49 @@
package kaptainwutax.seedcracker.cracker;
import com.google.common.collect.ImmutableSet;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.Biomes;
import net.minecraft.world.biome.layer.BiomeLayers;
import net.minecraft.world.biome.source.BiomeLayerSampler;
import net.minecraft.world.biome.source.BiomeSource;
import net.minecraft.world.gen.chunk.OverworldChunkGeneratorConfig;
import net.minecraft.world.level.LevelGeneratorType;
import net.minecraft.world.level.LevelProperties;
import java.util.Set;
public class FakeBiomeSource extends BiomeSource {
public static final OverworldChunkGeneratorConfig CHUNK_GEN_CONFIG = new OverworldChunkGeneratorConfig();
private static final Set<Biome> BIOMES;
private final BiomeLayerSampler biomeSampler;
private long seed;
private long hashedSeed;
public FakeBiomeSource(long worldSeed) {
super(BIOMES);
this.seed = worldSeed;
this.hashedSeed = LevelProperties.sha256Hash(worldSeed);
this.biomeSampler = BiomeLayers.build(this.seed, LevelGeneratorType.DEFAULT, CHUNK_GEN_CONFIG);
}
@Override
public Biome getBiomeForNoiseGen(int biomeX, int biomeY, int biomeZ) {
return this.biomeSampler.sample(biomeX, biomeZ);
}
public long getSeed() {
return this.seed;
}
public long getHashedSeed() {
return this.hashedSeed;
}
static {
BIOMES = ImmutableSet.<Biome>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);
}
}
@@ -7,39 +7,39 @@ import java.util.Random;
public class PillarData {
private List<Integer> heights;
private List<Integer> heights;
public PillarData(List<Integer> heights) {
this.heights = heights;
}
public PillarData(List<Integer> heights) {
this.heights = heights;
}
public List<Integer> getPillarSeeds() {
List<Integer> result = new ArrayList<>();
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(heights))result.add(pillarSeed);
}
for(int pillarSeed = 0; pillarSeed < (1 << 16); pillarSeed++) {
List<Integer> h = this.getPillarHeights(pillarSeed);
if(h.equals(this.heights)) result.add(pillarSeed);
}
return result;
}
return result;
}
public List<Integer> getPillarHeights(int spikeSeed) {
List<Integer> indices = new ArrayList<>();
public List<Integer> getPillarHeights(int spikeSeed) {
List<Integer> indices = new ArrayList<>();
for (int i = 0; i < 10; i++) {
indices.add(i);
}
for(int i = 0; i < 10; i++) {
indices.add(i);
}
Collections.shuffle(indices, new Random(spikeSeed));
Collections.shuffle(indices, new Random(spikeSeed));
List<Integer> heights = new ArrayList<>();
List<Integer> heights = new ArrayList<>();
for (Integer index: indices) {
heights.add(76 + index * 3);
}
for(Integer index : indices) {
heights.add(76 + index * 3);
}
return heights;
}
return heights;
}
}
@@ -1,172 +0,0 @@
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.build(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 Feature getFeature() {
return this.feature;
}
public boolean test(ChunkRandom rand) {
return this.feature.test(rand, this.offsetX, this.offsetZ);
}
@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.feature == this.feature;
}
return false;
}
public abstract static class Feature {
public final int salt;
public final int distance;
public Feature(int salt, int distance) {
this.salt = salt;
this.distance = distance;
}
public void build(StructureData data, ChunkPos chunkPos) {
int chunkX = chunkPos.x;
int chunkZ = chunkPos.z;
chunkX = chunkX < 0 ? chunkX - this.distance + 1 : chunkX;
chunkZ = chunkZ < 0 ? chunkZ - this.distance + 1 : chunkZ;
//Pick out in which region the chunk is.
int regionX = (chunkX / this.distance);
int regionZ = (chunkZ / this.distance);
data.regionX = regionX;
data.regionZ = regionZ;
regionX *= this.distance;
regionZ *= this.distance;
data.offsetX = chunkPos.x - regionX;
data.offsetZ = chunkPos.z - regionZ;
}
public abstract boolean test(ChunkRandom rand, int x, int 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 IGLOO = new Feature(14357618, 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 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 OCEAN_RUIN = new Feature(14357621, 16) {
@Override
public boolean test(ChunkRandom rand, int x, int z) {
return rand.nextInt(8) == x && rand.nextInt(8) == z;
}
};
public static final Feature SHIPWRECK = new Feature(165745295, 16) {
@Override
public boolean test(ChunkRandom rand, int x, int z) {
return rand.nextInt(8) == x && rand.nextInt(8) == z;
}
};
public static final Feature PILLAGER_OUTPOST = new Feature(165745296, 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;
}
};
public static final Feature BURIED_TREASURE = new Feature(10387320, 1) {
@Override
public boolean test(ChunkRandom rand, int x, int z) {
return rand.nextFloat() < 0.1f;
}
};
public static final Feature WOODLAND_MANSION = new Feature(10387319, 80) {
@Override
public boolean test(ChunkRandom rand, int x, int z) {
return (rand.nextInt(60) + rand.nextInt(60)) / 2 == x
&& (rand.nextInt(60) + rand.nextInt(60)) / 2 == z;
}
};
}
@@ -5,46 +5,82 @@ import kaptainwutax.seedcracker.cracker.population.PopulationData;
import kaptainwutax.seedcracker.util.Log;
import kaptainwutax.seedcracker.util.Rand;
import kaptainwutax.seedcracker.util.math.LCG;
import net.minecraft.world.gen.ChunkRandom;
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> buildStructureSeeds(int pillarSeed, List<StructureData> structureDataList, List<PopulationData> populationDataList, List<Long> structureSeeds) {
ChunkRandom chunkRandom = new ChunkRandom();
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);
for(long i = 0; i < (1L << 32); i++) {
if((i & ((1L << 28) - 1)) == 0) {
Log.warn("Progress " + (i * 100.0f) / (1L << 32) + "%...");
}
for(long i = start; i < end; i++) {
long structureSeed = this.timeMachine(i, pillarSeed);
boolean goodSeed = true;
for(StructureData structureData: structureDataList) {
if(!goodSeed)break;
chunkRandom.setStructureSeed(structureSeed, structureData.getRegionX(),
structureData.getRegionZ(), structureData.getSalt());
if(!structureData.test(chunkRandom))goodSeed = false;
if(!structureData.test(structureSeed, rand))goodSeed = false;
}
for(PopulationData populationData: populationDataList) {
for(DecoratorData decoratorData : decoratorDataList) {
if(!goodSeed)break;
if(!populationData.test(structureSeed))goodSeed = false;
if(!decoratorData.test(structureSeed))goodSeed = false;
}
if(goodSeed) {
structureSeeds.add(structureSeed);
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;
}
@@ -59,4 +95,8 @@ public class TimeMachine {
return currentSeed;
}
public void stop() {
this.isRunning = false;
}
}
@@ -0,0 +1,56 @@
package kaptainwutax.seedcracker.cracker.population;
import kaptainwutax.seedcracker.cracker.DecoratorCache;
import kaptainwutax.seedcracker.util.Rand;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.decorator.Decorator;
public abstract class DecoratorData {
private final ChunkPos chunkPos;
private final Decorator<?> decorator;
private final Biome biome;
public DecoratorData(ChunkPos chunkPos, Decorator<?> decorator, Biome biome) {
this.chunkPos = chunkPos;
this.decorator = decorator;
this.biome = biome;
}
public final boolean test(long structureSeed) {
long decoratorSeed = this.getPopulationSeed(structureSeed, this.chunkPos.x << 4, this.chunkPos.z << 4);
int salt = DecoratorCache.get().getSalt(this.biome, this.decorator, true);
if(salt == DecoratorCache.INVALID) {
return false;
}
decoratorSeed += salt;
decoratorSeed ^= Rand.JAVA_LCG.multiplier;
decoratorSeed &= Rand.JAVA_LCG.modulo - 1;
return this.testDecorator(new Rand(decoratorSeed, false));
}
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
public boolean equals(Object obj) {
if(obj == this)return true;
if(obj instanceof DecoratorData) {
DecoratorData decoratorData = ((DecoratorData)obj);
return decoratorData.chunkPos.equals(this.chunkPos) && decoratorData.decorator == this.decorator;
}
return false;
}
}
@@ -9,7 +9,7 @@ import net.minecraft.world.gen.decorator.Decorator;
import java.util.List;
public class DungeonData extends PopulationData {
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);
@@ -28,33 +28,26 @@ public class DungeonData extends PopulationData {
}
@Override
public boolean testDecorator(long decoratorSeed) {
public boolean testDecorator(Rand rand) {
if(this.starts.isEmpty())return true;
//TODO: This currently only supports 1 dungeon per chunk.
BlockPos start = this.starts.get(0);
long currentSeed = decoratorSeed;
boolean valid = false;
for(int i = 0; i < 8; i++) {
currentSeed = i == 0 ? Y_START_SKIP.nextSeed(currentSeed) : Y_SKIP.nextSeed(currentSeed);
int x = rand.nextInt(16);
int z = rand.nextInt(16);
int y = rand.nextInt(256);
if(currentSeed >> 40 == start.getY()) {
valid = true;
break;
if(y == start.getY() && x == start.getX() && z == start.getZ()) {
return true;
}
rand.nextInt(2);
rand.nextInt(2);
}
if(!valid)return false;
int x = (int)(REVERSE_SKIP.nextSeed(currentSeed) >> 44);
if(x != start.getX())return false;
int z = (int)(Rand.JAVA_LCG.nextSeed(currentSeed) >> 44);
if(z != start.getZ())return false;
return true;
return false;
}
}
@@ -10,7 +10,7 @@ import net.minecraft.world.gen.decorator.Decorator;
import java.util.List;
import java.util.stream.Collectors;
public class EmeraldOreData extends PopulationData {
public class EmeraldOreData extends DecoratorData {
public static final LCG[] SKIP = {
Rand.JAVA_LCG.combine(0),
@@ -30,19 +30,18 @@ public class EmeraldOreData extends PopulationData {
}
@Override
public boolean testDecorator(long decoratorSeed) {
public boolean testDecorator(Rand rand) {
if(this.starts.isEmpty())return true;
//TODO: This currently only supports 1 emerald per chunk.
BlockPos start = this.starts.get(0);
Rand rand = new Rand(decoratorSeed, false);
int b = rand.nextInt(6);
for(int i = 0; i < b + 3; i++) {
int x = rand.nextInt(16);
int y = rand.nextInt(28) + 4;
int z = rand.nextInt(16);
int y = rand.nextInt(28) + 4;
if(y == start.getY() && x == start.getX() && z == start.getZ()) {
return true;
@@ -6,7 +6,7 @@ import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.decorator.Decorator;
public class EndGatewayData extends PopulationData {
public class EndGatewayData extends DecoratorData {
private int xOffset;
private int zOffset;
@@ -20,9 +20,7 @@ public class EndGatewayData extends PopulationData {
}
@Override
public boolean testDecorator(long decoratorSeed) {
Rand rand = new Rand(decoratorSeed, false);
public boolean testDecorator(Rand rand) {
if(rand.nextInt(700) != 0)return false;
if(rand.nextInt(16) != this.xOffset)return false;
if(rand.nextInt(16) != this.zOffset)return false;
@@ -1,104 +0,0 @@
package kaptainwutax.seedcracker.cracker.population;
import kaptainwutax.seedcracker.cracker.DecoratorCache;
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.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.HashMap;
import java.util.List;
import java.util.Map;
public abstract class PopulationData {
private final ChunkPos chunkPos;
private final Decorator<?> decorator;
private final Biome biome;
public PopulationData(ChunkPos chunkPos, Decorator<?> decorator, Biome biome) {
this.chunkPos = chunkPos;
this.decorator = decorator;
this.biome = biome;
}
public final boolean test(long structureSeed) {
long decoratorSeed = this.getPopulationSeed(structureSeed, this.chunkPos.x << 4, this.chunkPos.z << 4);
int salt = DecoratorCache.get().getSalt(this.biome, this.decorator, true);
if(salt == DecoratorCache.INVALID) {
return false;
}
decoratorSeed += salt;
decoratorSeed ^= Rand.JAVA_LCG.multiplier;
decoratorSeed &= Rand.JAVA_LCG.modulo - 1;
return this.testDecorator(decoratorSeed);
}
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(long decoratorSeed);
@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.decorator == this.decorator;
}
return false;
}
public abstract static class Feature {
private Map<Biome, Long> CACHE = new HashMap<>();
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) {
if(CACHE.containsKey(biome)) {
return new ChunkRandom(CACHE.get(biome));
}
List<ConfiguredFeature<?>> 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());
long seed = chunkRandom.setFeatureSeed(populationSeed, i, this.genStep.ordinal());
CACHE.put(biome, seed ^ Rand.JAVA_LCG.multiplier);
return chunkRandom;
}
}
return null;
}
}
}
@@ -0,0 +1,42 @@
package kaptainwutax.seedcracker.cracker.structure;
import kaptainwutax.seedcracker.cracker.structure.type.FeatureType;
import kaptainwutax.seedcracker.util.Seeds;
import kaptainwutax.seedcracker.util.Rand;
import net.minecraft.util.math.ChunkPos;
public class StructureData {
public int chunkX;
public int chunkZ;
public int regionX;
public int regionZ;
public int offsetX;
public int offsetZ;
private final int salt;
private FeatureType<StructureData> featureType;
public StructureData(ChunkPos chunkPos, FeatureType<StructureData> featureType) {
this.featureType = featureType;
this.salt = this.featureType.salt;
this.featureType.build(this, chunkPos);
}
public boolean test(long structureSeed, Rand rand) {
Seeds.setRegionSeed(rand, structureSeed, this.regionX, this.regionZ, this.salt);
return this.featureType.test(rand, this, structureSeed);
}
@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 false;
}
}
@@ -0,0 +1,62 @@
package kaptainwutax.seedcracker.cracker.structure;
import kaptainwutax.seedcracker.cracker.structure.type.AbstractTempleType;
import kaptainwutax.seedcracker.cracker.structure.type.FeatureType;
import kaptainwutax.seedcracker.cracker.structure.type.RarityType;
import kaptainwutax.seedcracker.cracker.structure.type.TriangularType;
import kaptainwutax.seedcracker.util.Rand;
import kaptainwutax.seedcracker.util.Seeds;
public class StructureFeatures {
public static final FeatureType<StructureData> DESERT_PYRAMID = new AbstractTempleType(14357617, 32, 24);
public static final FeatureType<StructureData> IGLOO = new AbstractTempleType(14357618, 32, 24);
public static final FeatureType<StructureData> JUNGLE_TEMPLE = new AbstractTempleType(14357619, 32, 24);
public static final FeatureType<StructureData> SWAMP_HUT = new AbstractTempleType(14357620, 32, 24);
public static final FeatureType<StructureData> OCEAN_RUIN = new AbstractTempleType(14357621, 16, 8);
public static final FeatureType<StructureData> SHIPWRECK = new AbstractTempleType(165745295, 16, 8);
public static final FeatureType<StructureData> PILLAGER_OUTPOST = new AbstractTempleType(165745296, 32, 24) {
@Override
public boolean test(Rand rand, StructureData data, long structureSeed) {
if(!super.test(rand, data, structureSeed))return false;
Seeds.setWeakSeed(rand, structureSeed, data.chunkX, data.chunkZ);
return rand.nextInt(5) == 0;
}
};
public static final FeatureType<StructureData> VILLAGE = new AbstractTempleType(10387312, 32, 24);
public static final FeatureType<StructureData> END_CITY = new TriangularType(10387313, 20, 9);
public static final FeatureType<StructureData> OCEAN_MONUMENT = new TriangularType(10387313, 32, 27);
public static final FeatureType<StructureData> WOODLAND_MANSION = new TriangularType(10387319, 80, 60);
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) {
@Override
public boolean test(Rand rand, StructureData data, long structureSeed) {
Seeds.setWeakSeed(rand, structureSeed, data.chunkX, data.chunkZ);
return rand.nextInt(3) == 0
&& data.chunkX == ((data.chunkX >> 4) << 4) + 4 + rand.nextInt(8)
&& data.chunkZ == ((data.chunkZ >> 4) << 4) + 4 + rand.nextInt(8);
}
};
public static final FeatureType<StructureData> MINESHAFT = new FeatureType<StructureData>(-1, 1) {
@Override
public boolean test(Rand rand, StructureData data, long structureSeed) {
Seeds.setStructureStartSeed(rand, structureSeed, data.chunkX, data.chunkZ);
return rand.nextDouble() < 0.004D;
}
};
}
@@ -0,0 +1,20 @@
package kaptainwutax.seedcracker.cracker.structure.type;
import kaptainwutax.seedcracker.cracker.structure.StructureData;
import kaptainwutax.seedcracker.util.Rand;
public class AbstractTempleType extends FeatureType<StructureData> {
protected final int offset;
public AbstractTempleType(int salt, int distance, int offset) {
super(salt, distance);
this.offset = offset;
}
@Override
public boolean test(Rand rand, StructureData data, long structureSeed) {
return rand.nextInt(this.offset) == data.offsetX && rand.nextInt(this.offset) == data.offsetZ;
}
}
@@ -0,0 +1,43 @@
package kaptainwutax.seedcracker.cracker.structure.type;
import kaptainwutax.seedcracker.cracker.structure.StructureData;
import kaptainwutax.seedcracker.util.Rand;
import net.minecraft.util.math.ChunkPos;
public abstract class FeatureType<T extends StructureData> {
public final int salt;
public final int distance;
public FeatureType(int salt, int distance) {
this.salt = salt;
this.distance = distance;
}
public void build(T data, ChunkPos chunkPos) {
int chunkX = chunkPos.x;
int chunkZ = chunkPos.z;
data.chunkX = chunkX;
data.chunkZ = chunkZ;
chunkX = chunkX < 0 ? chunkX - this.distance + 1 : chunkX;
chunkZ = chunkZ < 0 ? chunkZ - this.distance + 1 : chunkZ;
//Pick out in which region the chunk is.
int regionX = (chunkX / this.distance);
int regionZ = (chunkZ / this.distance);
data.regionX = regionX;
data.regionZ = regionZ;
regionX *= this.distance;
regionZ *= this.distance;
data.offsetX = chunkPos.x - regionX;
data.offsetZ = chunkPos.z - regionZ;
}
public abstract boolean test(Rand rand, T data, long structureSeed);
}
@@ -0,0 +1,20 @@
package kaptainwutax.seedcracker.cracker.structure.type;
import kaptainwutax.seedcracker.cracker.structure.StructureData;
import kaptainwutax.seedcracker.util.Rand;
public class RarityType extends FeatureType<StructureData> {
private float rarity;
public RarityType(int salt, int distance, float rarity) {
super(salt, distance);
this.rarity = rarity;
}
@Override
public boolean test(Rand rand, StructureData data, long structureSeed) {
return rand.nextFloat() < this.rarity;
}
}
@@ -0,0 +1,22 @@
package kaptainwutax.seedcracker.cracker.structure.type;
import kaptainwutax.seedcracker.cracker.structure.StructureData;
import kaptainwutax.seedcracker.util.Rand;
public class TriangularType extends FeatureType<StructureData> {
protected final int peak;
public TriangularType(int salt, int distance, int peak) {
super(salt, distance);
this.peak = peak;
}
@Override
public boolean test(Rand rand, StructureData data, long structureSeed) {
return (rand.nextInt(this.peak) + rand.nextInt(this.peak)) / 2 == data.offsetX
&& (rand.nextInt(this.peak) + rand.nextInt(this.peak)) / 2 == data.offsetZ;
}
}
@@ -1,23 +0,0 @@
package kaptainwutax.seedcracker.feature.decoration;
import net.minecraft.block.Block;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
public abstract class DecorationFeature {
protected World world;
protected BlockPos pos;
public DecorationFeature(World world, BlockPos pos) {
this.world = world;
this.pos = pos;
}
public abstract void reverseSeed();
public Block getBlockAt(BlockPos pos) {
return this.world.getBlockState(pos).getBlock();
}
}
@@ -1,91 +0,0 @@
package kaptainwutax.seedcracker.feature.decoration;
import kaptainwutax.seedcracker.util.Rand;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3i;
import net.minecraft.world.World;
import java.util.ArrayList;
import java.util.List;
public class DungeonFeature extends DecorationFeature {
private static int MOSSY_COBBLESTONE_CALL = 0;
private static int COBBLESTONE_CALL = 1;
public DungeonFeature(World world, BlockPos pos) {
super(world, pos);
}
@Override
public void reverseSeed() {
BlockPos decoratorPos = this.getLocalPos(this.pos);
Vec3i dungeonSize = this.getDungeonSize(this.pos);
List<Integer> floorCalls = new ArrayList<>();
for(int xo = -dungeonSize.getX(); xo <= dungeonSize.getX(); xo++) {
for(int zo = -dungeonSize.getZ(); zo <= dungeonSize.getZ(); zo++) {
Block block = this.world.getBlockState(this.pos.add(xo, -1, zo)).getBlock();
if(block == Blocks.MOSSY_COBBLESTONE) {
floorCalls.add(MOSSY_COBBLESTONE_CALL);
} else if(block == Blocks.COBBLESTONE) {
floorCalls.add(COBBLESTONE_CALL);
}
}
}
List<Long> dungeonSeeds = this.getDungeonSeeds(dungeonSize, floorCalls);
List<Long> decoratorSeeds = new ArrayList<>();
for(long dungeonSeed: dungeonSeeds) {
long decoratorSeed = Rand.JAVA_LCG.combine(-3).nextSeed(dungeonSeed);
Rand rand = new Rand(decoratorSeed, false);
if(rand.nextInt(16) != decoratorPos.getX())continue;
if(rand.nextInt(256) != decoratorPos.getY())continue;
if(rand.nextInt(16) != decoratorPos.getZ())continue;
decoratorSeeds.add(decoratorSeed);
}
decoratorSeeds.forEach(System.out::println);
}
public BlockPos getLocalPos(BlockPos pos) {
return new BlockPos(pos.getX() & 15, pos.getY(), pos.getZ() & 15);
}
public Vec3i getDungeonSize(BlockPos spawnerPos) {
for(int xo = 4; xo >= 3; xo--) {
for(int zo = 4; zo >= 3; zo--) {
Block block = this.getBlockAt(spawnerPos.add(xo, -1, zo));
if(block != Blocks.MOSSY_COBBLESTONE)continue;
if(block != Blocks.COBBLESTONE)continue;
return new Vec3i(xo, 0, zo);
}
}
return Vec3i.ZERO;
}
public List<Long> getDungeonSeeds(Vec3i dungeonSize, List<Integer> floorCalls) {
List<Long> floorSeeds = new ArrayList<>();
//TODO: Lattice magic to find floorSeeds from floorCalls.
List<Long> dungeonSeeds = new ArrayList<>();
for(long floorSeed: floorSeeds) {
long dungeonSeed = Rand.JAVA_LCG.combine(-2).nextSeed(floorSeed);
Rand rand = new Rand(dungeonSeed, false);
if(rand.nextInt(2) + 3 != dungeonSize.getX())continue;
if(rand.nextInt(2) + 3 != dungeonSize.getZ())continue;
dungeonSeeds.add(dungeonSeed);
}
return dungeonSeeds;
}
}
@@ -9,6 +9,7 @@ import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.Heightmap;
import net.minecraft.world.World;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.Biomes;
import net.minecraft.world.dimension.DimensionType;
import java.util.ArrayList;
@@ -29,6 +30,11 @@ public class BiomeFinder extends Finder {
BlockPos blockPos = this.chunkPos.getCenterBlockPos().add(x, 0, z);
Biome biome = this.world.getBiome(blockPos);
//TODO: Fix this multi-threading issue.
if(biome == Biomes.THE_VOID) {
continue;
}
if(SeedCracker.get().onBiomeData(new BiomeData(blockPos.getX(), blockPos.getZ(), biome))) {
blockPos = this.world.getTopPosition(Heightmap.Type.WORLD_SURFACE, blockPos).down();
result.add(blockPos);
@@ -17,7 +17,7 @@ public abstract class BlockFinder extends Finder {
public BlockFinder(World world, ChunkPos chunkPos, Block block) {
super(world, chunkPos);
this.targetBlockStates.addAll(block.getStateFactory().getStates());
this.targetBlockStates.addAll(block.getStateManager().getStates());
}
public BlockFinder(World world, ChunkPos chunkPos, BlockState... blockStates) {
@@ -3,7 +3,6 @@ package kaptainwutax.seedcracker.finder;
public class DefaultFinderConfig extends FinderConfig {
public DefaultFinderConfig() {
super();
this.typeStates.put(Type.DIAMOND_ORE, false);
this.typeStates.put(Type.INFESTED_STONE_ORE, false);
this.typeStates.put(Type.IGLOO, false);
@@ -55,16 +55,17 @@ public abstract class Finder {
DimensionType playerDim = mc.player.world.dimension.getType();
if(finderDim != playerDim)return false;
Vec3d playerPos = mc.player.getPos();
double distance = playerPos.squaredDistanceTo(
this.chunkPos.x * 16,
playerPos.y,
this.chunkPos.z * 16
);
int renderDistance = mc.options.viewDistance * 16 + 16;
return distance <= renderDistance * renderDistance + 32;
Vec3d playerPos = mc.player.getPos();
for(Renderer renderer: this.renderers) {
BlockPos pos = renderer.getPos();
double distance = playerPos.squaredDistanceTo(pos.getX(), playerPos.y, pos.getZ());
if(distance <= renderDistance * renderDistance + 32)return true;
}
return false;
}
public void render() {
@@ -75,6 +75,7 @@ public class FinderConfig {
MONUMENT(OceanMonumentFinder::create, Category.STRUCTURES),
SWAMP_HUT(SwampHutFinder::create, Category.STRUCTURES),
MANSION(MansionFinder::create, Category.STRUCTURES),
SHIPWRECK(ShipwreckFinder::create, Category.STRUCTURES),
END_PILLARS(EndPillarsFinder::create, Category.OTHERS),
END_GATEWAY(EndGatewayFinder::create, Category.OTHERS),
@@ -1,6 +1,8 @@
package kaptainwutax.seedcracker.finder;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.World;
@@ -40,9 +42,12 @@ public class FinderQueue {
});
}
public void renderFinders() {
public void renderFinders(MatrixStack matrixStack) {
if(this.renderType == RenderType.OFF)return;
RenderSystem.pushMatrix();
RenderSystem.multMatrix(matrixStack.peek().getModel());
GlStateManager.disableTexture();
//Makes it render through blocks.
@@ -56,11 +61,7 @@ public class FinderQueue {
}
});
GlStateManager.enableTexture();
if(this.renderType == RenderType.XRAY) {
GlStateManager.enableDepthTest();
}
RenderSystem.popMatrix();
}
public void clear() {
@@ -69,7 +69,7 @@ public class DungeonFinder extends BlockFinder {
.map(pos -> this.getFloorCalls(this.getDungeonSize(pos), pos)).collect(Collectors.toList());
result.forEach(pos -> {
if(SeedCracker.get().onPopulationData(new DungeonData(this.chunkPos, biome, starts, floorCallsList))) {
if(SeedCracker.get().onDecoratorData(new DungeonData(this.chunkPos, biome, starts, floorCallsList))) {
this.renderers.add(new Cube(pos, new Vector4f(1.0f, 0.0f, 0.0f, 1.0f)));
}
});
@@ -48,7 +48,7 @@ public class EndGatewayFinder extends BlockFinder {
if(height >= 3 && height <= 9) {
newResult.add(pos);
if(SeedCracker.get().onPopulationData(new EndGatewayData(this.chunkPos, biome, pos, height))) {
if(SeedCracker.get().onDecoratorData(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)));
}
}
@@ -21,6 +21,8 @@ import java.util.List;
public class EmeraldOreFinder extends BlockFinder {
protected static List<BlockPos> SEARCH_POSITIONS = Finder.buildSearchPositions(Finder.CHUNK_POSITIONS, pos -> {
if(pos.getY() < 4)return true;
if(pos.getY() > 28 + 4)return true;
return false;
});
@@ -39,7 +41,7 @@ public class EmeraldOreFinder extends BlockFinder {
List<BlockPos> result = super.findInChunk();
if(!result.isEmpty() && SeedCracker.get().onPopulationData(new EmeraldOreData(this.chunkPos, biome, result))) {
if(!result.isEmpty() && SeedCracker.get().onDecoratorData(new EmeraldOreData(this.chunkPos, biome, result))) {
result.forEach(pos -> {
this.renderers.add(new Cube(pos, new Vector4f(0.0f, 1.0f, 0.0f, 1.0f)));
});
@@ -1,7 +1,8 @@
package kaptainwutax.seedcracker.finder.structure;
import kaptainwutax.seedcracker.SeedCracker;
import kaptainwutax.seedcracker.cracker.StructureData;
import kaptainwutax.seedcracker.cracker.structure.StructureData;
import kaptainwutax.seedcracker.cracker.structure.StructureFeatures;
import kaptainwutax.seedcracker.finder.BlockFinder;
import kaptainwutax.seedcracker.finder.Finder;
import kaptainwutax.seedcracker.render.Cube;
@@ -26,7 +27,7 @@ public class BuriedTreasureFinder extends BlockFinder {
int localX = pos.getX() & 15;
int localZ = pos.getZ() & 15;
if(localX != 9 || localZ != 9)return true;
if(pos.getY() > 90)return true;
return false;
});
@@ -55,28 +56,23 @@ public class BuriedTreasureFinder extends BlockFinder {
@Override
public List<BlockPos> findInChunk() {
//Gets all the positions with a chest in the chunk.
Biome biome = world.getBiome(this.chunkPos.getCenterBlockPos().add(9, 0, 9));
if(!biome.hasStructureFeature(Feature.BURIED_TREASURE))return new ArrayList<>();
List<BlockPos> result = super.findInChunk();
result.removeIf(pos -> {
//Chest can't be waterlogged!
BlockState chest = world.getBlockState(pos);
if(chest.get(ChestBlock.WATERLOGGED))return true;
//Only so many blocks can hold a treasure chest.
BlockState chestHolder = world.getBlockState(pos.down());
if(!CHEST_HOLDERS.contains(chestHolder))return true;
//Check if the biome contains the buried treasure feature.
Biome biome = world.getBiome(pos);
if(!biome.hasStructureFeature(Feature.BURIED_TREASURE))return true;
//Damn that chest be lucky!
return false;
});
result.forEach(pos -> {
if(SeedCracker.get().onStructureData(new StructureData(this.chunkPos, StructureData.BURIED_TREASURE))) {
if(SeedCracker.get().onStructureData(new StructureData(this.chunkPos, StructureFeatures.BURIED_TREASURE))) {
this.renderers.add(new Cube(pos, new Vector4f(1.0f, 1.0f, 0.0f, 1.0f)));
}
});
@@ -1,7 +1,8 @@
package kaptainwutax.seedcracker.finder.structure;
import kaptainwutax.seedcracker.SeedCracker;
import kaptainwutax.seedcracker.cracker.StructureData;
import kaptainwutax.seedcracker.cracker.structure.StructureData;
import kaptainwutax.seedcracker.cracker.structure.StructureFeatures;
import kaptainwutax.seedcracker.finder.Finder;
import kaptainwutax.seedcracker.render.Cuboid;
import net.minecraft.block.BlockState;
@@ -35,7 +36,7 @@ public class DesertTempleFinder extends AbstractTempleFinder {
combinedResult.addAll(positions);
positions.forEach(pos -> {
if( SeedCracker.get().onStructureData(new StructureData(this.chunkPos, StructureData.DESERT_PYRAMID))) {
if( SeedCracker.get().onStructureData(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)));
}
});
@@ -1,7 +1,8 @@
package kaptainwutax.seedcracker.finder.structure;
import kaptainwutax.seedcracker.SeedCracker;
import kaptainwutax.seedcracker.cracker.StructureData;
import kaptainwutax.seedcracker.cracker.structure.StructureData;
import kaptainwutax.seedcracker.cracker.structure.StructureFeatures;
import kaptainwutax.seedcracker.finder.Finder;
import kaptainwutax.seedcracker.render.Cube;
import kaptainwutax.seedcracker.render.Cuboid;
@@ -85,7 +86,7 @@ public class EndCityFinder extends Finder {
combinedResult.addAll(positions);
positions.forEach(pos -> {
if(SeedCracker.get().onStructureData(new StructureData(this.chunkPos, StructureData.END_CITY))) {
if(SeedCracker.get().onStructureData(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)));
}
@@ -1,7 +1,8 @@
package kaptainwutax.seedcracker.finder.structure;
import kaptainwutax.seedcracker.SeedCracker;
import kaptainwutax.seedcracker.cracker.StructureData;
import kaptainwutax.seedcracker.cracker.structure.StructureData;
import kaptainwutax.seedcracker.cracker.structure.StructureFeatures;
import kaptainwutax.seedcracker.finder.Finder;
import kaptainwutax.seedcracker.render.Cube;
import kaptainwutax.seedcracker.render.Cuboid;
@@ -44,7 +45,7 @@ public class IglooFinder extends AbstractTempleFinder {
combinedResult.addAll(positions);
positions.forEach(pos -> {
if(SeedCracker.get().onStructureData(new StructureData(this.chunkPos, StructureData.IGLOO))) {
if(SeedCracker.get().onStructureData(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)));
}
@@ -1,7 +1,8 @@
package kaptainwutax.seedcracker.finder.structure;
import kaptainwutax.seedcracker.SeedCracker;
import kaptainwutax.seedcracker.cracker.StructureData;
import kaptainwutax.seedcracker.cracker.structure.StructureData;
import kaptainwutax.seedcracker.cracker.structure.StructureFeatures;
import kaptainwutax.seedcracker.finder.Finder;
import kaptainwutax.seedcracker.render.Cuboid;
import net.minecraft.block.*;
@@ -35,7 +36,7 @@ public class JungleTempleFinder extends AbstractTempleFinder {
combinedResult.addAll(positions);
positions.forEach(pos -> {
if(SeedCracker.get().onStructureData(new StructureData(this.chunkPos, StructureData.JUNGLE_TEMPLE))) {
if(SeedCracker.get().onStructureData(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)));
}
});
@@ -1,7 +1,8 @@
package kaptainwutax.seedcracker.finder.structure;
import kaptainwutax.seedcracker.SeedCracker;
import kaptainwutax.seedcracker.cracker.StructureData;
import kaptainwutax.seedcracker.cracker.structure.StructureData;
import kaptainwutax.seedcracker.cracker.structure.StructureFeatures;
import kaptainwutax.seedcracker.finder.Finder;
import kaptainwutax.seedcracker.render.Cube;
import kaptainwutax.seedcracker.render.Cuboid;
@@ -13,7 +14,9 @@ import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3i;
import net.minecraft.world.World;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.gen.feature.Feature;
import java.util.ArrayList;
import java.util.HashMap;
@@ -23,7 +26,6 @@ import java.util.Map;
public class MansionFinder extends Finder {
protected static List<BlockPos> SEARCH_POSITIONS = buildSearchPositions(CHUNK_POSITIONS, pos -> {
if(pos.getY() != 64)return true;
if((pos.getX() & 15) != 0)return true;
if((pos.getZ() & 15) != 0)return true;
return false;
@@ -35,31 +37,32 @@ public class MansionFinder extends Finder {
public MansionFinder(World world, ChunkPos chunkPos) {
super(world, chunkPos);
Direction.Type.HORIZONTAL.forEach(direction -> {
PieceFinder finder = new PieceFinder(world, chunkPos, direction, size);
for(Direction direction: Direction.values()) {
PieceFinder finder = new PieceFinder(world, chunkPos, direction, this.size);
finder.searchPositions = SEARCH_POSITIONS;
buildStructure(finder);
this.finders.add(finder);
});
}
}
@Override
public List<BlockPos> findInChunk() {
Biome biome = this.world.getBiome(this.chunkPos.getCenterBlockPos().add(9, 0, 9));
if(!biome.hasStructureFeature(Feature.WOODLAND_MANSION)) {
return new ArrayList<>();
}
Map<PieceFinder, List<BlockPos>> result = this.findInChunkPieces();
List<BlockPos> combinedResult = new ArrayList<>();
result.forEach((pieceFinder, positions) -> {
positions.removeIf(pos -> {
//Figure this out, it's not a trivial task.
return false;
});
combinedResult.addAll(positions);
positions.forEach(pos -> {
if(SeedCracker.get().onStructureData(new StructureData(this.chunkPos, StructureData.WOODLAND_MANSION))) {
if(SeedCracker.get().onStructureData(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)));
}
@@ -80,12 +83,26 @@ public class MansionFinder extends Finder {
}
public void buildStructure(PieceFinder finder) {
BlockState air = Blocks.AIR.getDefaultState();
BlockState cobblestone = Blocks.COBBLESTONE.getDefaultState();
BlockState birchPlanks = Blocks.BIRCH_PLANKS.getDefaultState();
BlockState redCarpet = Blocks.RED_CARPET.getDefaultState();
BlockState whiteCarpet = Blocks.WHITE_CARPET.getDefaultState();
//TODO: Finish this.
finder.fillWithOutline(0, 0, 0, 15, 0, 15, birchPlanks, birchPlanks, false);
finder.fillWithOutline(0, 0, 8, 6, 0, 12, null, null, false);
finder.fillWithOutline(0, 0, 12, 9, 0, 15, null, null, false);
finder.fillWithOutline(15, 0, 0, 15, 0, 15, cobblestone, cobblestone, false);
finder.addBlock(Blocks.DARK_OAK_LOG.getDefaultState(), 15, 0, 15);
finder.addBlock(Blocks.DARK_OAK_LOG.getDefaultState(), 15, 0, 7);
finder.addBlock(Blocks.DARK_OAK_LOG.getDefaultState(), 14, 0, 7);
finder.fillWithOutline(9, 1, 0, 9, 1, 8, whiteCarpet, whiteCarpet, false);
finder.addBlock(whiteCarpet, 8,1, 8);
finder.fillWithOutline(13, 1, 0, 13, 1, 8, whiteCarpet, whiteCarpet, false);
finder.addBlock(whiteCarpet, 14,1, 8);
finder.fillWithOutline(10, 1, 0, 12, 1, 15, redCarpet, redCarpet, false);
}
@Override
@@ -1,7 +1,8 @@
package kaptainwutax.seedcracker.finder.structure;
import kaptainwutax.seedcracker.SeedCracker;
import kaptainwutax.seedcracker.cracker.StructureData;
import kaptainwutax.seedcracker.cracker.structure.StructureData;
import kaptainwutax.seedcracker.cracker.structure.StructureFeatures;
import kaptainwutax.seedcracker.finder.Finder;
import kaptainwutax.seedcracker.render.Cube;
import kaptainwutax.seedcracker.render.Cuboid;
@@ -57,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, StructureData.OCEAN_MONUMENT))) {
if(SeedCracker.get().onStructureData(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)));
}
@@ -18,7 +18,7 @@ import java.util.Map;
public class PieceFinder extends Finder {
protected Map<BlockPos, BlockState> structure = new LinkedHashMap<>();
private MutableIntBoundingBox boundingBox;
private BlockBox boundingBox;
protected List<BlockPos> searchPositions = new ArrayList<>();
protected Direction facing;
@@ -40,12 +40,12 @@ public class PieceFinder extends Finder {
this.depth = size.getZ();
if(this.facing.getAxis() == Direction.Axis.Z) {
this.boundingBox = new MutableIntBoundingBox(
this.boundingBox = new BlockBox(
0, 0, 0,
size.getX() - 1, size.getY() - 1, size.getZ() - 1
);
} else {
this.boundingBox = new MutableIntBoundingBox(
this.boundingBox = new BlockBox(
0, 0, 0,
size.getZ() - 1, size.getY() - 1, size.getX() - 1
);
@@ -71,7 +71,7 @@ public class PieceFinder extends Finder {
//FOR DEBUGGING PIECES.
if(this.debug) {
MinecraftClient.getInstance().execute(() -> {
int y = this.rotation.ordinal() * 10 + this.mirror.ordinal() * 20 + 100;
int y = this.rotation.ordinal() * 10 + this.mirror.ordinal() * 20 + 120;
if (this.chunkPos.x % 2 == 0 && this.chunkPos.z % 2 == 0) {
this.structure.forEach((pos, state) -> {
@@ -89,7 +89,7 @@ public class PieceFinder extends Finder {
BlockState state = this.world.getBlockState(pos);
//Blockstate may change when it gets placed in the world, that's why it's using the block here.
if(!state.getBlock().equals(entry.getValue().getBlock())) {
if(entry.getValue() != null && !state.getBlock().equals(entry.getValue().getBlock())) {
found = false;
break;
}
@@ -200,10 +200,6 @@ public class PieceFinder extends Finder {
}
protected void addBlock(BlockState state, int x, int y, int z) {
if(state == null) {
return;
}
BlockPos pos = new BlockPos(
this.applyXTransform(x, z),
this.applyYTransform(y),
@@ -211,6 +207,11 @@ public class PieceFinder extends Finder {
);
if(this.boundingBox.contains(pos)) {
if(state == null) {
this.structure.remove(pos);
return;
}
if (this.mirror != BlockMirror.NONE) {
state = state.mirror(this.mirror);
}
@@ -219,6 +220,7 @@ public class PieceFinder extends Finder {
state = state.rotate(this.rotation);
}
this.structure.put(pos, state);
}
}
@@ -0,0 +1,211 @@
package kaptainwutax.seedcracker.finder.structure;
import kaptainwutax.seedcracker.SeedCracker;
import kaptainwutax.seedcracker.cracker.structure.StructureData;
import kaptainwutax.seedcracker.cracker.structure.StructureFeatures;
import kaptainwutax.seedcracker.finder.BlockFinder;
import kaptainwutax.seedcracker.finder.Finder;
import kaptainwutax.seedcracker.render.Cube;
import kaptainwutax.seedcracker.render.Cuboid;
import net.minecraft.block.*;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.ChestBlockEntity;
import net.minecraft.block.enums.ChestType;
import net.minecraft.client.util.math.Vector4f;
import net.minecraft.util.math.BlockBox;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.World;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.gen.feature.Feature;
import java.util.ArrayList;
import java.util.List;
public class ShipwreckFinder extends BlockFinder {
protected static List<BlockPos> SEARCH_POSITIONS = Finder.buildSearchPositions(Finder.CHUNK_POSITIONS, pos -> {
return false;
});
public ShipwreckFinder(World world, ChunkPos chunkPos) {
super(world, chunkPos, Blocks.CHEST);
this.searchPositions = SEARCH_POSITIONS;
}
@Override
public List<BlockPos> findInChunk() {
Biome biome = this.world.getBiome(this.chunkPos.getCenterBlockPos().add(9, 0, 9));
if(!biome.hasStructureFeature(Feature.SHIPWRECK)) {
return new ArrayList<>();
}
List<BlockPos> result = super.findInChunk();
result.removeIf(pos -> {
BlockState state = this.world.getBlockState(pos);
if(state.get(ChestBlock.CHEST_TYPE) != ChestType.SINGLE)return true;
BlockEntity blockEntity = this.world.getBlockEntity(pos);
if(!(blockEntity instanceof ChestBlockEntity))return true;
return !this.onChestFound(pos);
});
return result;
}
/**
* Source: https://github.com/skyrising/casual-mod/blob/master/src/main/java/de/skyrising/casual/ShipwreckFinder.java
* */
private boolean onChestFound(BlockPos pos) {
BlockPos.Mutable mutablePos = new BlockPos.Mutable(pos);
Direction chestFacing = world.getBlockState(pos).get(ChestBlock.FACING);
int[] stairs = new int[4];
int totalStairs = 0;
int[] trapdoors = new int[4];
int totalTrapdoors = 0;
for(int y = -1; y <= 2; y++) {
for(int x = -1; x <= 1; x++) {
for(int z = -1; z <= 1; z++) {
if (x == 0 && y == 0 && z == 0)continue;
mutablePos.set(pos.getX() + x, pos.getY() + y, pos.getZ() + z);
BlockState neighborState = world.getBlockState(mutablePos);
Block neighborBlock = neighborState.getBlock();
if(neighborBlock == Blocks.VOID_AIR)return false;
if(neighborBlock instanceof StairsBlock) {
stairs[y + 1]++;
totalStairs++;
} else if(neighborBlock instanceof TrapdoorBlock) {
trapdoors[y + 1]++;
totalTrapdoors++;
}
}
}
}
//System.out.printf("%s: chest facing %s\n", pos, chestFacing);
int chestX = 4;
int chestY = 2;
int chestZ = 0;
int length = 16;
int height = 9;
Direction direction = chestFacing;
if(trapdoors[3] > 4) { // with_mast[_degraded]
chestZ = 9;
height = 21;
length = 28;
} else if(totalTrapdoors == 0 && stairs[3] == 3) { // upsidedown_backhalf[_degraded]
if(stairs[0] == 0) {
chestX = 2;
chestZ = 12;
direction = chestFacing.getOpposite();
} else { // redundant
chestX = 3;
chestY = 5;
chestZ = 5;
direction = chestFacing.rotateYClockwise();
}
} else if(totalTrapdoors == 0) { // rightsideup that have backhalf
if(stairs[0] == 4) {
if(totalStairs > 4) {
chestX = 6;
chestY = 4;
chestZ = 12;
direction = chestFacing.getOpposite();
} else { // sideways backhalf
chestX = 6;
chestY = 3;
chestZ = 8;
length = 17;
direction = chestFacing.getOpposite();
}
} else if(stairs[0] == 3 && totalStairs > 5) {
chestX = 5;
chestZ = 6;
direction = chestFacing.rotateYCounterclockwise();
}
mutablePos.set(pos);
mutablePos.setOffset(0, -chestY, 0);
mutablePos.setOffset(direction.rotateYClockwise(), chestX - 4);
mutablePos.setOffset(direction, -chestZ - 1);
if(this.world.getBlockState(mutablePos).getMaterial() == Material.WOOD) {
if(length == 17) { // sideways
chestZ += 11;
length += 11;
} else {
chestZ += 12;
length += 12;
}
mutablePos.setOffset(0, 10, 0);
if(this.world.getBlockState(mutablePos).getBlock() instanceof LogBlock) {
height = 21;
}
}
} else if(totalTrapdoors == 2 && trapdoors[3] == 2 && stairs[3] == 3) { // rightsideup_fronthalf[_degraded]
chestZ = 8;
length = 24;
}
if(chestZ != 0) {
mutablePos.set(pos);
mutablePos.setOffset(direction, 15 - chestZ);
mutablePos.setOffset(direction.rotateYClockwise(), chestX - 4);
BlockPos.Mutable pos2 = new BlockPos.Mutable(mutablePos);
pos2.setOffset(0, -chestY, 0);
pos2.setOffset(direction, -15);
pos2.setOffset(direction.rotateYClockwise(), 4);
BlockPos.Mutable pos3 = new BlockPos.Mutable(pos2);
pos3.setOffset(direction, length - 1);
pos3.setOffset(direction.rotateYClockwise(), -8);
pos3.setOffset(0, height - 1, 0);
BlockBox box = new BlockBox(
Math.min(pos2.getX(), pos3.getX()), pos2.getY(), Math.min(pos2.getZ(), pos3.getZ()),
Math.max(pos2.getX(), pos3.getX()), pos3.getY(), Math.max(pos2.getZ(), pos3.getZ()));
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))) {
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;
}
}
}
return false;
}
@Override
public boolean isValidDimension(DimensionType dimension) {
return dimension == DimensionType.OVERWORLD;
}
public static List<Finder> create(World world, ChunkPos chunkPos) {
List<Finder> finders = new ArrayList<>();
finders.add(new ShipwreckFinder(world, chunkPos));
finders.add(new ShipwreckFinder(world, new ChunkPos(chunkPos.x - 1, chunkPos.z)));
finders.add(new ShipwreckFinder(world, new ChunkPos(chunkPos.x, chunkPos.z - 1)));
finders.add(new ShipwreckFinder(world, new ChunkPos(chunkPos.x - 1, chunkPos.z - 1)));
finders.add(new ShipwreckFinder(world, new ChunkPos(chunkPos.x + 1, chunkPos.z)));
finders.add(new ShipwreckFinder(world, new ChunkPos(chunkPos.x, chunkPos.z + 1)));
finders.add(new ShipwreckFinder(world, new ChunkPos(chunkPos.x + 1, chunkPos.z + 1)));
finders.add(new ShipwreckFinder(world, new ChunkPos(chunkPos.x - 1, chunkPos.z - 1)));
finders.add(new ShipwreckFinder(world, new ChunkPos(chunkPos.x - 1, chunkPos.z + 1)));
return finders;
}
}
@@ -1,7 +1,8 @@
package kaptainwutax.seedcracker.finder.structure;
import kaptainwutax.seedcracker.SeedCracker;
import kaptainwutax.seedcracker.cracker.StructureData;
import kaptainwutax.seedcracker.cracker.structure.StructureData;
import kaptainwutax.seedcracker.cracker.structure.StructureFeatures;
import kaptainwutax.seedcracker.finder.Finder;
import kaptainwutax.seedcracker.render.Cuboid;
import net.minecraft.block.BlockState;
@@ -36,7 +37,7 @@ public class SwampHutFinder extends AbstractTempleFinder {
combinedResult.addAll(positions);
positions.forEach(pos -> {
if(SeedCracker.get().onStructureData(new StructureData(this.chunkPos, StructureData.SWAMP_HUT))) {
if(SeedCracker.get().onStructureData(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)));
}
});
@@ -2,6 +2,7 @@ package kaptainwutax.seedcracker.mixin;
import com.mojang.authlib.GameProfile;
import com.mojang.brigadier.CommandDispatcher;
import kaptainwutax.seedcracker.SeedCracker;
import kaptainwutax.seedcracker.command.ClientCommands;
import kaptainwutax.seedcracker.finder.FinderQueue;
import net.minecraft.client.MinecraftClient;
@@ -9,6 +10,7 @@ import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.client.network.packet.ChunkDataS2CPacket;
import net.minecraft.client.network.packet.CommandTreeS2CPacket;
import net.minecraft.client.network.packet.PlayerRespawnS2CPacket;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.network.ClientConnection;
import net.minecraft.server.command.CommandSource;
@@ -44,4 +46,9 @@ public abstract class ClientPlayNetworkHandlerMixin {
ClientCommands.registerCommands((CommandDispatcher<ServerCommandSource>)(Object)this.commandDispatcher);
}
@Inject(method = "onPlayerRespawn", at = @At("HEAD"))
public void onPlayerRespawn(PlayerRespawnS2CPacket packet, CallbackInfo ci) {
SeedCracker.get().hashedWorldSeed = packet.method_22425();
}
}
@@ -1,14 +1,15 @@
package kaptainwutax.seedcracker.mixin;
import kaptainwutax.seedcracker.finder.FinderQueue;
import kaptainwutax.seedcracker.SeedCracker;
import kaptainwutax.seedcracker.finder.FinderQueue;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.Biomes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.concurrent.Executors;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ClientWorld.class)
public abstract class ClientWorldMixin {
@@ -17,8 +18,11 @@ public abstract class ClientWorldMixin {
private void disconnect(CallbackInfo ci) {
SeedCracker.get().clear();
FinderQueue.get().clear();
FinderQueue.SERVICE.shutdown();
FinderQueue.SERVICE = Executors.newFixedThreadPool(5);
}
@Inject(method = "getGeneratorStoredBiome", at = @At("HEAD"), cancellable = true)
private void getGeneratorStoredBiome(int x, int y, int z, CallbackInfoReturnable<Biome> ci) {
ci.setReturnValue(Biomes.THE_VOID);
}
}
@@ -2,6 +2,7 @@ package kaptainwutax.seedcracker.mixin;
import kaptainwutax.seedcracker.render.RenderQueue;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.util.math.MatrixStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
@@ -10,14 +11,14 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(GameRenderer.class)
public abstract class GameRendererMixin {
@Inject(method = "renderCenter", at = @At("HEAD"))
private void renderCenterStart(float delta, long time, CallbackInfo ci) {
RenderQueue.get().setTrackRender(true);
@Inject(method = "renderWorld", at = @At("HEAD"))
private void renderWorldStart(float delta, long time, MatrixStack matrixStack, CallbackInfo ci) {
RenderQueue.get().setTrackRender(matrixStack);
}
@Inject(method = "renderCenter", at = @At("TAIL"))
private void renderCenterFinish(float delta, long time, CallbackInfo ci) {
RenderQueue.get().setTrackRender(false);
@Inject(method = "renderWorld", at = @At("TAIL"))
private void renderWorldFinish(float delta, long time, MatrixStack matrixStack, CallbackInfo ci) {
RenderQueue.get().setTrackRender(null);
}
}
@@ -18,4 +18,9 @@ public class Cube extends Cuboid {
super(pos, new Vec3i(1, 1, 1), color);
}
@Override
public BlockPos getPos() {
return this.start;
}
}
@@ -1,6 +1,7 @@
package kaptainwutax.seedcracker.render;
import net.minecraft.client.util.math.Vector4f;
import net.minecraft.util.math.BlockBox;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3i;
@@ -24,6 +25,10 @@ public class Cuboid extends Renderer {
this(start, new Vec3i(end.getX() - start.getX(), end.getY() - start.getY(), end.getZ() - start.getZ()), color);
}
public Cuboid(BlockBox box, Vector4f color) {
this(new BlockPos(box.minX, box.minY, box.minZ), new BlockPos(box.maxX, box.maxY, box.maxZ), color);
}
public Cuboid(BlockPos start, Vec3i size, Vector4f color) {
this.start = start;
this.size = size;
@@ -52,4 +57,9 @@ public class Cuboid extends Renderer {
}
}
@Override
public BlockPos getPos() {
return this.start.add(this.size.getX() / 2, this.size.getY() / 2, this.size.getZ() / 2);
}
}
@@ -5,6 +5,7 @@ import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.Tessellator;
import net.minecraft.client.render.VertexFormats;
import net.minecraft.client.util.math.Vector4f;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
public class Line extends Renderer {
@@ -33,7 +34,7 @@ public class Line extends Renderer {
Vec3d camPos = this.mc.gameRenderer.getCamera().getPos();
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBufferBuilder();
BufferBuilder buffer = tessellator.getBuffer();
//This is how thick the line is.
GlStateManager.lineWidth(2.0f);
@@ -48,18 +49,24 @@ public class Line extends Renderer {
}
protected void putVertex(BufferBuilder buffer, Vec3d camPos, Vec3d pos) {
for(int i = 0; i < 2; i++) {
buffer.vertex(
pos.getX() - camPos.x,
pos.getY() - camPos.y,
pos.getZ() - camPos.z
).color(
this.color.getX(),
this.color.getY(),
this.color.getZ(),
this.color.getW()
).next();
}
buffer.vertex(
pos.getX() - camPos.x,
pos.getY() - camPos.y,
pos.getZ() - camPos.z
).color(
this.color.getX(),
this.color.getY(),
this.color.getZ(),
this.color.getW()
).next();
}
@Override
public BlockPos getPos() {
double x = (this.end.getX() - this.start.getX()) / 2 + this.start.getX();
double y = (this.end.getY() - this.start.getY()) / 2 + this.start.getY();
double z = (this.end.getZ() - this.start.getZ()) / 2 + this.start.getZ();
return new BlockPos(x, y, z);
}
}
@@ -1,19 +1,22 @@
package kaptainwutax.seedcracker.render;
import net.minecraft.client.util.math.MatrixStack;
import java.util.*;
import java.util.function.Consumer;
public class RenderQueue {
private final static RenderQueue INSTANCE = new RenderQueue();
private Map<String, List<Runnable>> typeRunnableMap = new HashMap<>();
private boolean trackRender = false;
private Map<String, List<Consumer<MatrixStack>>> typeRunnableMap = new HashMap<>();
private MatrixStack matrixStack = null;
public static RenderQueue get() {
return INSTANCE;
}
public void add(String type, Runnable runnable) {
public void add(String type, Consumer<MatrixStack> runnable) {
Objects.requireNonNull(type);
Objects.requireNonNull(runnable);
@@ -21,11 +24,11 @@ public class RenderQueue {
this.typeRunnableMap.put(type, new ArrayList<>());
}
List<Runnable> runnableList = this.typeRunnableMap.get(type);
List<Consumer<MatrixStack>> runnableList = this.typeRunnableMap.get(type);
runnableList.add(runnable);
}
public void remove(String type, Runnable runnable) {
public void remove(String type, Consumer<MatrixStack> runnable) {
Objects.requireNonNull(type);
Objects.requireNonNull(runnable);
@@ -33,17 +36,17 @@ public class RenderQueue {
return;
}
List<Runnable> runnableList = this.typeRunnableMap.get(type);
List<Consumer<MatrixStack>> runnableList = this.typeRunnableMap.get(type);
runnableList.remove(runnable);
}
public void setTrackRender(boolean flag) {
this.trackRender = flag;
public void setTrackRender(MatrixStack matrixStack) {
this.matrixStack = matrixStack;
}
public void onRender(String type) {
if(!this.trackRender || !this.typeRunnableMap.containsKey(type))return;
this.typeRunnableMap.get(type).forEach(Runnable::run);
if(this.matrixStack == null || !this.typeRunnableMap.containsKey(type))return;
this.typeRunnableMap.get(type).forEach(r -> r.accept(this.matrixStack));
}
}
@@ -10,6 +10,8 @@ public abstract class Renderer {
public abstract void render();
public abstract BlockPos getPos();
protected Vec3d toVec3d(BlockPos pos) {
return new Vec3d(pos.getX(), pos.getY(), pos.getZ());
}
@@ -2,6 +2,8 @@ package kaptainwutax.seedcracker.util;
import kaptainwutax.seedcracker.util.math.LCG;
import java.util.Random;
public class Rand implements Cloneable {
public static final LCG JAVA_LCG = new LCG(0x5DEECE66DL, 0xBL, 1L << 48);
@@ -69,6 +71,10 @@ public class Rand implements Cloneable {
return (((long)this.next(27) << 27) + this.next(27)) / (double)(1L << 54);
}
public Random toRandom() {
return new Random(this.seed ^ JAVA_LCG.multiplier);
}
@Override
public boolean equals(Object obj) {
if(obj == this)return true;
@@ -0,0 +1,29 @@
package kaptainwutax.seedcracker.util;
public class Seeds {
public static long setRegionSeed(Rand rand, long worldSeed, int regionX, int regionZ, int salt) {
long seed = (long)regionX * 341873128712L + (long)regionZ * 132897987541L + worldSeed + (long)salt;
rand.setSeed(seed, true);
return seed;
}
public static long setStructureStartSeed(Rand rand, long worldSeed, int chunkX, int chunkZ) {
rand.setSeed(worldSeed, true);
long a = rand.nextLong();
long b = rand.nextLong();
long seed = (long)chunkX * a ^ (long)chunkZ * b ^ worldSeed;
rand.setSeed(seed, true);
return seed;
}
public static long setWeakSeed(Rand rand, long worldSeed, int chunkX, int chunkZ) {
int sX = chunkX >> 4;
int sZ = chunkZ >> 4;
long seed = (long)(sX ^ sZ << 4) ^ worldSeed;
rand.setSeed(seed, true);
rand.nextInt();
return seed;
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"schemaVersion": 1,
"id": "seedcracker",
"version": "0.0.1",
"version": "0.0.2",
"name": "Seed Cracker",
"description": "This is an example description! Tell everyone what your mod is about!",