diff --git a/divinemc-archived-patches/work/server/0046-Implement-Secure-Seed.patch b/divinemc-archived-patches/work/server/0046-Implement-Secure-Seed.patch deleted file mode 100644 index 56da7a7..0000000 --- a/divinemc-archived-patches/work/server/0046-Implement-Secure-Seed.patch +++ /dev/null @@ -1,832 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sat, 20 Jul 2024 22:04:52 +0300 -Subject: [PATCH] Implement Secure Seed - -Original license: GPLv3 -Original project: https://github.com/plasmoapp/matter - -diff --git a/src/main/java/net/minecraft/server/commands/SeedCommand.java b/src/main/java/net/minecraft/server/commands/SeedCommand.java -index 0b500b19a99fa6c2740c0db350a166462668df9c..f13185628dec90a044bf03cf38394b5be8ab2003 100644 ---- a/src/main/java/net/minecraft/server/commands/SeedCommand.java -+++ b/src/main/java/net/minecraft/server/commands/SeedCommand.java -@@ -12,6 +12,17 @@ public class SeedCommand { - long l = context.getSource().getLevel().getSeed(); - Component component = ComponentUtils.copyOnClickText(String.valueOf(l)); - context.getSource().sendSuccess(() -> Component.translatable("commands.seed.success", component), false); -+ -+ // DivineMC start - Implement Secure Seed -+ if (space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed) { -+ space.bxteam.divinemc.seed.Globals.setupGlobals(context.getSource().getLevel()); -+ String seedStr = space.bxteam.divinemc.seed.Globals.seedToString(space.bxteam.divinemc.seed.Globals.worldSeed); -+ Component featureSeedComponent = ComponentUtils.copyOnClickText(seedStr); -+ -+ context.getSource().sendSuccess(() -> Component.translatable(("Feature seed: %s"), featureSeedComponent), false); -+ } -+ // DivineMC end -+ - return (int)l; - })); - } -diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java -index 05e16103af3fd276f0196ddf1a2e5b729b025c34..e0118dfee89d4319f70a0d2f84ba4c21b03a9ed9 100644 ---- a/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java -+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java -@@ -167,7 +167,17 @@ public class DedicatedServerProperties extends Settings { - return GsonHelper.parse(!s1.isEmpty() ? s1 : "{}"); - }, new JsonObject()), (String) this.get("level-type", (s1) -> { -diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index aea9a45c0916501f71018d3250b56da435f5664e..564395b9be9f161dc3ea7956b9b2432dc8bf45fa 100644 ---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java -+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -692,6 +692,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon - } - - public ChunkGenerator getGenerator() { -+ space.bxteam.divinemc.seed.Globals.setupGlobals(level); // DivineMC - Implement Secure Seed - return this.chunkMap.generator(); - } - -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index fe8a1a073920b7cbbe3791ac1fcac3fccec6b9f7..b0095c0848ca0162944961a24c7b807fb5846b06 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -644,6 +644,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - chunkgenerator = new org.bukkit.craftbukkit.generator.CustomChunkGenerator(this, chunkgenerator, gen); - } - // CraftBukkit end -+ space.bxteam.divinemc.seed.Globals.setupGlobals(this); // DivineMC - Implement Secure Seed - boolean flag2 = minecraftserver.forceSynchronousWrites(); - DataFixer datafixer = minecraftserver.getFixerUpper(); - EntityPersistentStorage entitypersistentstorage = new EntityStorage(new SimpleRegionStorage(new RegionStorageInfo(convertable_conversionsession.getLevelId(), resourcekey, "entities"), convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), datafixer, flag2, DataFixTypes.ENTITY_CHUNK), this, minecraftserver); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Slime.java b/src/main/java/net/minecraft/world/entity/monster/Slime.java -index d58f7b251d0c322d63e7e5e8ed30b41427a11227..7fe1ae012dc3fec82bd75c6f44f1d32bf30fd666 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Slime.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Slime.java -@@ -433,8 +433,13 @@ public class Slime extends Mob implements Enemy { - return false; - } - -- ChunkPos chunkcoordintpair = new ChunkPos(pos); -- boolean flag = world.getMinecraftWorld().paperConfig().entities.spawning.allChunksAreSlimeChunks || WorldgenRandom.seedSlimeChunk(chunkcoordintpair.x, chunkcoordintpair.z, ((WorldGenLevel) world).getSeed(), world.getMinecraftWorld().spigotConfig.slimeSeed).nextInt(10) == 0; // Spigot // Paper -+ ChunkPos chunkcoordintpair = new ChunkPos(pos); -+ // DivineMC start - Implement Secure Seed -+ boolean isSlimeChunk = space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed -+ ? world.getChunk(chunkcoordintpair.x, chunkcoordintpair.z).isSlimeChunk() -+ : WorldgenRandom.seedSlimeChunk(chunkcoordintpair.x, chunkcoordintpair.z, ((WorldGenLevel) world).getSeed(), world.getMinecraftWorld().spigotConfig.slimeSeed).nextInt(10) == 0; // Spigot // Paper -+ boolean flag = world.getMinecraftWorld().paperConfig().entities.spawning.allChunksAreSlimeChunks || isSlimeChunk; -+ // DivineMC end - - // Paper start - Replace rules for Height in Slime Chunks - final double maxHeightSlimeChunk = world.getMinecraftWorld().paperConfig().entities.spawning.slimeSpawnHeight.slimeChunk.maximum; -diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java -index f87abb22dd161b2b74401086de80dc95c9ac2dbb..5ca052b94ff9fa36e3241a745229839a1e9bc852 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java -+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java -@@ -85,6 +85,11 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh - protected final LevelHeightAccessor levelHeightAccessor; - protected final LevelChunkSection[] sections; - -+ // DivineMC start - Implement Secure Seed -+ private boolean slimeChunk; -+ private boolean hasComputedSlimeChunk; -+ // DivineMC end -+ - // CraftBukkit start - SPIGOT-6814: move to IChunkAccess to account for 1.17 to 1.18 chunk upgrading. - private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry(); - public org.bukkit.craftbukkit.persistence.DirtyCraftPersistentDataContainer persistentDataContainer = new org.bukkit.craftbukkit.persistence.DirtyCraftPersistentDataContainer(ChunkAccess.DATA_TYPE_REGISTRY); -@@ -189,6 +194,17 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh - return GameEventListenerRegistry.NOOP; - } - -+ // DivineMC start - Implement Secure Seed -+ public boolean isSlimeChunk() { -+ if (!hasComputedSlimeChunk) { -+ hasComputedSlimeChunk = true; -+ slimeChunk = space.bxteam.divinemc.seed.WorldgenCryptoRandom.seedSlimeChunk(chunkPos.x, chunkPos.z).nextInt(10) == 0; -+ } -+ -+ return slimeChunk; -+ } -+ // DivineMC end -+ - public abstract BlockState getBlockState(final int x, final int y, final int z); // Paper - @Nullable - public abstract BlockState setBlockState(BlockPos pos, BlockState state, boolean moved); -diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java -index e0cb360ece042c4fc6aa0d10106923fe25288f5c..b42ab192fec723a17acd70aeb093895c8938ece2 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java -+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java -@@ -345,7 +345,11 @@ public abstract class ChunkGenerator { - return structure.step().ordinal(); - })); - List list = (List) this.featuresPerStep.get(); -- WorldgenRandom seededrandom = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed())); -+ // DivineMC start - Implement Secure Seed -+ WorldgenRandom seededrandom = space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed -+ ? new space.bxteam.divinemc.seed.WorldgenCryptoRandom(blockposition.getX(), blockposition.getZ(), space.bxteam.divinemc.seed.Globals.Salt.UNDEFINED, 0) -+ : new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed())); -+ // DivineMC end - long i = seededrandom.setDecorationSeed(generatoraccessseed.getSeed(), blockposition.getX(), blockposition.getZ()); - Set> set = new ObjectArraySet(); - -@@ -584,9 +588,18 @@ public abstract class ChunkGenerator { - ArrayList arraylist = new ArrayList(list.size()); - - arraylist.addAll(list); -- WorldgenRandom seededrandom = new WorldgenRandom(new LegacyRandomSource(0L)); -- -- seededrandom.setLargeFeatureSeed(placementCalculator.getLevelSeed(), chunkcoordintpair.x, chunkcoordintpair.z); -+ // DivineMC start - Implement Secure Seed -+ WorldgenRandom seededrandom; -+ if (space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed) { -+ seededrandom = new space.bxteam.divinemc.seed.WorldgenCryptoRandom( -+ chunkcoordintpair.x, chunkcoordintpair.z, space.bxteam.divinemc.seed.Globals.Salt.GENERATE_FEATURE, 0 -+ ); -+ } else { -+ seededrandom = new WorldgenRandom(new LegacyRandomSource(0L)); -+ -+ seededrandom.setLargeFeatureSeed(placementCalculator.getLevelSeed(), chunkcoordintpair.x, chunkcoordintpair.z); -+ } -+ // DivineMC end - int i = 0; - - StructureSet.StructureSelectionEntry structureset_a1; -diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java b/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java -index a20520a6bd28bae1cee82258ac49d9753faba2bd..a0fbcf642f5a2aa6354d4287961d93779893d852 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java -+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java -@@ -224,15 +224,20 @@ public class ChunkGeneratorStructureState { - List> list = new ArrayList(j); - int k = placement.spread(); - HolderSet holderset = placement.preferredBiomes(); -- RandomSource randomsource = RandomSource.create(); -- -- // Paper start - Add missing structure set seed configs -- if (this.conf.strongholdSeed != null && structureSetEntry.is(net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.STRONGHOLDS)) { -- randomsource.setSeed(this.conf.strongholdSeed); -- } else { -- // Paper end - Add missing structure set seed configs -- randomsource.setSeed(this.concentricRingsSeed); -- } // Paper - Add missing structure set seed configs -+ // DivineMC start - Implement Secure Seed -+ RandomSource randomsource = space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed -+ ? new space.bxteam.divinemc.seed.WorldgenCryptoRandom(0, 0, space.bxteam.divinemc.seed.Globals.Salt.STRONGHOLDS, 0) -+ : RandomSource.create(); -+ -+ if (!space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed) { -+ // Paper start - Add missing structure set seed configs -+ if (this.conf.strongholdSeed != null && structureSetEntry.is(net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.STRONGHOLDS)) { -+ randomsource.setSeed(this.conf.strongholdSeed); -+ } else { -+ // Paper end - Add missing structure set seed configs -+ randomsource.setSeed(this.concentricRingsSeed); -+ } // Paper - Add missing structure set seed configs -+ } // DivineMC end - double d0 = randomsource.nextDouble() * Math.PI * 2.0D; - int l = 0; - int i1 = 0; -diff --git a/src/main/java/net/minecraft/world/level/chunk/status/ChunkStep.java b/src/main/java/net/minecraft/world/level/chunk/status/ChunkStep.java -index f9aad1b8c02b70e620efdc2a58cadf4fff0f3ed5..261661977c3670d5173c677f041b2305a212718c 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/status/ChunkStep.java -+++ b/src/main/java/net/minecraft/world/level/chunk/status/ChunkStep.java -@@ -60,6 +60,7 @@ public final class ChunkStep implements ca.spottedleaf.moonrise.patches.chunk_sy - } - - public CompletableFuture apply(WorldGenContext context, StaticCache2D staticCache2D, ChunkAccess chunk) { -+ space.bxteam.divinemc.seed.Globals.setupGlobals(context.level()); // DivineMC - Implement Secure Seed - if (chunk.getPersistedStatus().isBefore(this.targetStatus)) { - ProfiledDuration profiledDuration = JvmProfiler.INSTANCE.onChunkGenerate(chunk.getPos(), context.level().dimension(), this.targetStatus.getName()); - return this.task.doWork(context, this, staticCache2D, chunk).thenApply(generated -> this.completeChunkGeneration(generated, profiledDuration)); -diff --git a/src/main/java/net/minecraft/world/level/levelgen/WorldOptions.java b/src/main/java/net/minecraft/world/level/levelgen/WorldOptions.java -index 41c19e4e7bde4632879da564f52f3d373de27ec4..6070110bdfada835dd6b04f66644a47aa868fe4e 100644 ---- a/src/main/java/net/minecraft/world/level/levelgen/WorldOptions.java -+++ b/src/main/java/net/minecraft/world/level/levelgen/WorldOptions.java -@@ -9,8 +9,19 @@ import net.minecraft.util.RandomSource; - import org.apache.commons.lang3.StringUtils; - - public class WorldOptions { -+ // DivineMC start - Implement Secure Seed -+ private static final boolean isSecureSeedEnabled = space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed; - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( -- instance -> instance.group( -+ instance -> isSecureSeedEnabled -+ ? instance.group( -+ Codec.LONG.fieldOf("seed").stable().forGetter(WorldOptions::seed), -+ Codec.LONG_STREAM.fieldOf("feature_seed").stable().forGetter(WorldOptions::featureSeedStream), -+ Codec.BOOL.fieldOf("generate_features").orElse(true).stable().forGetter(WorldOptions::generateStructures), -+ Codec.BOOL.fieldOf("bonus_chest").orElse(false).stable().forGetter(WorldOptions::generateBonusChest), -+ Codec.STRING.lenientOptionalFieldOf("legacy_custom_options").stable().forGetter(generatorOptions -> generatorOptions.legacyCustomOptions) -+ ) -+ .apply(instance, instance.stable(WorldOptions::new)) -+ : instance.group( - Codec.LONG.fieldOf("seed").stable().forGetter(WorldOptions::seed), - Codec.BOOL.fieldOf("generate_features").orElse(true).stable().forGetter(WorldOptions::generateStructures), - Codec.BOOL.fieldOf("bonus_chest").orElse(false).stable().forGetter(WorldOptions::generateBonusChest), -@@ -18,8 +29,14 @@ public class WorldOptions { - ) - .apply(instance, instance.stable(WorldOptions::new)) - ); -- public static final WorldOptions DEMO_OPTIONS = new WorldOptions((long)"North Carolina".hashCode(), true, true); -+ // DivineMC end -+ // DivineMC start - Implement Secure Seed -+ public static final WorldOptions DEMO_OPTIONS = isSecureSeedEnabled -+ ? new WorldOptions((long) "North Carolina".hashCode(), space.bxteam.divinemc.seed.Globals.createRandomWorldSeed(), true, true) -+ : new WorldOptions("North Carolina".hashCode(), true, true); -+ // DivineMC end - private final long seed; -+ private long[] featureSeed = space.bxteam.divinemc.seed.Globals.createRandomWorldSeed(); // DivineMC - Implement Secure Seed - private final boolean generateStructures; - private final boolean generateBonusChest; - private final Optional legacyCustomOptions; -@@ -28,8 +45,18 @@ public class WorldOptions { - this(seed, generateStructures, bonusChest, Optional.empty()); - } - -+ public WorldOptions(long seed, long[] featureSeed, boolean generateStructures, boolean bonusChest) { -+ this(seed, featureSeed, generateStructures, bonusChest, Optional.empty()); -+ } -+ - public static WorldOptions defaultWithRandomSeed() { -- return new WorldOptions(randomSeed(), true, false); -+ return isSecureSeedEnabled -+ ? new WorldOptions(randomSeed(), space.bxteam.divinemc.seed.Globals.createRandomWorldSeed(), true, false) -+ : new WorldOptions(randomSeed(), true, false); -+ } -+ -+ private WorldOptions(long seed, java.util.stream.LongStream featureSeed, boolean generateStructures, boolean bonusChest, Optional legacyCustomOptions) { -+ this(seed, featureSeed.toArray(), generateStructures, bonusChest, legacyCustomOptions); - } - - public static WorldOptions testWorldWithRandomSeed() { -@@ -43,10 +70,27 @@ public class WorldOptions { - this.legacyCustomOptions = legacyCustomOptions; - } - -+ // DivineMC start - Implement Secure Seed -+ private WorldOptions(long seed, long[] featureSeed, boolean generateStructures, boolean bonusChest, Optional legacyCustomOptions) { -+ this(seed, generateStructures, bonusChest, legacyCustomOptions); -+ this.featureSeed = featureSeed; -+ } -+ // DivineMC end -+ - public long seed() { - return this.seed; - } - -+ // DivineMC start - Implement Secure Seed -+ public long[] featureSeed() { -+ return this.featureSeed; -+ } -+ -+ public java.util.stream.LongStream featureSeedStream() { -+ return java.util.stream.LongStream.of(this.featureSeed); -+ } -+ // DivineMC end -+ - public boolean generateStructures() { - return this.generateStructures; - } -@@ -59,17 +103,25 @@ public class WorldOptions { - return this.legacyCustomOptions.isPresent(); - } - -+ // DivineMC start - Implement Secure Seed - public WorldOptions withBonusChest(boolean bonusChest) { -- return new WorldOptions(this.seed, this.generateStructures, bonusChest, this.legacyCustomOptions); -+ return isSecureSeedEnabled -+ ? new WorldOptions(this.seed, this.featureSeed, this.generateStructures, bonusChest, this.legacyCustomOptions) -+ : new WorldOptions(this.seed, this.generateStructures, bonusChest, this.legacyCustomOptions); - } - - public WorldOptions withStructures(boolean structures) { -- return new WorldOptions(this.seed, structures, this.generateBonusChest, this.legacyCustomOptions); -+ return isSecureSeedEnabled -+ ? new WorldOptions(this.seed, this.featureSeed, structures, this.generateBonusChest, this.legacyCustomOptions) -+ : new WorldOptions(this.seed, structures, this.generateBonusChest, this.legacyCustomOptions); - } - - public WorldOptions withSeed(OptionalLong seed) { -- return new WorldOptions(seed.orElse(randomSeed()), this.generateStructures, this.generateBonusChest, this.legacyCustomOptions); -+ return isSecureSeedEnabled -+ ? new WorldOptions(seed.orElse(randomSeed()), space.bxteam.divinemc.seed.Globals.createRandomWorldSeed(), this.generateStructures, this.generateBonusChest, this.legacyCustomOptions) -+ : new WorldOptions(seed.orElse(randomSeed()), this.generateStructures, this.generateBonusChest, this.legacyCustomOptions); - } -+ // DivineMC end - - public static OptionalLong parseSeed(String seed) { - seed = seed.trim(); -diff --git a/src/main/java/net/minecraft/world/level/levelgen/feature/GeodeFeature.java b/src/main/java/net/minecraft/world/level/levelgen/feature/GeodeFeature.java -index 270db8b29cdf65e9bb932637425214eefeca86b7..6cf307b4ddb87ad54ead02dd10290a7a825095e3 100644 ---- a/src/main/java/net/minecraft/world/level/levelgen/feature/GeodeFeature.java -+++ b/src/main/java/net/minecraft/world/level/levelgen/feature/GeodeFeature.java -@@ -41,7 +41,11 @@ public class GeodeFeature extends Feature { - int j = geodeConfiguration.maxGenOffset; - List> list = Lists.newLinkedList(); - int k = geodeConfiguration.distributionPoints.sample(randomSource); -- WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(worldGenLevel.getSeed())); -+ // DivineMC start - Implement Secure Seed -+ WorldgenRandom worldgenRandom = space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed -+ ? new space.bxteam.divinemc.seed.WorldgenCryptoRandom(0, 0, space.bxteam.divinemc.seed.Globals.Salt.GEODE_FEATURE, 0) -+ : new WorldgenRandom(new LegacyRandomSource(worldGenLevel.getSeed())); -+ // DivineMC end - NormalNoise normalNoise = NormalNoise.create(worldgenRandom, -4, 1.0); - List list2 = Lists.newLinkedList(); - double d = (double)k / (double)geodeConfiguration.outerWallDistance.getMaxValue(); -diff --git a/src/main/java/net/minecraft/world/level/levelgen/structure/Structure.java b/src/main/java/net/minecraft/world/level/levelgen/structure/Structure.java -index 13f7fce1959c0f44e047616674198176e667067f..23528e2eef5d097cad44e8f51a6199a7bf718044 100644 ---- a/src/main/java/net/minecraft/world/level/levelgen/structure/Structure.java -+++ b/src/main/java/net/minecraft/world/level/levelgen/structure/Structure.java -@@ -248,6 +248,14 @@ public abstract class Structure { - } - - private static WorldgenRandom makeRandom(long seed, ChunkPos chunkPos) { -+ // DivineMC start - Implement Secure Seed -+ if (space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed) { -+ return new space.bxteam.divinemc.seed.WorldgenCryptoRandom( -+ chunkPos.x, chunkPos.z, space.bxteam.divinemc.seed.Globals.Salt.GENERATE_FEATURE, seed -+ ); -+ } -+ // DivineMC end -+ - WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); - worldgenRandom.setLargeFeatureSeed(seed, chunkPos.x, chunkPos.z); - return worldgenRandom; -diff --git a/src/main/java/net/minecraft/world/level/levelgen/structure/placement/RandomSpreadStructurePlacement.java b/src/main/java/net/minecraft/world/level/levelgen/structure/placement/RandomSpreadStructurePlacement.java -index f873a0a0734b4fe74ba5b5f8ae0cc3c78fa76b9f..1e29d5554ac2be3219f075092778e49022788723 100644 ---- a/src/main/java/net/minecraft/world/level/levelgen/structure/placement/RandomSpreadStructurePlacement.java -+++ b/src/main/java/net/minecraft/world/level/levelgen/structure/placement/RandomSpreadStructurePlacement.java -@@ -71,8 +71,17 @@ public class RandomSpreadStructurePlacement extends StructurePlacement { - public ChunkPos getPotentialStructureChunk(long seed, int chunkX, int chunkZ) { - int i = Math.floorDiv(chunkX, this.spacing); - int j = Math.floorDiv(chunkZ, this.spacing); -- WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); -- worldgenRandom.setLargeFeatureWithSalt(seed, i, j, this.salt()); -+ // DivineMC start - Implement Secure Seed -+ WorldgenRandom worldgenRandom; -+ if (space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed) { -+ worldgenRandom = new space.bxteam.divinemc.seed.WorldgenCryptoRandom( -+ i, j, space.bxteam.divinemc.seed.Globals.Salt.POTENTIONAL_FEATURE, this.salt -+ ); -+ } else { -+ worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); -+ worldgenRandom.setLargeFeatureWithSalt(seed, i, j, this.salt()); -+ } -+ // DivineMC end - int k = this.spacing - this.separation; - int l = this.spreadType.evaluate(worldgenRandom, k); - int m = this.spreadType.evaluate(worldgenRandom, k); -diff --git a/src/main/java/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java b/src/main/java/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java -index cbf13e4f2da6a27619e9bc9a7cd73bb6e69cad2a..8aebe917973f65123a3b0744172a307f95b05cb0 100644 ---- a/src/main/java/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java -+++ b/src/main/java/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java -@@ -118,8 +118,18 @@ public abstract class StructurePlacement { - public abstract StructurePlacementType type(); - - private static boolean probabilityReducer(long seed, int salt, int chunkX, int chunkZ, float frequency, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper - Add missing structure set seed configs; ignore here -- WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); -- worldgenRandom.setLargeFeatureWithSalt(seed, salt, chunkX, chunkZ); -+ // DivineMC start - Implement Secure Seed -+ WorldgenRandom worldgenRandom; -+ if (space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed) { -+ worldgenRandom = new space.bxteam.divinemc.seed.WorldgenCryptoRandom( -+ chunkX, chunkZ, space.bxteam.divinemc.seed.Globals.Salt.UNDEFINED, salt -+ ); -+ } else { -+ worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); -+ worldgenRandom.setLargeFeatureWithSalt(seed, salt, chunkX, chunkZ); -+ } -+ // DivineMC end -+ - return worldgenRandom.nextFloat() < frequency; - } - -diff --git a/src/main/java/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java b/src/main/java/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java -index 65a0d9e7dd742732974774daabce02e9e52039ac..53e1261ef3ec66d90bcc17e99cb21e7d8ef9ccea 100644 ---- a/src/main/java/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java -+++ b/src/main/java/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java -@@ -64,7 +64,11 @@ public class JigsawPlacement { - ChunkGenerator chunkGenerator = context.chunkGenerator(); - StructureTemplateManager structureTemplateManager = context.structureTemplateManager(); - LevelHeightAccessor levelHeightAccessor = context.heightAccessor(); -- WorldgenRandom worldgenRandom = context.random(); -+ // DivineMC start - Implement Secure Seed -+ WorldgenRandom worldgenRandom = space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed -+ ? new space.bxteam.divinemc.seed.WorldgenCryptoRandom(context.chunkPos().x, context.chunkPos().z, space.bxteam.divinemc.seed.Globals.Salt.JIGSAW_PLACEMENT, 0) -+ : context.random(); -+ // DivineMC end - Registry registry = registryAccess.lookupOrThrow(Registries.TEMPLATE_POOL); - Rotation rotation = Rotation.getRandom(worldgenRandom); - StructureTemplatePool structureTemplatePool = structurePool.unwrapKey() -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java -index c2bffe3450ee9f768e00a23ec09df74d7a06d49b..922d1037352198432c16b782c892467c76e17428 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java -@@ -206,7 +206,12 @@ public class CraftChunk implements Chunk { - @Override - public boolean isSlimeChunk() { - // 987234911L is deterimined in EntitySlime when seeing if a slime can spawn in a chunk -- return this.worldServer.paperConfig().entities.spawning.allChunksAreSlimeChunks || WorldgenRandom.seedSlimeChunk(this.getX(), this.getZ(), this.getWorld().getSeed(), worldServer.spigotConfig.slimeSeed).nextInt(10) == 0; // Paper -+ // DivineMC start - Implement Secure Seed -+ boolean isSlimeChunk = space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed -+ ? worldServer.getChunk(this.getX(), this.getZ()).isSlimeChunk() -+ : WorldgenRandom.seedSlimeChunk(this.getX(), this.getZ(), this.getWorld().getSeed(), worldServer.spigotConfig.slimeSeed).nextInt(10) == 0; // Paper -+ return this.worldServer.paperConfig().entities.spawning.allChunksAreSlimeChunks || isSlimeChunk; -+ // DivineMC end - } - - @Override -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index 88343cbdaa95f7f2bf6a196b58f23195186749a2..62807b021b936477db6e266633767d81a9436bb2 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -1409,7 +1409,11 @@ public final class CraftServer implements Server { - iregistrycustom_dimension = leveldataanddimensions.dimensions().dimensionsRegistryAccess(); - } else { - LevelSettings worldsettings; -- WorldOptions worldoptions = new WorldOptions(creator.seed(), creator.generateStructures(), false); -+ // DivineMC start - Implement Secure Seed -+ WorldOptions worldoptions = space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed -+ ? new WorldOptions(creator.seed(), space.bxteam.divinemc.seed.Globals.createRandomWorldSeed(), creator.generateStructures(), false) -+ : new WorldOptions(creator.seed(), creator.generateStructures(), false); -+ // DivineMC end - WorldDimensions worlddimensions; - - DedicatedServerProperties.WorldDimensionData properties = new DedicatedServerProperties.WorldDimensionData(GsonHelper.parse((creator.generatorSettings().isEmpty()) ? "{}" : creator.generatorSettings()), creator.type().name().toLowerCase(Locale.ROOT)); -diff --git a/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java b/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -index 9deb9310b6829f2e1bac36327bbb21cee099b22b..c494d88cda016ff7a4da37ac1cee2a05876e595f 100644 ---- a/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -+++ b/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -@@ -158,10 +158,12 @@ public class DivineConfig { - public static boolean disableNonEditableSignWarning = true; - public static boolean removeVanillaUsernameCheck = false; - public static boolean disableMovedWronglyThreshold = false; -+ public static boolean enableSecureSeed = false; - private static void miscSettings() { - disableNonEditableSignWarning = getBoolean("settings.misc.disable-non-editable-sign-warning", disableNonEditableSignWarning); - removeVanillaUsernameCheck = getBoolean("settings.misc.remove-vanilla-username-check", removeVanillaUsernameCheck); - disableMovedWronglyThreshold = getBoolean("settings.misc.disable-moved-wrongly-threshold", disableMovedWronglyThreshold); -+ enableSecureSeed = getBoolean("settings.misc.enable-secure-seed", enableSecureSeed); - } - - public static boolean biomeManagerOptimization = true; -diff --git a/src/main/java/space/bxteam/divinemc/seed/Globals.java b/src/main/java/space/bxteam/divinemc/seed/Globals.java -new file mode 100644 -index 0000000000000000000000000000000000000000..39d2d74f6a89ecbe42ef45d9fc500762a2dfce51 ---- /dev/null -+++ b/src/main/java/space/bxteam/divinemc/seed/Globals.java -@@ -0,0 +1,94 @@ -+package space.bxteam.divinemc.seed; -+ -+import com.google.common.collect.Iterables; -+import net.minecraft.server.level.ServerLevel; -+ -+import java.math.BigInteger; -+import java.security.SecureRandom; -+import java.util.Optional; -+ -+public class Globals { -+ public static final int WORLD_SEED_LONGS = 16; -+ public static final int WORLD_SEED_BITS = WORLD_SEED_LONGS * 64; -+ -+ public static final long[] worldSeed = new long[WORLD_SEED_LONGS]; -+ public static final ThreadLocal dimension = ThreadLocal.withInitial(() -> 0); -+ -+ public enum Salt { -+ UNDEFINED, -+ BASTION_FEATURE, -+ WOODLAND_MANSION_FEATURE, -+ MINESHAFT_FEATURE, -+ BURIED_TREASURE_FEATURE, -+ NETHER_FORTRESS_FEATURE, -+ PILLAGER_OUTPOST_FEATURE, -+ GEODE_FEATURE, -+ NETHER_FOSSIL_FEATURE, -+ OCEAN_MONUMENT_FEATURE, -+ RUINED_PORTAL_FEATURE, -+ POTENTIONAL_FEATURE, -+ GENERATE_FEATURE, -+ JIGSAW_PLACEMENT, -+ STRONGHOLDS, -+ POPULATION, -+ DECORATION, -+ SLIME_CHUNK -+ } -+ -+ public static void setupGlobals(ServerLevel world) { -+ if (!space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed) return; -+ -+ long[] seed = world.getServer().getWorldData().worldGenOptions().featureSeed(); -+ System.arraycopy(seed, 0, worldSeed, 0, WORLD_SEED_LONGS); -+ int worldIndex = Iterables.indexOf(world.getServer().levelKeys(), it -> it == world.dimension()); -+ if (worldIndex == -1) -+ worldIndex = world.getServer().levelKeys().size(); // if we are in world construction it may not have been added to the map yet -+ dimension.set(worldIndex); -+ } -+ -+ public static long[] createRandomWorldSeed() { -+ long[] seed = new long[WORLD_SEED_LONGS]; -+ SecureRandom rand = new SecureRandom(); -+ for (int i = 0; i < WORLD_SEED_LONGS; i++) { -+ seed[i] = rand.nextLong(); -+ } -+ return seed; -+ } -+ -+ // 1024-bit string -> 16 * 64 long[] -+ public static Optional parseSeed(String seedStr) { -+ if (seedStr.isEmpty()) return Optional.empty(); -+ -+ if (seedStr.length() != WORLD_SEED_BITS) { -+ throw new IllegalArgumentException("Secure seed length must be " + WORLD_SEED_BITS + "-bit but found " + seedStr.length() + "-bit."); -+ } -+ -+ long[] seed = new long[WORLD_SEED_LONGS]; -+ -+ for (int i = 0; i < WORLD_SEED_LONGS; i++) { -+ int start = i * 64; -+ int end = start + 64; -+ String seedSection = seedStr.substring(start, end); -+ -+ BigInteger seedInDecimal = new BigInteger(seedSection, 2); -+ seed[i] = seedInDecimal.longValue(); -+ } -+ -+ return Optional.of(seed); -+ } -+ -+ // 16 * 64 long[] -> 1024-bit string -+ public static String seedToString(long[] seed) { -+ StringBuilder sb = new StringBuilder(); -+ -+ for (long longV : seed) { -+ // Convert to 64-bit binary string per long -+ // Use format to keep 64-bit length, and use 0 to complete space -+ String binaryStr = String.format("%64s", Long.toBinaryString(longV)).replace(' ', '0'); -+ -+ sb.append(binaryStr); -+ } -+ -+ return sb.toString(); -+ } -+} -diff --git a/src/main/java/space/bxteam/divinemc/seed/Hashing.java b/src/main/java/space/bxteam/divinemc/seed/Hashing.java -new file mode 100644 -index 0000000000000000000000000000000000000000..5974e52ec4ad6422ef0eac19d0b32be233889a40 ---- /dev/null -+++ b/src/main/java/space/bxteam/divinemc/seed/Hashing.java -@@ -0,0 +1,73 @@ -+package space.bxteam.divinemc.seed; -+ -+public class Hashing { -+ // https://en.wikipedia.org/wiki/BLAKE_(hash_function) -+ // https://github.com/bcgit/bc-java/blob/master/core/src/main/java/org/bouncycastle/crypto/digests/Blake2bDigest.java -+ -+ private final static long[] blake2b_IV = { -+ 0x6a09e667f3bcc908L, 0xbb67ae8584caa73bL, 0x3c6ef372fe94f82bL, -+ 0xa54ff53a5f1d36f1L, 0x510e527fade682d1L, 0x9b05688c2b3e6c1fL, -+ 0x1f83d9abfb41bd6bL, 0x5be0cd19137e2179L -+ }; -+ -+ private final static byte[][] blake2b_sigma = { -+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, -+ {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, -+ {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, -+ {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}, -+ {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13}, -+ {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9}, -+ {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11}, -+ {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10}, -+ {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5}, -+ {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0}, -+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, -+ {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3} -+ }; -+ -+ public static long[] hashWorldSeed(long[] worldSeed) { -+ long[] result = blake2b_IV.clone(); -+ result[0] ^= 0x01010040; -+ hash(worldSeed, result, new long[16], 0, false); -+ return result; -+ } -+ -+ public static void hash(long[] message, long[] chainValue, long[] internalState, long messageOffset, boolean isFinal) { -+ assert message.length == 16; -+ assert chainValue.length == 8; -+ assert internalState.length == 16; -+ -+ System.arraycopy(chainValue, 0, internalState, 0, chainValue.length); -+ System.arraycopy(blake2b_IV, 0, internalState, chainValue.length, 4); -+ internalState[12] = messageOffset ^ blake2b_IV[4]; -+ internalState[13] = blake2b_IV[5]; -+ if (isFinal) internalState[14] = ~blake2b_IV[6]; -+ internalState[15] = blake2b_IV[7]; -+ -+ for (int round = 0; round < 12; round++) { -+ G(message[blake2b_sigma[round][0]], message[blake2b_sigma[round][1]], 0, 4, 8, 12, internalState); -+ G(message[blake2b_sigma[round][2]], message[blake2b_sigma[round][3]], 1, 5, 9, 13, internalState); -+ G(message[blake2b_sigma[round][4]], message[blake2b_sigma[round][5]], 2, 6, 10, 14, internalState); -+ G(message[blake2b_sigma[round][6]], message[blake2b_sigma[round][7]], 3, 7, 11, 15, internalState); -+ G(message[blake2b_sigma[round][8]], message[blake2b_sigma[round][9]], 0, 5, 10, 15, internalState); -+ G(message[blake2b_sigma[round][10]], message[blake2b_sigma[round][11]], 1, 6, 11, 12, internalState); -+ G(message[blake2b_sigma[round][12]], message[blake2b_sigma[round][13]], 2, 7, 8, 13, internalState); -+ G(message[blake2b_sigma[round][14]], message[blake2b_sigma[round][15]], 3, 4, 9, 14, internalState); -+ } -+ -+ for (int i = 0; i < 8; i++) { -+ chainValue[i] ^= internalState[i] ^ internalState[i + 8]; -+ } -+ } -+ -+ private static void G(long m1, long m2, int posA, int posB, int posC, int posD, long[] internalState) { -+ internalState[posA] = internalState[posA] + internalState[posB] + m1; -+ internalState[posD] = Long.rotateRight(internalState[posD] ^ internalState[posA], 32); -+ internalState[posC] = internalState[posC] + internalState[posD]; -+ internalState[posB] = Long.rotateRight(internalState[posB] ^ internalState[posC], 24); // replaces 25 of BLAKE -+ internalState[posA] = internalState[posA] + internalState[posB] + m2; -+ internalState[posD] = Long.rotateRight(internalState[posD] ^ internalState[posA], 16); -+ internalState[posC] = internalState[posC] + internalState[posD]; -+ internalState[posB] = Long.rotateRight(internalState[posB] ^ internalState[posC], 63); // replaces 11 of BLAKE -+ } -+} -diff --git a/src/main/java/space/bxteam/divinemc/seed/WorldgenCryptoRandom.java b/src/main/java/space/bxteam/divinemc/seed/WorldgenCryptoRandom.java -new file mode 100644 -index 0000000000000000000000000000000000000000..f6e69a105c8267c1cf85e77e406ec9fc634a8dd6 ---- /dev/null -+++ b/src/main/java/space/bxteam/divinemc/seed/WorldgenCryptoRandom.java -@@ -0,0 +1,159 @@ -+package space.bxteam.divinemc.seed; -+ -+import net.minecraft.util.Mth; -+import net.minecraft.util.RandomSource; -+import net.minecraft.world.level.levelgen.LegacyRandomSource; -+import net.minecraft.world.level.levelgen.WorldgenRandom; -+import org.jetbrains.annotations.NotNull; -+ -+import java.util.Arrays; -+ -+public class WorldgenCryptoRandom extends WorldgenRandom { -+ // hash the world seed to guard against badly chosen world seeds -+ private static final long[] HASHED_ZERO_SEED = Hashing.hashWorldSeed(new long[Globals.WORLD_SEED_LONGS]); -+ private static final ThreadLocal LAST_SEEN_WORLD_SEED = ThreadLocal.withInitial(() -> new long[Globals.WORLD_SEED_LONGS]); -+ private static final ThreadLocal HASHED_WORLD_SEED = ThreadLocal.withInitial(() -> HASHED_ZERO_SEED); -+ -+ private final long[] worldSeed = new long[Globals.WORLD_SEED_LONGS]; -+ private final long[] randomBits = new long[8]; -+ private int randomBitIndex; -+ private static final int MAX_RANDOM_BIT_INDEX = 64 * 8; -+ private static final int LOG2_MAX_RANDOM_BIT_INDEX = 9; -+ private long counter; -+ private final long[] message = new long[16]; -+ private final long[] cachedInternalState = new long[16]; -+ -+ public WorldgenCryptoRandom(int x, int z, Globals.Salt typeSalt, long salt) { -+ super(new LegacyRandomSource(0L)); -+ if (typeSalt != null) { -+ this.setSecureSeed(x, z, typeSalt, salt); -+ } -+ } -+ -+ public void setSecureSeed(int x, int z, Globals.Salt typeSalt, long salt) { -+ System.arraycopy(Globals.worldSeed, 0, this.worldSeed, 0, Globals.WORLD_SEED_LONGS); -+ message[0] = ((long) x << 32) | ((long) z & 0xffffffffL); -+ message[1] = ((long) Globals.dimension.get() << 32) | ((long) salt & 0xffffffffL); -+ message[2] = typeSalt.ordinal(); -+ message[3] = counter = 0; -+ randomBitIndex = MAX_RANDOM_BIT_INDEX; -+ } -+ -+ private long[] getHashedWorldSeed() { -+ if (!Arrays.equals(worldSeed, LAST_SEEN_WORLD_SEED.get())) { -+ HASHED_WORLD_SEED.set(Hashing.hashWorldSeed(worldSeed)); -+ System.arraycopy(worldSeed, 0, LAST_SEEN_WORLD_SEED.get(), 0, Globals.WORLD_SEED_LONGS); -+ } -+ return HASHED_WORLD_SEED.get(); -+ } -+ -+ private void moreRandomBits() { -+ message[3] = counter++; -+ System.arraycopy(getHashedWorldSeed(), 0, randomBits, 0, 8); -+ Hashing.hash(message, randomBits, cachedInternalState, 64, true); -+ } -+ -+ private long getBits(int count) { -+ if (randomBitIndex >= MAX_RANDOM_BIT_INDEX) { -+ moreRandomBits(); -+ randomBitIndex -= MAX_RANDOM_BIT_INDEX; -+ } -+ -+ int alignment = randomBitIndex & 63; -+ if ((randomBitIndex >>> 6) == ((randomBitIndex + count) >>> 6)) { -+ long result = (randomBits[randomBitIndex >>> 6] >>> alignment) & ((1L << count) - 1); -+ randomBitIndex += count; -+ return result; -+ } else { -+ long result = (randomBits[randomBitIndex >>> 6] >>> alignment) & ((1L << (64 - alignment)) - 1); -+ randomBitIndex += count; -+ if (randomBitIndex >= MAX_RANDOM_BIT_INDEX) { -+ moreRandomBits(); -+ randomBitIndex -= MAX_RANDOM_BIT_INDEX; -+ } -+ alignment = randomBitIndex & 63; -+ result <<= alignment; -+ result |= (randomBits[randomBitIndex >>> 6] >>> (64 - alignment)) & ((1L << alignment) - 1); -+ -+ return result; -+ } -+ } -+ -+ @Override -+ public @NotNull RandomSource fork() { -+ WorldgenCryptoRandom fork = new WorldgenCryptoRandom(0, 0, null, 0); -+ -+ System.arraycopy(Globals.worldSeed, 0, fork.worldSeed, 0, Globals.WORLD_SEED_LONGS); -+ fork.message[0] = this.message[0]; -+ fork.message[1] = this.message[1]; -+ fork.message[2] = this.message[2]; -+ fork.message[3] = this.message[3]; -+ fork.randomBitIndex = this.randomBitIndex; -+ fork.counter = this.counter; -+ fork.nextLong(); -+ -+ return fork; -+ } -+ -+ @Override -+ public int next(int bits) { -+ return (int) getBits(bits); -+ } -+ -+ @Override -+ public void consumeCount(int count) { -+ randomBitIndex += count; -+ if (randomBitIndex >= MAX_RANDOM_BIT_INDEX * 2) { -+ randomBitIndex -= MAX_RANDOM_BIT_INDEX; -+ counter += randomBitIndex >>> LOG2_MAX_RANDOM_BIT_INDEX; -+ randomBitIndex &= MAX_RANDOM_BIT_INDEX - 1; -+ randomBitIndex += MAX_RANDOM_BIT_INDEX; -+ } -+ } -+ -+ @Override -+ public int nextInt(int bound) { -+ int bits = Mth.ceillog2(bound); -+ int result; -+ do { -+ result = (int) getBits(bits); -+ } while (result >= bound); -+ -+ return result; -+ } -+ -+ @Override -+ public long nextLong() { -+ return getBits(64); -+ } -+ -+ @Override -+ public double nextDouble() { -+ return getBits(53) * 0x1.0p-53; -+ } -+ -+ @Override -+ public long setDecorationSeed(long worldSeed, int blockX, int blockZ) { -+ setSecureSeed(blockX, blockZ, Globals.Salt.POPULATION, 0); -+ return ((long) blockX << 32) | ((long) blockZ & 0xffffffffL); -+ } -+ -+ @Override -+ public void setFeatureSeed(long populationSeed, int index, int step) { -+ setSecureSeed((int) (populationSeed >> 32), (int) populationSeed, Globals.Salt.DECORATION, index + 10000L * step); -+ } -+ -+ @Override -+ public void setLargeFeatureSeed(long worldSeed, int chunkX, int chunkZ) { -+ super.setLargeFeatureSeed(worldSeed, chunkX, chunkZ); -+ } -+ -+ @Override -+ public void setLargeFeatureWithSalt(long worldSeed, int regionX, int regionZ, int salt) { -+ super.setLargeFeatureWithSalt(worldSeed, regionX, regionZ, salt); -+ } -+ -+ public static RandomSource seedSlimeChunk(int chunkX, int chunkZ) { -+ return new WorldgenCryptoRandom(chunkX, chunkZ, Globals.Salt.SLIME_CHUNK, 0); -+ } -+} diff --git a/divinemc-server/minecraft-patches/features/0004-Make-entity-goals-public.patch b/divinemc-server/minecraft-patches/features/0004-Make-entity-goals-public.patch deleted file mode 100644 index 7ace47f..0000000 --- a/divinemc-server/minecraft-patches/features/0004-Make-entity-goals-public.patch +++ /dev/null @@ -1,139 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sun, 12 Jan 2025 02:05:38 +0300 -Subject: [PATCH] Make entity goals public - - -diff --git a/net/minecraft/world/entity/animal/Bee.java b/net/minecraft/world/entity/animal/Bee.java -index 57c50ce5724b073b1aedf4df3129285143097303..1c3bbf91d9527ded33ec4b5a508d4c6c5b835167 100644 ---- a/net/minecraft/world/entity/animal/Bee.java -+++ b/net/minecraft/world/entity/animal/Bee.java -@@ -786,7 +786,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - && (!state.is(Blocks.SUNFLOWER) || state.getValue(DoublePlantBlock.HALF) == DoubleBlockHalf.UPPER); - } - -- abstract class BaseBeeGoal extends Goal { -+ public abstract class BaseBeeGoal extends Goal { // DivineMC - make public - public abstract boolean canBeeUse(); - - public abstract boolean canBeeContinueToUse(); -@@ -818,7 +818,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - } - } - -- static class BeeBecomeAngryTargetGoal extends NearestAttackableTargetGoal { -+ public static class BeeBecomeAngryTargetGoal extends NearestAttackableTargetGoal { // DivineMC - make public - BeeBecomeAngryTargetGoal(Bee mob) { - super(mob, Player.class, 10, true, false, mob::isAngryAt); - } -@@ -845,7 +845,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - } - } - -- class BeeEnterHiveGoal extends Bee.BaseBeeGoal { -+ public class BeeEnterHiveGoal extends Bee.BaseBeeGoal { // DivineMC - make public - @Override - public boolean canBeeUse() { - if (Bee.this.hivePos != null && Bee.this.wantsToEnterHive() && Bee.this.hivePos.closerToCenterThan(Bee.this.position(), 2.0)) { -@@ -1048,7 +1048,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - } - } - -- class BeeGrowCropGoal extends Bee.BaseBeeGoal { -+ public class BeeGrowCropGoal extends Bee.BaseBeeGoal { // DivineMC - make public - static final int GROW_CHANCE = 30; - - @Override -@@ -1104,7 +1104,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - } - } - -- class BeeHurtByOtherGoal extends HurtByTargetGoal { -+ public class BeeHurtByOtherGoal extends HurtByTargetGoal { // DivineMC - make public - BeeHurtByOtherGoal(final Bee mob) { - super(mob); - } -@@ -1122,7 +1122,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - } - } - -- class BeeLocateHiveGoal extends Bee.BaseBeeGoal { -+ public class BeeLocateHiveGoal extends Bee.BaseBeeGoal { // DivineMC - make public - @Override - public boolean canBeeUse() { - return Bee.this.remainingCooldownBeforeLocatingNewHive == 0 && !Bee.this.hasHive() && Bee.this.wantsToEnterHive(); -@@ -1161,7 +1161,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - } - } - -- class BeeLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur - Ridables -+ public class BeeLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur - Ridables // DivineMC - make public - BeeLookControl(final Mob mob) { - super(mob); - } -@@ -1179,7 +1179,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - } - } - -- class BeePollinateGoal extends Bee.BaseBeeGoal { -+ public class BeePollinateGoal extends Bee.BaseBeeGoal { // DivineMC - make public - private static final int MIN_POLLINATION_TICKS = 400; - private static final double ARRIVAL_THRESHOLD = 0.1; - private static final int POSITION_CHANGE_CHANCE = 25; -@@ -1351,7 +1351,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - } - } - -- class BeeWanderGoal extends Goal { -+ public class BeeWanderGoal extends Goal { // DivineMC - make public - BeeWanderGoal() { - this.setFlags(EnumSet.of(Goal.Flag.MOVE)); - } -diff --git a/net/minecraft/world/entity/animal/Cat.java b/net/minecraft/world/entity/animal/Cat.java -index edd796fd34e43d66a48104201d885756fdd968c3..32eb4cd68f9caa42230ea50ec8ace12c455b21f8 100644 ---- a/net/minecraft/world/entity/animal/Cat.java -+++ b/net/minecraft/world/entity/animal/Cat.java -@@ -517,7 +517,7 @@ public class Cat extends TamableAnimal implements VariantHolder extends AvoidEntityGoal { -+ public static class CatAvoidEntityGoal extends AvoidEntityGoal { // DivineMC - make public - private final Cat cat; - - public CatAvoidEntityGoal(Cat cat, Class entityClassToAvoid, float maxDist, double walkSpeedModifier, double sprintSpeedModifier) { -@@ -536,7 +536,7 @@ public class Cat extends TamableAnimal implements VariantHolder(this, Creaking.class, 8.0F, 1.0, 1.2)); - this.goalSelector.addGoal(2, new Vindicator.VindicatorBreakDoorGoal(this)); - this.goalSelector.addGoal(3, new AbstractIllager.RaiderOpenDoorGoal(this)); -- this.goalSelector.addGoal(4, new Raider.HoldGroundAttackGoal(this, 10.0F)); -+ this.goalSelector.addGoal(4, new HoldGroundAttackGoal(this, 10.0F)); // DivineMC - Remove Raider reference - this.goalSelector.addGoal(5, new MeleeAttackGoal(this, 1.0, false)); - this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables - this.targetSelector.addGoal(1, new HurtByTargetGoal(this, Raider.class).setAlertOthers()); -diff --git a/net/minecraft/world/entity/raid/Raider.java b/net/minecraft/world/entity/raid/Raider.java -index c06b589e669b055a26f662df60070d5908256220..458e76317f4cd10f881ae2fdf990c392d68f5241 100644 ---- a/net/minecraft/world/entity/raid/Raider.java -+++ b/net/minecraft/world/entity/raid/Raider.java -@@ -475,7 +475,7 @@ public abstract class Raider extends PatrollingMonster { - } - } - -- static class RaiderMoveThroughVillageGoal extends Goal { -+ public class RaiderMoveThroughVillageGoal extends Goal { // DivineMC - package-private static -> public non-static - private final Raider raider; - private final double speedModifier; - private BlockPos poiPos; diff --git a/divinemc-server/minecraft-patches/features/0005-lithium-fast_util.patch b/divinemc-server/minecraft-patches/features/0004-lithium-fast_util.patch similarity index 96% rename from divinemc-server/minecraft-patches/features/0005-lithium-fast_util.patch rename to divinemc-server/minecraft-patches/features/0004-lithium-fast_util.patch index 5996d9f..7abdd99 100644 --- a/divinemc-server/minecraft-patches/features/0005-lithium-fast_util.patch +++ b/divinemc-server/minecraft-patches/features/0004-lithium-fast_util.patch @@ -27,7 +27,7 @@ index 216f97207dac88cc1dc3df59c6ee8a62c7614b4a..05c7de5729466786a0196fa5f91eccc3 public static Direction getApproximateNearest(double x, double y, double z) { diff --git a/net/minecraft/world/phys/AABB.java b/net/minecraft/world/phys/AABB.java -index c9c6e4e460ad8435f12761704bb9b0284d6aa708..b4ae2ad4b6852b7c1042675dafcb15d136c20eab 100644 +index c9c6e4e460ad8435f12761704bb9b0284d6aa708..9581e7d51edaa6c51538a5267f421b034f4bfff0 100644 --- a/net/minecraft/world/phys/AABB.java +++ b/net/minecraft/world/phys/AABB.java @@ -18,6 +18,15 @@ public class AABB { diff --git a/divinemc-server/minecraft-patches/features/0006-Petal-Reduce-work-done-by-game-event-system.patch b/divinemc-server/minecraft-patches/features/0005-Petal-Reduce-work-done-by-game-event-system.patch similarity index 100% rename from divinemc-server/minecraft-patches/features/0006-Petal-Reduce-work-done-by-game-event-system.patch rename to divinemc-server/minecraft-patches/features/0005-Petal-Reduce-work-done-by-game-event-system.patch diff --git a/divinemc-server/minecraft-patches/features/0007-Fix-MC-65198.patch b/divinemc-server/minecraft-patches/features/0006-Fix-MC-65198.patch similarity index 100% rename from divinemc-server/minecraft-patches/features/0007-Fix-MC-65198.patch rename to divinemc-server/minecraft-patches/features/0006-Fix-MC-65198.patch diff --git a/divinemc-server/minecraft-patches/features/0008-C2ME-Optimize-world-gen-math.patch b/divinemc-server/minecraft-patches/features/0007-C2ME-Optimize-world-gen-math.patch similarity index 100% rename from divinemc-server/minecraft-patches/features/0008-C2ME-Optimize-world-gen-math.patch rename to divinemc-server/minecraft-patches/features/0007-C2ME-Optimize-world-gen-math.patch diff --git a/divinemc-server/minecraft-patches/features/0009-Carpet-Fixes-RecipeManager-Optimize.patch b/divinemc-server/minecraft-patches/features/0008-Carpet-Fixes-RecipeManager-Optimize.patch similarity index 96% rename from divinemc-server/minecraft-patches/features/0009-Carpet-Fixes-RecipeManager-Optimize.patch rename to divinemc-server/minecraft-patches/features/0008-Carpet-Fixes-RecipeManager-Optimize.patch index f2903ff..78ff764 100644 --- a/divinemc-server/minecraft-patches/features/0009-Carpet-Fixes-RecipeManager-Optimize.patch +++ b/divinemc-server/minecraft-patches/features/0008-Carpet-Fixes-RecipeManager-Optimize.patch @@ -22,7 +22,7 @@ index aefaac550b58be479cc282f52dea91d4b1e530f6..2877a3229e03285e9ba5ec2bb68e17c9 // CraftBukkit end } diff --git a/net/minecraft/world/item/crafting/RecipeMap.java b/net/minecraft/world/item/crafting/RecipeMap.java -index 320c19a6b3a3b3bf95788cd01f89858af90e8817..a59d03c415a1888098abe5d8309437e2e679591e 100644 +index 098753ddd215b6ef5915fac71d8c4f0b19cf4142..1778e58dca9430756d59d07bf017ebe4cc1f4ed4 100644 --- a/net/minecraft/world/item/crafting/RecipeMap.java +++ b/net/minecraft/world/item/crafting/RecipeMap.java @@ -75,4 +75,24 @@ public class RecipeMap { diff --git a/divinemc-server/minecraft-patches/features/0010-No-chat-sign.patch b/divinemc-server/minecraft-patches/features/0009-No-chat-sign.patch similarity index 100% rename from divinemc-server/minecraft-patches/features/0010-No-chat-sign.patch rename to divinemc-server/minecraft-patches/features/0009-No-chat-sign.patch diff --git a/divinemc-server/minecraft-patches/features/0010-Implement-Secure-Seed.patch b/divinemc-server/minecraft-patches/features/0010-Implement-Secure-Seed.patch new file mode 100644 index 0000000..4980f36 --- /dev/null +++ b/divinemc-server/minecraft-patches/features/0010-Implement-Secure-Seed.patch @@ -0,0 +1,411 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> +Date: Sat, 20 Jul 2024 22:04:52 +0300 +Subject: [PATCH] Implement Secure Seed + +Original license: GPLv3 +Original project: https://github.com/plasmoapp/matter + +diff --git a/net/minecraft/server/dedicated/DedicatedServerProperties.java b/net/minecraft/server/dedicated/DedicatedServerProperties.java +index 5748658abf0b90812005ae9d426df92daf5532f0..a010ff015ea8f2aa3f53681865e3ce95942b7525 100644 +--- a/net/minecraft/server/dedicated/DedicatedServerProperties.java ++++ b/net/minecraft/server/dedicated/DedicatedServerProperties.java +@@ -114,7 +114,17 @@ public class DedicatedServerProperties extends Settings GsonHelper.parse(!property.isEmpty() ? property : "{}"), new JsonObject()), + this.get("level-type", property -> property.toLowerCase(Locale.ROOT), WorldPresets.NORMAL.location().toString()) +diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java +index 6540b2d6a1062d883811ce240c49d30d1925b291..bceaf150f7e9b5c4a08be6102571d8fef68a2fc2 100644 +--- a/net/minecraft/server/level/ServerChunkCache.java ++++ b/net/minecraft/server/level/ServerChunkCache.java +@@ -652,6 +652,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + } + + public ChunkGenerator getGenerator() { ++ space.bxteam.divinemc.seed.Globals.setupGlobals(level); // DivineMC - Implement Secure Seed + return this.chunkMap.generator(); + } + +diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java +index 069f793b6dcc1e560ca0b1070b97f4f0006ae377..e90014f10c4d6b63a6f9307cdbf6adc208811f68 100644 +--- a/net/minecraft/server/level/ServerLevel.java ++++ b/net/minecraft/server/level/ServerLevel.java +@@ -634,6 +634,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + chunkGenerator = new org.bukkit.craftbukkit.generator.CustomChunkGenerator(this, chunkGenerator, gen); + } + // CraftBukkit end ++ space.bxteam.divinemc.seed.Globals.setupGlobals(this); // DivineMC - Implement Secure Seed + boolean flag = server.forceSynchronousWrites(); + DataFixer fixerUpper = server.getFixerUpper(); + // Paper - rewrite chunk system +diff --git a/net/minecraft/world/entity/monster/Slime.java b/net/minecraft/world/entity/monster/Slime.java +index 240a54b210e23d5b79e6bcaf3806aa454668135d..bea5f39e3d9a3546c3092aeda12edc597725e0f2 100644 +--- a/net/minecraft/world/entity/monster/Slime.java ++++ b/net/minecraft/world/entity/monster/Slime.java +@@ -423,8 +423,13 @@ public class Slime extends Mob implements Enemy { + return false; + } + +- ChunkPos chunkPos = new ChunkPos(pos); +- boolean flag = level.getMinecraftWorld().paperConfig().entities.spawning.allChunksAreSlimeChunks || WorldgenRandom.seedSlimeChunk(chunkPos.x, chunkPos.z, ((WorldGenLevel) level).getSeed(), level.getMinecraftWorld().spigotConfig.slimeSeed).nextInt(10) == 0; // Spigot // Paper ++ ChunkPos chunkPos = new ChunkPos(pos); ++ // DivineMC start - Implement Secure Seed ++ boolean isSlimeChunk = space.bxteam.divinemc.configuration.DivineGlobalConfiguration.get().misc.enableSecureSeed ++ ? level.getChunk(chunkPos.x, chunkPos.z).isSlimeChunk() ++ : WorldgenRandom.seedSlimeChunk(chunkPos.x, chunkPos.z, ((WorldGenLevel) level).getSeed(), level.getMinecraftWorld().spigotConfig.slimeSeed).nextInt(10) == 0; // Spigot // Paper ++ boolean flag = level.getMinecraftWorld().paperConfig().entities.spawning.allChunksAreSlimeChunks || isSlimeChunk; ++ // DivineMC end - Implement Secure Seed + // Paper start - Replace rules for Height in Slime Chunks + final double maxHeightSlimeChunk = level.getMinecraftWorld().paperConfig().entities.spawning.slimeSpawnHeight.slimeChunk.maximum; + if (random.nextInt(10) == 0 && flag && pos.getY() < maxHeightSlimeChunk) { +diff --git a/net/minecraft/world/level/chunk/ChunkAccess.java b/net/minecraft/world/level/chunk/ChunkAccess.java +index 6d565b52552534ce9cacfc35ad1bf4adcb69eac3..7c3321b43e9eb1e6d15a571a8292853be4930448 100644 +--- a/net/minecraft/world/level/chunk/ChunkAccess.java ++++ b/net/minecraft/world/level/chunk/ChunkAccess.java +@@ -82,6 +82,10 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh + public final Map blockEntities = new Object2ObjectOpenHashMap<>(); + protected final LevelHeightAccessor levelHeightAccessor; + protected final LevelChunkSection[] sections; ++ // DivineMC start - Implement Secure Seed ++ private boolean slimeChunk; ++ private boolean hasComputedSlimeChunk; ++ // DivineMC end - Implement Secure Seed + // CraftBukkit start - SPIGOT-6814: move to IChunkAccess to account for 1.17 to 1.18 chunk upgrading. + private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry(); + public org.bukkit.craftbukkit.persistence.DirtyCraftPersistentDataContainer persistentDataContainer = new org.bukkit.craftbukkit.persistence.DirtyCraftPersistentDataContainer(ChunkAccess.DATA_TYPE_REGISTRY); +@@ -191,6 +195,17 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh + return GameEventListenerRegistry.NOOP; + } + ++ // DivineMC start - Implement Secure Seed ++ public boolean isSlimeChunk() { ++ if (!hasComputedSlimeChunk) { ++ hasComputedSlimeChunk = true; ++ slimeChunk = space.bxteam.divinemc.seed.WorldgenCryptoRandom.seedSlimeChunk(chunkPos.x, chunkPos.z).nextInt(10) == 0; ++ } ++ ++ return slimeChunk; ++ } ++ // DivineMC end - Implement Secure Seed ++ + public abstract BlockState getBlockState(final int x, final int y, final int z); // Paper + @Nullable + public abstract BlockState setBlockState(BlockPos pos, BlockState state, boolean isMoving); +diff --git a/net/minecraft/world/level/chunk/ChunkGenerator.java b/net/minecraft/world/level/chunk/ChunkGenerator.java +index 6ed51cf42b5864194d671b5b56f5b9bdf0291dc0..ebf6bdace2ac4cb0e1bba3cd3a567e4087fcf58e 100644 +--- a/net/minecraft/world/level/chunk/ChunkGenerator.java ++++ b/net/minecraft/world/level/chunk/ChunkGenerator.java +@@ -343,7 +343,11 @@ public abstract class ChunkGenerator { + Registry registry = level.registryAccess().lookupOrThrow(Registries.STRUCTURE); + Map> map = registry.stream().collect(Collectors.groupingBy(structure1 -> structure1.step().ordinal())); + List list = this.featuresPerStep.get(); +- WorldgenRandom worldgenRandom = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed())); ++ // DivineMC start - Implement Secure Seed ++ WorldgenRandom worldgenRandom = space.bxteam.divinemc.configuration.DivineGlobalConfiguration.get().misc.enableSecureSeed ++ ? new space.bxteam.divinemc.seed.WorldgenCryptoRandom(blockPos.getX(), blockPos.getZ(), space.bxteam.divinemc.seed.Globals.Salt.UNDEFINED, 0) ++ : new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed())); ++ // DivineMC end - Implement Secure Seed + long l = worldgenRandom.setDecorationSeed(level.getSeed(), blockPos.getX(), blockPos.getZ()); + Set> set = new ObjectArraySet<>(); + ChunkPos.rangeClosed(sectionPos.chunk(), 1).forEach(chunkPos -> { +@@ -556,8 +560,18 @@ public abstract class ChunkGenerator { + } else { + ArrayList list1 = new ArrayList<>(list.size()); + list1.addAll(list); +- WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); +- worldgenRandom.setLargeFeatureSeed(structureState.getLevelSeed(), pos.x, pos.z); ++ // DivineMC start - Implement Secure Seed ++ WorldgenRandom worldgenRandom; ++ if (space.bxteam.divinemc.configuration.DivineGlobalConfiguration.get().misc.enableSecureSeed) { ++ worldgenRandom = new space.bxteam.divinemc.seed.WorldgenCryptoRandom( ++ pos.x, pos.z, space.bxteam.divinemc.seed.Globals.Salt.GENERATE_FEATURE, 0 ++ ); ++ } else { ++ worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); ++ ++ worldgenRandom.setLargeFeatureSeed(structureState.getLevelSeed(), pos.x, pos.z); ++ } ++ // DivineMC end - Implement Secure Seed + int i = 0; + + for (StructureSet.StructureSelectionEntry structureSelectionEntry1 : list1) { +diff --git a/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java b/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java +index 619b98e42e254c0c260c171a26a2472ddf59b885..66faaf1a7350ed3561e630ca0aab7dd3dc84dd9f 100644 +--- a/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java ++++ b/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java +@@ -205,14 +205,21 @@ public class ChunkGeneratorStructureState { + List> list = new ArrayList<>(count); + int spread = placement.spread(); + HolderSet holderSet = placement.preferredBiomes(); +- RandomSource randomSource = RandomSource.create(); +- // Paper start - Add missing structure set seed configs +- if (this.conf.strongholdSeed != null && structureSet.is(net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.STRONGHOLDS)) { +- randomSource.setSeed(this.conf.strongholdSeed); +- } else { +- // Paper end - Add missing structure set seed configs +- randomSource.setSeed(this.concentricRingsSeed); +- } // Paper - Add missing structure set seed configs ++ // DivineMC start - Implement Secure Seed ++ RandomSource randomSource = space.bxteam.divinemc.configuration.DivineGlobalConfiguration.get().misc.enableSecureSeed ++ ? new space.bxteam.divinemc.seed.WorldgenCryptoRandom(0, 0, space.bxteam.divinemc.seed.Globals.Salt.STRONGHOLDS, 0) ++ : RandomSource.create(); ++ ++ if (!space.bxteam.divinemc.configuration.DivineGlobalConfiguration.get().misc.enableSecureSeed) { ++ // Paper start - Add missing structure set seed configs ++ if (this.conf.strongholdSeed != null && structureSet.is(net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.STRONGHOLDS)) { ++ randomSource.setSeed(this.conf.strongholdSeed); ++ } else { ++ // Paper end - Add missing structure set seed configs ++ randomSource.setSeed(this.concentricRingsSeed); ++ } // Paper - Add missing structure set seed configs ++ } ++ // DivineMC end - Implement Secure Seed + double d = randomSource.nextDouble() * Math.PI * 2.0; + int i = 0; + int i1 = 0; +diff --git a/net/minecraft/world/level/chunk/status/ChunkStep.java b/net/minecraft/world/level/chunk/status/ChunkStep.java +index b8348976e80578d9eff64eea68c04c603fed49ad..d84099c67704881a99d371ff79177257faa1abfc 100644 +--- a/net/minecraft/world/level/chunk/status/ChunkStep.java ++++ b/net/minecraft/world/level/chunk/status/ChunkStep.java +@@ -60,6 +60,7 @@ public final class ChunkStep implements ca.spottedleaf.moonrise.patches.chunk_sy + } + + public CompletableFuture apply(WorldGenContext worldGenContext, StaticCache2D cache, ChunkAccess chunk) { ++ space.bxteam.divinemc.seed.Globals.setupGlobals(worldGenContext.level()); // DivineMC - Implement Secure Seed + if (chunk.getPersistedStatus().isBefore(this.targetStatus)) { + ProfiledDuration profiledDuration = JvmProfiler.INSTANCE + .onChunkGenerate(chunk.getPos(), worldGenContext.level().dimension(), this.targetStatus.getName()); +diff --git a/net/minecraft/world/level/levelgen/WorldOptions.java b/net/minecraft/world/level/levelgen/WorldOptions.java +index c92508741439a8d0d833ea02d0104416adb83c92..15799548cae858ed7421f0f8cb07fa2f3db67e2b 100644 +--- a/net/minecraft/world/level/levelgen/WorldOptions.java ++++ b/net/minecraft/world/level/levelgen/WorldOptions.java +@@ -9,17 +9,28 @@ import net.minecraft.util.RandomSource; + import org.apache.commons.lang3.StringUtils; + + public class WorldOptions { ++ // DivineMC start - Implement Secure Seed ++ private static final boolean isSecureSeedEnabled = space.bxteam.divinemc.configuration.DivineGlobalConfiguration.get().misc.enableSecureSeed; + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( +- instance -> instance.group( ++ instance -> isSecureSeedEnabled ++ ? instance.group( + Codec.LONG.fieldOf("seed").stable().forGetter(WorldOptions::seed), ++ Codec.LONG_STREAM.fieldOf("feature_seed").stable().forGetter(WorldOptions::featureSeedStream), + Codec.BOOL.fieldOf("generate_features").orElse(true).stable().forGetter(WorldOptions::generateStructures), + Codec.BOOL.fieldOf("bonus_chest").orElse(false).stable().forGetter(WorldOptions::generateBonusChest), +- Codec.STRING.lenientOptionalFieldOf("legacy_custom_options").stable().forGetter(worldOptions -> worldOptions.legacyCustomOptions) +- ) +- .apply(instance, instance.stable(WorldOptions::new)) ++ Codec.STRING.lenientOptionalFieldOf("legacy_custom_options").stable().forGetter(generatorOptions -> generatorOptions.legacyCustomOptions)).apply(instance, instance.stable(WorldOptions::new)) ++ : instance.group( ++ Codec.LONG.fieldOf("seed").stable().forGetter(WorldOptions::seed), ++ Codec.BOOL.fieldOf("generate_features").orElse(true).stable().forGetter(WorldOptions::generateStructures), ++ Codec.BOOL.fieldOf("bonus_chest").orElse(false).stable().forGetter(WorldOptions::generateBonusChest), ++ Codec.STRING.lenientOptionalFieldOf("legacy_custom_options").stable().forGetter(worldOptions -> worldOptions.legacyCustomOptions)).apply(instance, instance.stable(WorldOptions::new)) + ); +- public static final WorldOptions DEMO_OPTIONS = new WorldOptions("North Carolina".hashCode(), true, true); ++ public static final WorldOptions DEMO_OPTIONS = isSecureSeedEnabled ++ ? new WorldOptions("North Carolina".hashCode(), space.bxteam.divinemc.seed.Globals.createRandomWorldSeed(), true, true) ++ : new WorldOptions("North Carolina".hashCode(), true, true); ++ // DivineMC end - Implement Secure Seed + private final long seed; ++ private long[] featureSeed = space.bxteam.divinemc.seed.Globals.createRandomWorldSeed(); // DivineMC - Implement Secure Seed + private final boolean generateStructures; + private final boolean generateBonusChest; + private final Optional legacyCustomOptions; +@@ -28,9 +39,21 @@ public class WorldOptions { + this(seed, generateStructures, generateBonusChest, Optional.empty()); + } + ++ // DivineMC start - Implement Secure Seed ++ public WorldOptions(long seed, long[] featureSeed, boolean generateStructures, boolean bonusChest) { ++ this(seed, featureSeed, generateStructures, bonusChest, Optional.empty()); ++ } ++ + public static WorldOptions defaultWithRandomSeed() { +- return new WorldOptions(randomSeed(), true, false); ++ return isSecureSeedEnabled ++ ? new WorldOptions(randomSeed(), space.bxteam.divinemc.seed.Globals.createRandomWorldSeed(), true, false) ++ : new WorldOptions(randomSeed(), true, false); ++ } ++ ++ private WorldOptions(long seed, java.util.stream.LongStream featureSeed, boolean generateStructures, boolean bonusChest, Optional legacyCustomOptions) { ++ this(seed, featureSeed.toArray(), generateStructures, bonusChest, legacyCustomOptions); + } ++ // DivineMC end - Implement Secure Seed + + public static WorldOptions testWorldWithRandomSeed() { + return new WorldOptions(randomSeed(), false, false); +@@ -43,10 +66,27 @@ public class WorldOptions { + this.legacyCustomOptions = legacyCustomOptions; + } + ++ // DivineMC start - Implement Secure Seed ++ private WorldOptions(long seed, long[] featureSeed, boolean generateStructures, boolean bonusChest, Optional legacyCustomOptions) { ++ this(seed, generateStructures, bonusChest, legacyCustomOptions); ++ this.featureSeed = featureSeed; ++ } ++ // DivineMC end - Implement Secure Seed ++ + public long seed() { + return this.seed; + } + ++ // DivineMC start - Implement Secure Seed ++ public long[] featureSeed() { ++ return this.featureSeed; ++ } ++ ++ public java.util.stream.LongStream featureSeedStream() { ++ return java.util.stream.LongStream.of(this.featureSeed); ++ } ++ // DivineMC end - Implement Secure Seed ++ + public boolean generateStructures() { + return this.generateStructures; + } +@@ -59,17 +99,25 @@ public class WorldOptions { + return this.legacyCustomOptions.isPresent(); + } + ++ // DivineMC start - Implement Secure Seed + public WorldOptions withBonusChest(boolean generateBonusChest) { +- return new WorldOptions(this.seed, this.generateStructures, generateBonusChest, this.legacyCustomOptions); ++ return isSecureSeedEnabled ++ ? new WorldOptions(this.seed, this.featureSeed, this.generateStructures, generateBonusChest, this.legacyCustomOptions) ++ : new WorldOptions(this.seed, this.generateStructures, generateBonusChest, this.legacyCustomOptions); + } + + public WorldOptions withStructures(boolean generateStructures) { +- return new WorldOptions(this.seed, generateStructures, this.generateBonusChest, this.legacyCustomOptions); ++ return isSecureSeedEnabled ++ ? new WorldOptions(this.seed, this.featureSeed, generateStructures, this.generateBonusChest, this.legacyCustomOptions) ++ : new WorldOptions(this.seed, generateStructures, this.generateBonusChest, this.legacyCustomOptions); + } + + public WorldOptions withSeed(OptionalLong seed) { +- return new WorldOptions(seed.orElse(randomSeed()), this.generateStructures, this.generateBonusChest, this.legacyCustomOptions); ++ return isSecureSeedEnabled ++ ? new WorldOptions(seed.orElse(randomSeed()), space.bxteam.divinemc.seed.Globals.createRandomWorldSeed(), this.generateStructures, this.generateBonusChest, this.legacyCustomOptions) ++ : new WorldOptions(seed.orElse(randomSeed()), this.generateStructures, this.generateBonusChest, this.legacyCustomOptions); + } ++ // DivineMC end - Implement Secure Seed + + public static OptionalLong parseSeed(String seed) { + seed = seed.trim(); +diff --git a/net/minecraft/world/level/levelgen/feature/GeodeFeature.java b/net/minecraft/world/level/levelgen/feature/GeodeFeature.java +index 38475f6975533909924c8d54f438cf43cdfe31a3..c5a75c27242698fe2bc0415b8df6657f57024d86 100644 +--- a/net/minecraft/world/level/levelgen/feature/GeodeFeature.java ++++ b/net/minecraft/world/level/levelgen/feature/GeodeFeature.java +@@ -41,7 +41,11 @@ public class GeodeFeature extends Feature { + int i1 = geodeConfiguration.maxGenOffset; + List> list = Lists.newLinkedList(); + int i2 = geodeConfiguration.distributionPoints.sample(randomSource); +- WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(worldGenLevel.getSeed())); ++ // DivineMC start - Implement Secure Seed ++ WorldgenRandom worldgenRandom = space.bxteam.divinemc.configuration.DivineGlobalConfiguration.get().misc.enableSecureSeed ++ ? new space.bxteam.divinemc.seed.WorldgenCryptoRandom(0, 0, space.bxteam.divinemc.seed.Globals.Salt.GEODE_FEATURE, 0) ++ : new WorldgenRandom(new LegacyRandomSource(worldGenLevel.getSeed())); ++ // DivineMC end - Implement Secure Seed + NormalNoise normalNoise = NormalNoise.create(worldgenRandom, -4, 1.0); + List list1 = Lists.newLinkedList(); + double d = (double)i2 / geodeConfiguration.outerWallDistance.getMaxValue(); +diff --git a/net/minecraft/world/level/levelgen/structure/Structure.java b/net/minecraft/world/level/levelgen/structure/Structure.java +index 8328e864c72b7a358d6bb1f33459b8c4df2ecb1a..fcec1e0110c539b12d1153c2982d165cf032197e 100644 +--- a/net/minecraft/world/level/levelgen/structure/Structure.java ++++ b/net/minecraft/world/level/levelgen/structure/Structure.java +@@ -249,6 +249,14 @@ public abstract class Structure { + } + + private static WorldgenRandom makeRandom(long seed, ChunkPos chunkPos) { ++ // DivineMC start - Implement Secure Seed ++ if (space.bxteam.divinemc.configuration.DivineGlobalConfiguration.get().misc.enableSecureSeed) { ++ return new space.bxteam.divinemc.seed.WorldgenCryptoRandom( ++ chunkPos.x, chunkPos.z, space.bxteam.divinemc.seed.Globals.Salt.GENERATE_FEATURE, seed ++ ); ++ } ++ // DivineMC end - Implement Secure Seed ++ + WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); + worldgenRandom.setLargeFeatureSeed(seed, chunkPos.x, chunkPos.z); + return worldgenRandom; +diff --git a/net/minecraft/world/level/levelgen/structure/placement/RandomSpreadStructurePlacement.java b/net/minecraft/world/level/levelgen/structure/placement/RandomSpreadStructurePlacement.java +index ee0d9dddb36b6879fa113299e24f1aa3b2b151cc..f256173d032ca506f34cd55779239d6d5ccae593 100644 +--- a/net/minecraft/world/level/levelgen/structure/placement/RandomSpreadStructurePlacement.java ++++ b/net/minecraft/world/level/levelgen/structure/placement/RandomSpreadStructurePlacement.java +@@ -67,8 +67,17 @@ public class RandomSpreadStructurePlacement extends StructurePlacement { + public ChunkPos getPotentialStructureChunk(long seed, int regionX, int regionZ) { + int i = Math.floorDiv(regionX, this.spacing); + int i1 = Math.floorDiv(regionZ, this.spacing); +- WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); +- worldgenRandom.setLargeFeatureWithSalt(seed, i, i1, this.salt()); ++ // DivineMC start - Implement Secure Seed ++ WorldgenRandom worldgenRandom; ++ if (space.bxteam.divinemc.configuration.DivineGlobalConfiguration.get().misc.enableSecureSeed) { ++ worldgenRandom = new space.bxteam.divinemc.seed.WorldgenCryptoRandom( ++ i, i1, space.bxteam.divinemc.seed.Globals.Salt.POTENTIONAL_FEATURE, this.salt ++ ); ++ } else { ++ worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); ++ worldgenRandom.setLargeFeatureWithSalt(seed, i, i1, this.salt()); ++ } ++ // DivineMC end - Implement Secure Seed + int i2 = this.spacing - this.separation; + int i3 = this.spreadType.evaluate(worldgenRandom, i2); + int i4 = this.spreadType.evaluate(worldgenRandom, i2); +diff --git a/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java b/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java +index 670335a7bbfbc9da64c389977498c22dfcd03251..771320f999c63671db6ad64c15dfee18ecff65c7 100644 +--- a/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java ++++ b/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java +@@ -118,8 +118,17 @@ public abstract class StructurePlacement { + public abstract StructurePlacementType type(); + + private static boolean probabilityReducer(long levelSeed, int regionX, int regionZ, int salt, float probability, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper - Add missing structure set seed configs; ignore here +- WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); +- worldgenRandom.setLargeFeatureWithSalt(levelSeed, regionX, regionZ, salt); ++ // DivineMC start - Implement Secure Seed ++ WorldgenRandom worldgenRandom; ++ if (space.bxteam.divinemc.configuration.DivineGlobalConfiguration.get().misc.enableSecureSeed) { ++ worldgenRandom = new space.bxteam.divinemc.seed.WorldgenCryptoRandom( ++ regionX, regionZ, space.bxteam.divinemc.seed.Globals.Salt.UNDEFINED, salt ++ ); ++ } else { ++ worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); ++ worldgenRandom.setLargeFeatureWithSalt(levelSeed, salt, regionX, regionZ); ++ } ++ // DivineMC end - Implement Secure Seed + return worldgenRandom.nextFloat() < probability; + } + +diff --git a/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java b/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java +index eb85edaa3b7fab4f11545b0fa8bfea882dedb67d..0e8f96a420735298db4be4a2184829d5114b34c5 100644 +--- a/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java ++++ b/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java +@@ -64,7 +64,11 @@ public class JigsawPlacement { + ChunkGenerator chunkGenerator = context.chunkGenerator(); + StructureTemplateManager structureTemplateManager = context.structureTemplateManager(); + LevelHeightAccessor levelHeightAccessor = context.heightAccessor(); +- WorldgenRandom worldgenRandom = context.random(); ++ // DivineMC start - Implement Secure Seed ++ WorldgenRandom worldgenRandom = space.bxteam.divinemc.configuration.DivineGlobalConfiguration.get().misc.enableSecureSeed ++ ? new space.bxteam.divinemc.seed.WorldgenCryptoRandom(context.chunkPos().x, context.chunkPos().z, space.bxteam.divinemc.seed.Globals.Salt.JIGSAW_PLACEMENT, 0) ++ : context.random(); ++ // DivineMC end - Implement Secure Seed + Registry registry = registryAccess.lookupOrThrow(Registries.TEMPLATE_POOL); + Rotation random = Rotation.getRandom(worldgenRandom); + StructureTemplatePool structureTemplatePool = startPool.unwrapKey() diff --git a/divinemc-server/paper-patches/features/0005-Implement-Secure-Seed.patch b/divinemc-server/paper-patches/features/0005-Implement-Secure-Seed.patch new file mode 100644 index 0000000..7696ba4 --- /dev/null +++ b/divinemc-server/paper-patches/features/0005-Implement-Secure-Seed.patch @@ -0,0 +1,43 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> +Date: Sat, 20 Jul 2024 22:04:52 +0300 +Subject: [PATCH] Implement Secure Seed + +Original license: GPLv3 +Original project: https://github.com/plasmoapp/matter + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java +index de8b9048c8395c05b8688bc9d984b8ad680f15b3..832b5743c9b64be1401bda821484730436d88dc0 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java +@@ -206,7 +206,12 @@ public class CraftChunk implements Chunk { + @Override + public boolean isSlimeChunk() { + // 987234911L is deterimined in EntitySlime when seeing if a slime can spawn in a chunk +- return this.worldServer.paperConfig().entities.spawning.allChunksAreSlimeChunks || WorldgenRandom.seedSlimeChunk(this.getX(), this.getZ(), this.getWorld().getSeed(), worldServer.spigotConfig.slimeSeed).nextInt(10) == 0; // Paper ++ // DivineMC start - Implement Secure Seed ++ boolean isSlimeChunk = space.bxteam.divinemc.configuration.DivineGlobalConfiguration.get().misc.enableSecureSeed ++ ? worldServer.getChunk(this.getX(), this.getZ()).isSlimeChunk() ++ : WorldgenRandom.seedSlimeChunk(this.getX(), this.getZ(), this.getWorld().getSeed(), worldServer.spigotConfig.slimeSeed).nextInt(10) == 0; // Paper ++ return this.worldServer.paperConfig().entities.spawning.allChunksAreSlimeChunks || isSlimeChunk; ++ // DivineMC end - Implement Secure Seed + } + + @Override +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index 64f0c861c6ff4e523a882c11aef26f801fc14487..99976733c280dfaf718bf20f49a603355ed3bdaf 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -1403,7 +1403,11 @@ public final class CraftServer implements Server { + registryAccess = levelDataAndDimensions.dimensions().dimensionsRegistryAccess(); + } else { + LevelSettings levelSettings; +- WorldOptions worldOptions = new WorldOptions(creator.seed(), creator.generateStructures(), false); ++ // DivineMC start - Implement Secure Seed ++ WorldOptions worldoptions = space.bxteam.divinemc.configuration.DivineGlobalConfiguration.get().misc.enableSecureSeed ++ ? new WorldOptions(creator.seed(), space.bxteam.divinemc.seed.Globals.createRandomWorldSeed(), creator.generateStructures(), false) ++ : new WorldOptions(creator.seed(), creator.generateStructures(), false); ++ // DivineMC end - Implement Secure Seed + WorldDimensions worldDimensions; + + DedicatedServerProperties.WorldDimensionData properties = new DedicatedServerProperties.WorldDimensionData(GsonHelper.parse((creator.generatorSettings().isEmpty()) ? "{}" : creator.generatorSettings()), creator.type().name().toLowerCase(Locale.ROOT)); diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/configuration/DivineGlobalConfiguration.java b/divinemc-server/src/main/java/space/bxteam/divinemc/configuration/DivineGlobalConfiguration.java index 341932c..a1c9998 100644 --- a/divinemc-server/src/main/java/space/bxteam/divinemc/configuration/DivineGlobalConfiguration.java +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/configuration/DivineGlobalConfiguration.java @@ -33,5 +33,6 @@ public class DivineGlobalConfiguration extends ConfigurationPart { public boolean disableNonEditableSignWarning = true; public boolean removeVanillaUsernameCheck = false; public boolean disableMovedWronglyThreshold = false; + public boolean enableSecureSeed = false; } } diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/seed/Globals.java b/divinemc-server/src/main/java/space/bxteam/divinemc/seed/Globals.java new file mode 100644 index 0000000..886f8c2 --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/seed/Globals.java @@ -0,0 +1,94 @@ +package space.bxteam.divinemc.seed; + +import com.google.common.collect.Iterables; +import net.minecraft.server.level.ServerLevel; + +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.Optional; + +public class Globals { + public static final int WORLD_SEED_LONGS = 16; + public static final int WORLD_SEED_BITS = WORLD_SEED_LONGS * 64; + + public static final long[] worldSeed = new long[WORLD_SEED_LONGS]; + public static final ThreadLocal dimension = ThreadLocal.withInitial(() -> 0); + + public enum Salt { + UNDEFINED, + BASTION_FEATURE, + WOODLAND_MANSION_FEATURE, + MINESHAFT_FEATURE, + BURIED_TREASURE_FEATURE, + NETHER_FORTRESS_FEATURE, + PILLAGER_OUTPOST_FEATURE, + GEODE_FEATURE, + NETHER_FOSSIL_FEATURE, + OCEAN_MONUMENT_FEATURE, + RUINED_PORTAL_FEATURE, + POTENTIONAL_FEATURE, + GENERATE_FEATURE, + JIGSAW_PLACEMENT, + STRONGHOLDS, + POPULATION, + DECORATION, + SLIME_CHUNK + } + + public static void setupGlobals(ServerLevel world) { + if (!space.bxteam.divinemc.configuration.DivineGlobalConfiguration.get().misc.enableSecureSeed) return; + + long[] seed = world.getServer().getWorldData().worldGenOptions().featureSeed(); + System.arraycopy(seed, 0, worldSeed, 0, WORLD_SEED_LONGS); + int worldIndex = Iterables.indexOf(world.getServer().levelKeys(), it -> it == world.dimension()); + if (worldIndex == -1) + worldIndex = world.getServer().levelKeys().size(); // if we are in world construction it may not have been added to the map yet + dimension.set(worldIndex); + } + + public static long[] createRandomWorldSeed() { + long[] seed = new long[WORLD_SEED_LONGS]; + SecureRandom rand = new SecureRandom(); + for (int i = 0; i < WORLD_SEED_LONGS; i++) { + seed[i] = rand.nextLong(); + } + return seed; + } + + // 1024-bit string -> 16 * 64 long[] + public static Optional parseSeed(String seedStr) { + if (seedStr.isEmpty()) return Optional.empty(); + + if (seedStr.length() != WORLD_SEED_BITS) { + throw new IllegalArgumentException("Secure seed length must be " + WORLD_SEED_BITS + "-bit but found " + seedStr.length() + "-bit."); + } + + long[] seed = new long[WORLD_SEED_LONGS]; + + for (int i = 0; i < WORLD_SEED_LONGS; i++) { + int start = i * 64; + int end = start + 64; + String seedSection = seedStr.substring(start, end); + + BigInteger seedInDecimal = new BigInteger(seedSection, 2); + seed[i] = seedInDecimal.longValue(); + } + + return Optional.of(seed); + } + + // 16 * 64 long[] -> 1024-bit string + public static String seedToString(long[] seed) { + StringBuilder sb = new StringBuilder(); + + for (long longV : seed) { + // Convert to 64-bit binary string per long + // Use format to keep 64-bit length, and use 0 to complete space + String binaryStr = String.format("%64s", Long.toBinaryString(longV)).replace(' ', '0'); + + sb.append(binaryStr); + } + + return sb.toString(); + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/seed/Hashing.java b/divinemc-server/src/main/java/space/bxteam/divinemc/seed/Hashing.java new file mode 100644 index 0000000..5974e52 --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/seed/Hashing.java @@ -0,0 +1,73 @@ +package space.bxteam.divinemc.seed; + +public class Hashing { + // https://en.wikipedia.org/wiki/BLAKE_(hash_function) + // https://github.com/bcgit/bc-java/blob/master/core/src/main/java/org/bouncycastle/crypto/digests/Blake2bDigest.java + + private final static long[] blake2b_IV = { + 0x6a09e667f3bcc908L, 0xbb67ae8584caa73bL, 0x3c6ef372fe94f82bL, + 0xa54ff53a5f1d36f1L, 0x510e527fade682d1L, 0x9b05688c2b3e6c1fL, + 0x1f83d9abfb41bd6bL, 0x5be0cd19137e2179L + }; + + private final static byte[][] blake2b_sigma = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, + {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, + {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}, + {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13}, + {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9}, + {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11}, + {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10}, + {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5}, + {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3} + }; + + public static long[] hashWorldSeed(long[] worldSeed) { + long[] result = blake2b_IV.clone(); + result[0] ^= 0x01010040; + hash(worldSeed, result, new long[16], 0, false); + return result; + } + + public static void hash(long[] message, long[] chainValue, long[] internalState, long messageOffset, boolean isFinal) { + assert message.length == 16; + assert chainValue.length == 8; + assert internalState.length == 16; + + System.arraycopy(chainValue, 0, internalState, 0, chainValue.length); + System.arraycopy(blake2b_IV, 0, internalState, chainValue.length, 4); + internalState[12] = messageOffset ^ blake2b_IV[4]; + internalState[13] = blake2b_IV[5]; + if (isFinal) internalState[14] = ~blake2b_IV[6]; + internalState[15] = blake2b_IV[7]; + + for (int round = 0; round < 12; round++) { + G(message[blake2b_sigma[round][0]], message[blake2b_sigma[round][1]], 0, 4, 8, 12, internalState); + G(message[blake2b_sigma[round][2]], message[blake2b_sigma[round][3]], 1, 5, 9, 13, internalState); + G(message[blake2b_sigma[round][4]], message[blake2b_sigma[round][5]], 2, 6, 10, 14, internalState); + G(message[blake2b_sigma[round][6]], message[blake2b_sigma[round][7]], 3, 7, 11, 15, internalState); + G(message[blake2b_sigma[round][8]], message[blake2b_sigma[round][9]], 0, 5, 10, 15, internalState); + G(message[blake2b_sigma[round][10]], message[blake2b_sigma[round][11]], 1, 6, 11, 12, internalState); + G(message[blake2b_sigma[round][12]], message[blake2b_sigma[round][13]], 2, 7, 8, 13, internalState); + G(message[blake2b_sigma[round][14]], message[blake2b_sigma[round][15]], 3, 4, 9, 14, internalState); + } + + for (int i = 0; i < 8; i++) { + chainValue[i] ^= internalState[i] ^ internalState[i + 8]; + } + } + + private static void G(long m1, long m2, int posA, int posB, int posC, int posD, long[] internalState) { + internalState[posA] = internalState[posA] + internalState[posB] + m1; + internalState[posD] = Long.rotateRight(internalState[posD] ^ internalState[posA], 32); + internalState[posC] = internalState[posC] + internalState[posD]; + internalState[posB] = Long.rotateRight(internalState[posB] ^ internalState[posC], 24); // replaces 25 of BLAKE + internalState[posA] = internalState[posA] + internalState[posB] + m2; + internalState[posD] = Long.rotateRight(internalState[posD] ^ internalState[posA], 16); + internalState[posC] = internalState[posC] + internalState[posD]; + internalState[posB] = Long.rotateRight(internalState[posB] ^ internalState[posC], 63); // replaces 11 of BLAKE + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/seed/WorldgenCryptoRandom.java b/divinemc-server/src/main/java/space/bxteam/divinemc/seed/WorldgenCryptoRandom.java new file mode 100644 index 0000000..f6e69a1 --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/seed/WorldgenCryptoRandom.java @@ -0,0 +1,159 @@ +package space.bxteam.divinemc.seed; + +import net.minecraft.util.Mth; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.levelgen.LegacyRandomSource; +import net.minecraft.world.level.levelgen.WorldgenRandom; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; + +public class WorldgenCryptoRandom extends WorldgenRandom { + // hash the world seed to guard against badly chosen world seeds + private static final long[] HASHED_ZERO_SEED = Hashing.hashWorldSeed(new long[Globals.WORLD_SEED_LONGS]); + private static final ThreadLocal LAST_SEEN_WORLD_SEED = ThreadLocal.withInitial(() -> new long[Globals.WORLD_SEED_LONGS]); + private static final ThreadLocal HASHED_WORLD_SEED = ThreadLocal.withInitial(() -> HASHED_ZERO_SEED); + + private final long[] worldSeed = new long[Globals.WORLD_SEED_LONGS]; + private final long[] randomBits = new long[8]; + private int randomBitIndex; + private static final int MAX_RANDOM_BIT_INDEX = 64 * 8; + private static final int LOG2_MAX_RANDOM_BIT_INDEX = 9; + private long counter; + private final long[] message = new long[16]; + private final long[] cachedInternalState = new long[16]; + + public WorldgenCryptoRandom(int x, int z, Globals.Salt typeSalt, long salt) { + super(new LegacyRandomSource(0L)); + if (typeSalt != null) { + this.setSecureSeed(x, z, typeSalt, salt); + } + } + + public void setSecureSeed(int x, int z, Globals.Salt typeSalt, long salt) { + System.arraycopy(Globals.worldSeed, 0, this.worldSeed, 0, Globals.WORLD_SEED_LONGS); + message[0] = ((long) x << 32) | ((long) z & 0xffffffffL); + message[1] = ((long) Globals.dimension.get() << 32) | ((long) salt & 0xffffffffL); + message[2] = typeSalt.ordinal(); + message[3] = counter = 0; + randomBitIndex = MAX_RANDOM_BIT_INDEX; + } + + private long[] getHashedWorldSeed() { + if (!Arrays.equals(worldSeed, LAST_SEEN_WORLD_SEED.get())) { + HASHED_WORLD_SEED.set(Hashing.hashWorldSeed(worldSeed)); + System.arraycopy(worldSeed, 0, LAST_SEEN_WORLD_SEED.get(), 0, Globals.WORLD_SEED_LONGS); + } + return HASHED_WORLD_SEED.get(); + } + + private void moreRandomBits() { + message[3] = counter++; + System.arraycopy(getHashedWorldSeed(), 0, randomBits, 0, 8); + Hashing.hash(message, randomBits, cachedInternalState, 64, true); + } + + private long getBits(int count) { + if (randomBitIndex >= MAX_RANDOM_BIT_INDEX) { + moreRandomBits(); + randomBitIndex -= MAX_RANDOM_BIT_INDEX; + } + + int alignment = randomBitIndex & 63; + if ((randomBitIndex >>> 6) == ((randomBitIndex + count) >>> 6)) { + long result = (randomBits[randomBitIndex >>> 6] >>> alignment) & ((1L << count) - 1); + randomBitIndex += count; + return result; + } else { + long result = (randomBits[randomBitIndex >>> 6] >>> alignment) & ((1L << (64 - alignment)) - 1); + randomBitIndex += count; + if (randomBitIndex >= MAX_RANDOM_BIT_INDEX) { + moreRandomBits(); + randomBitIndex -= MAX_RANDOM_BIT_INDEX; + } + alignment = randomBitIndex & 63; + result <<= alignment; + result |= (randomBits[randomBitIndex >>> 6] >>> (64 - alignment)) & ((1L << alignment) - 1); + + return result; + } + } + + @Override + public @NotNull RandomSource fork() { + WorldgenCryptoRandom fork = new WorldgenCryptoRandom(0, 0, null, 0); + + System.arraycopy(Globals.worldSeed, 0, fork.worldSeed, 0, Globals.WORLD_SEED_LONGS); + fork.message[0] = this.message[0]; + fork.message[1] = this.message[1]; + fork.message[2] = this.message[2]; + fork.message[3] = this.message[3]; + fork.randomBitIndex = this.randomBitIndex; + fork.counter = this.counter; + fork.nextLong(); + + return fork; + } + + @Override + public int next(int bits) { + return (int) getBits(bits); + } + + @Override + public void consumeCount(int count) { + randomBitIndex += count; + if (randomBitIndex >= MAX_RANDOM_BIT_INDEX * 2) { + randomBitIndex -= MAX_RANDOM_BIT_INDEX; + counter += randomBitIndex >>> LOG2_MAX_RANDOM_BIT_INDEX; + randomBitIndex &= MAX_RANDOM_BIT_INDEX - 1; + randomBitIndex += MAX_RANDOM_BIT_INDEX; + } + } + + @Override + public int nextInt(int bound) { + int bits = Mth.ceillog2(bound); + int result; + do { + result = (int) getBits(bits); + } while (result >= bound); + + return result; + } + + @Override + public long nextLong() { + return getBits(64); + } + + @Override + public double nextDouble() { + return getBits(53) * 0x1.0p-53; + } + + @Override + public long setDecorationSeed(long worldSeed, int blockX, int blockZ) { + setSecureSeed(blockX, blockZ, Globals.Salt.POPULATION, 0); + return ((long) blockX << 32) | ((long) blockZ & 0xffffffffL); + } + + @Override + public void setFeatureSeed(long populationSeed, int index, int step) { + setSecureSeed((int) (populationSeed >> 32), (int) populationSeed, Globals.Salt.DECORATION, index + 10000L * step); + } + + @Override + public void setLargeFeatureSeed(long worldSeed, int chunkX, int chunkZ) { + super.setLargeFeatureSeed(worldSeed, chunkX, chunkZ); + } + + @Override + public void setLargeFeatureWithSalt(long worldSeed, int regionX, int regionZ, int salt) { + super.setLargeFeatureWithSalt(worldSeed, regionX, regionZ, salt); + } + + public static RandomSource seedSlimeChunk(int chunkX, int chunkZ) { + return new WorldgenCryptoRandom(chunkX, chunkZ, Globals.Salt.SLIME_CHUNK, 0); + } +}