From bea4f0cbe67522eb887c8957d0516746cdc17152 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Wed, 11 Oct 2023 06:05:02 -0400 Subject: [PATCH] Matter: Add Secure Seed --- patches/server/0035-Matter-Secure-Seed.patch | 953 ++++++++++++++++++ patches/server/0036-Matter-Seed-Command.patch | 53 + 2 files changed, 1006 insertions(+) create mode 100644 patches/server/0035-Matter-Secure-Seed.patch create mode 100644 patches/server/0036-Matter-Seed-Command.patch diff --git a/patches/server/0035-Matter-Secure-Seed.patch b/patches/server/0035-Matter-Secure-Seed.patch new file mode 100644 index 000000000..1ac23c9eb --- /dev/null +++ b/patches/server/0035-Matter-Secure-Seed.patch @@ -0,0 +1,953 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Apehum +Date: Thu, 9 Dec 2021 02:18:17 +0800 +Subject: [PATCH] Matter: Secure Seed + +Original license: GPLv3 +Original project: https://github.com/plasmoapp/matter + +diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java +index 3876d6f0225a0028fd44ff87842fc124b8e680f9..4981539173adf245af65d55a412f5e061f628377 100644 +--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java ++++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java +@@ -49,6 +49,10 @@ import org.slf4j.Logger; + import joptsimple.OptionSet; + // CraftBukkit end + ++// Matter start - Feature Secure Seed ++import su.plo.matter.Globals; ++// Matter end - Feature Secure Seed ++ + public class DedicatedServerProperties extends Settings { + + static final Logger LOGGER = LogUtils.getLogger(); +@@ -165,7 +169,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 0f17b1f50d369c46526d14eefdd9e0a498c11528..8f0aa6e4a092aac0e3ed92a4c075704618ffb737 100644 +--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java ++++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +@@ -48,6 +48,7 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp + import net.minecraft.world.level.storage.DimensionDataStorage; + import net.minecraft.world.level.storage.LevelData; + import net.minecraft.world.level.storage.LevelStorageSource; ++import su.plo.matter.Globals; + + public class ServerChunkCache extends ChunkSource { + +@@ -698,6 +699,8 @@ public class ServerChunkCache extends ChunkSource { + } + + public ChunkGenerator getGenerator() { ++ // Matter ++ Globals.setupGlobals(level); + 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 b487eb9538681db2d0bcd9710a9d73ffa9c76a0c..821edcea6ff4054c9d0421810cafff534c16e27b 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -176,6 +176,10 @@ import org.bukkit.event.world.TimeSkipEvent; + // CraftBukkit end + import it.unimi.dsi.fastutil.ints.IntArrayList; // Paper + ++// Matter start ++import su.plo.matter.Globals; ++// Matter end ++ + public class ServerLevel extends Level implements WorldGenLevel { + + public static final BlockPos END_SPAWN_POINT = new BlockPos(100, 50, 0); +@@ -745,6 +749,7 @@ public class ServerLevel extends Level implements WorldGenLevel { + chunkgenerator = new org.bukkit.craftbukkit.generator.CustomChunkGenerator(this, chunkgenerator, gen); + } + // CraftBukkit end ++ Globals.setupGlobals(this); // Matter + boolean flag2 = minecraftserver.forceSynchronousWrites(); + DataFixer datafixer = minecraftserver.getFixerUpper(); + this.entityStorage = new EntityRegionFileStorage(convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), flag2); // Paper - rewrite chunk system //EntityPersistentStorage entitypersistentstorage = new EntityStorage(this, convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), datafixer, flag2, 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 e2eb27a60fde8a937ad1101bbda54c89ef358826..b1356958ce42bf818f68cb7bc089feb1ee3c5ba5 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/Slime.java ++++ b/src/main/java/net/minecraft/world/entity/monster/Slime.java +@@ -425,7 +425,7 @@ public class Slime extends Mob implements Enemy { + } + + 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 ++ boolean flag = world.getMinecraftWorld().paperConfig().entities.spawning.allChunksAreSlimeChunks || world.getChunk(chunkcoordintpair.x, chunkcoordintpair.z).isSlimeChunk(); // Spigot // Paper // Matter + + // 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 8b96d1b7548d354fbcabe6d1b5e9d6c3e2a5cb9d..f0dc2722029204623c56e8c61989bbf4431813a5 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java ++++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java +@@ -53,6 +53,7 @@ import net.minecraft.world.level.material.Fluid; + import net.minecraft.world.ticks.SerializableTickContainer; + import net.minecraft.world.ticks.TickContainerAccess; + import org.slf4j.Logger; ++import su.plo.matter.WorldgenCryptoRandom; + + public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiomeSource, LightChunk, StructureAccess { + +@@ -82,6 +83,11 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom + protected final LevelHeightAccessor levelHeightAccessor; + protected final LevelChunkSection[] sections; + ++ // Matter start ++ private boolean slimeChunk; ++ private boolean hasComputedSlimeChunk; ++ // Matter 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); +@@ -166,6 +172,17 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom + return GameEventListenerRegistry.NOOP; + } + ++ // Matter start ++ public boolean isSlimeChunk() { ++ if (!hasComputedSlimeChunk) { ++ hasComputedSlimeChunk = true; ++ slimeChunk = WorldgenCryptoRandom.seedSlimeChunk(chunkPos.x, chunkPos.z).nextInt(10) == 0; ++ } ++ ++ return slimeChunk; ++ } ++ // Matter 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 d5c2a608e1b4c8099c96b33d9d758e968350a46d..39edcc5d820cb9c2f141d37f8688356da2c53a39 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java ++++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java +@@ -77,6 +77,11 @@ import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement + import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; + import org.apache.commons.lang3.mutable.MutableBoolean; + ++// Matter start ++import su.plo.matter.Globals; ++import su.plo.matter.WorldgenCryptoRandom; ++// Matter end ++ + public abstract class ChunkGenerator { + + public static final Codec CODEC = BuiltInRegistries.CHUNK_GENERATOR.byNameCodec().dispatchStable(ChunkGenerator::codec, Function.identity()); +@@ -339,8 +344,12 @@ public abstract class ChunkGenerator { + return structure.step().ordinal(); + })); + List list = (List) this.featuresPerStep.get(); +- WorldgenRandom seededrandom = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed())); ++ // Matter start - Feature Secure Seed ++ WorldgenRandom seededrandom = new WorldgenCryptoRandom( ++ blockposition.getX(), blockposition.getZ(), Globals.Salt.UNDEFINED, 0 ++ ); + long i = seededrandom.setDecorationSeed(generatoraccessseed.getSeed(), blockposition.getX(), blockposition.getZ()); ++ // Matter end + Set> set = new ObjectArraySet(); + + ChunkPos.rangeClosed(sectionposition.chunk(), 1).forEach((chunkcoordintpair1) -> { +@@ -578,9 +587,11 @@ 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); ++ // Matter start - Feature Secure Seed ++ WorldgenRandom seededrandom = new WorldgenCryptoRandom( ++ chunkcoordintpair.x, chunkcoordintpair.z, Globals.Salt.GENERATE_FEATURE, 0 ++ ); ++ // Matter 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 f8cd23fb6ea7909b8f30bd21d3f2c7bcc483ef21..9392aae2d10685001649f560386313684bed08af 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java ++++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java +@@ -39,6 +39,11 @@ import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStruct + import org.spigotmc.SpigotWorldConfig; + // Spigot end + ++// Matter start ++import su.plo.matter.Globals; ++import su.plo.matter.WorldgenCryptoRandom; ++// Matter end ++ + public class ChunkGeneratorStructureState { + + private static final Logger LOGGER = LogUtils.getLogger(); +@@ -221,15 +226,9 @@ public class ChunkGeneratorStructureState { + List> list = new ArrayList(j); + int k = placement.spread(); + HolderSet holderset = placement.preferredBiomes(); +- RandomSource randomsource = RandomSource.create(); ++ // Matter - Feature Secure Seed ++ RandomSource randomsource = new WorldgenCryptoRandom(0, 0, Globals.Salt.STRONGHOLDS, 0); + +- // Paper start +- if (this.conf.strongholdSeed != null && structureSetEntry.is(net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.STRONGHOLDS)) { +- randomsource.setSeed(this.conf.strongholdSeed); +- } else { +- // Paper end +- randomsource.setSeed(this.concentricRingsSeed); +- } // Paper + double d0 = randomsource.nextDouble() * 3.141592653589793D * 2.0D; + int l = 0; + int i1 = 0; +diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java b/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java +index a907b79fd8291a0e92db138f37239d17424188a1..b8d3a7424aa5f87977b957294892466b809d3923 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java ++++ b/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java +@@ -28,6 +28,10 @@ import net.minecraft.world.level.levelgen.Heightmap; + import net.minecraft.world.level.levelgen.blending.Blender; + import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; + ++// Matter start ++import su.plo.matter.Globals; ++// Matter end ++ + public class ChunkStatus { + + // Paper start - rewrite chunk system +@@ -253,6 +257,7 @@ public class ChunkStatus { + } + + public CompletableFuture> generate(Executor executor, ServerLevel world, ChunkGenerator generator, StructureTemplateManager structureTemplateManager, ThreadedLevelLightEngine lightingProvider, Function>> fullChunkConverter, List chunks) { ++ Globals.setupGlobals(world); // Matter + ChunkAccess ichunkaccess = (ChunkAccess) chunks.get(chunks.size() / 2); + ProfiledDuration profiledduration = JvmProfiler.INSTANCE.onChunkGenerate(ichunkaccess.getPos(), world.dimension(), this.toString()); + +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 d38cabc9eeb45dd863e5f87b7df3b6327ea6a4a2..1d7e6f3a0ddc0eb8774379fc6fa31cc7c8d6170c 100644 +--- a/src/main/java/net/minecraft/world/level/levelgen/WorldOptions.java ++++ b/src/main/java/net/minecraft/world/level/levelgen/WorldOptions.java +@@ -8,27 +8,47 @@ import java.util.OptionalLong; + import net.minecraft.util.RandomSource; + import org.apache.commons.lang3.StringUtils; + ++// Matter start - Feature Secure Seed ++import java.util.stream.LongStream; ++import su.plo.matter.Globals; ++// Matter end - Feature Secure Seed ++ + public class WorldOptions { ++ // Matter start - Feature Secure Seed + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec((instance) -> { +- return 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.optionalFieldOf("legacy_custom_options").stable().forGetter((generatorOptions) -> { ++ return 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.optionalFieldOf("legacy_custom_options").stable().forGetter((generatorOptions) -> { + return generatorOptions.legacyCustomOptions; + })).apply(instance, instance.stable(WorldOptions::new)); + }); +- public static final WorldOptions DEMO_OPTIONS = new WorldOptions((long)"North Carolina".hashCode(), true, true); ++ // Matter end - Feature Secure Seed ++ public static final WorldOptions DEMO_OPTIONS = new WorldOptions((long)"North Carolina".hashCode(), Globals.createRandomWorldSeed(), true, true); + private final long seed; ++ // Matter - Feature Secure Seed ++ private final long[] featureSeed; + private final boolean generateStructures; + private final boolean generateBonusChest; + private final Optional legacyCustomOptions; + +- public WorldOptions(long seed, boolean generateStructures, boolean bonusChest) { +- 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 new WorldOptions(randomSeed(), Globals.createRandomWorldSeed(), true, false); ++ } ++ ++ // Matter start - Feature Secure Seed ++ private WorldOptions(long seed, LongStream featureSeed, boolean generateStructures, boolean bonusChest, Optional legacyCustomOptions) { ++ this(seed, featureSeed.toArray(), generateStructures, bonusChest, legacyCustomOptions); + } + +- private WorldOptions(long seed, boolean generateStructures, boolean bonusChest, Optional legacyCustomOptions) { ++ private WorldOptions(long seed, long[] featureSeed, boolean generateStructures, boolean bonusChest, Optional legacyCustomOptions) { ++ this.featureSeed = featureSeed; ++ // Matter end - Feature Secure Seed + this.seed = seed; + this.generateStructures = generateStructures; + this.generateBonusChest = bonusChest; +@@ -39,6 +59,16 @@ public class WorldOptions { + return this.seed; + } + ++ // Matter start - Feature Secure Seed ++ public long[] featureSeed() { ++ return this.featureSeed; ++ } ++ ++ public LongStream featureSeedStream() { ++ return LongStream.of(this.featureSeed); ++ } ++ // Matter end - Feature Secure Seed ++ + public boolean generateStructures() { + return this.generateStructures; + } +@@ -52,15 +82,18 @@ public class WorldOptions { + } + + public WorldOptions withBonusChest(boolean bonusChest) { +- return new WorldOptions(this.seed, this.generateStructures, bonusChest, this.legacyCustomOptions); ++ // Matter - Feature Secure Seed ++ return new WorldOptions(this.seed, this.featureSeed, this.generateStructures, bonusChest, this.legacyCustomOptions); + } + + public WorldOptions withStructures(boolean structures) { +- return new WorldOptions(this.seed, structures, this.generateBonusChest, this.legacyCustomOptions); ++ // Matter - Feature Secure Seed ++ return new WorldOptions(this.seed, this.featureSeed, structures, this.generateBonusChest, this.legacyCustomOptions); + } + + public WorldOptions withSeed(OptionalLong seed) { +- return new WorldOptions(seed.orElse(randomSeed()), this.generateStructures, this.generateBonusChest, this.legacyCustomOptions); ++ // Matter - Feature Secure Seed ++ return new WorldOptions(seed.orElse(randomSeed()), Globals.createRandomWorldSeed(), this.generateStructures, this.generateBonusChest, this.legacyCustomOptions); + } + + public static OptionalLong parseSeed(String seed) { +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 f945fae50983424091b58f83ed14f2e8f2621619..ce36a573aec188de6b8ce70ead93333ee4cacd4f 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 +@@ -25,6 +25,11 @@ import net.minecraft.world.level.levelgen.feature.configurations.GeodeConfigurat + import net.minecraft.world.level.levelgen.synth.NormalNoise; + import net.minecraft.world.level.material.FluidState; + ++// Matter start ++import su.plo.matter.Globals; ++import su.plo.matter.WorldgenCryptoRandom; ++// Matter end ++ + public class GeodeFeature extends Feature { + private static final Direction[] DIRECTIONS = Direction.values(); + +@@ -42,7 +47,8 @@ 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())); ++ // Matter - Feature Secure Seed ++ WorldgenRandom worldgenRandom = new WorldgenCryptoRandom(0, 0, Globals.Salt.GEODE_FEATURE, 0); + NormalNoise normalNoise = NormalNoise.create(worldgenRandom, -4, 1.0D); + 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 8eaa1a57e904fe7e540b311c6c5c36b755f021fc..53cfd94fe85b2f99f50f2a3b14a827d8792b8fc9 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 +@@ -38,6 +38,11 @@ import net.minecraft.world.level.levelgen.structure.pieces.PiecesContainer; + import net.minecraft.world.level.levelgen.structure.pieces.StructurePiecesBuilder; + import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; + ++// Matter start ++import su.plo.matter.Globals; ++import su.plo.matter.WorldgenCryptoRandom; ++// Matter end ++ + public abstract class Structure { + public static final Codec DIRECT_CODEC = BuiltInRegistries.STRUCTURE_TYPE.byNameCodec().dispatch(Structure::type, StructureType::codec); + public static final Codec> CODEC = RegistryFileCodec.create(Registries.STRUCTURE, DIRECT_CODEC); +@@ -164,9 +169,11 @@ public abstract class Structure { + } + + private static WorldgenRandom makeRandom(long seed, ChunkPos chunkPos) { +- WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); +- worldgenRandom.setLargeFeatureSeed(seed, chunkPos.x, chunkPos.z); +- return worldgenRandom; ++ // Matter start - Feature Secure Seed ++ return new WorldgenCryptoRandom( ++ chunkPos.x, chunkPos.z, Globals.Salt.GENERATE_FEATURE, seed ++ ); ++ // Matter end + } + } + +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 41e24325dcb37e08936b1d13af34d338487929f3..b1c1106cc174972430d23d0ab5a10c391f75b2a6 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 +@@ -10,7 +10,10 @@ import net.minecraft.world.level.ChunkPos; + import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; + import net.minecraft.world.level.levelgen.LegacyRandomSource; + import net.minecraft.world.level.levelgen.WorldgenRandom; +- ++// Matter start ++import su.plo.matter.Globals; ++import su.plo.matter.WorldgenCryptoRandom; ++// Matter end + public class RandomSpreadStructurePlacement extends StructurePlacement { + public static final Codec CODEC = ExtraCodecs.validate(RecordCodecBuilder.mapCodec((instance) -> { + return placementCodec(instance).and(instance.group(Codec.intRange(0, 4096).fieldOf("spacing").forGetter(RandomSpreadStructurePlacement::spacing), Codec.intRange(0, 4096).fieldOf("separation").forGetter(RandomSpreadStructurePlacement::separation), RandomSpreadType.CODEC.optionalFieldOf("spread_type", RandomSpreadType.LINEAR).forGetter(RandomSpreadStructurePlacement::spreadType))).apply(instance, RandomSpreadStructurePlacement::new); +@@ -51,8 +54,11 @@ 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()); ++ // Matter start ++ WorldgenRandom worldgenRandom = new WorldgenCryptoRandom( ++ i, j, Globals.Salt.POTENTIONAL_FEATURE, this.salt ++ ); ++ // Matter 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 594a2dd3b1d4c29c969d1992b8e93795da00e682..1068c34b3e300d547c6648c9b26270747832e2d2 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 +@@ -17,6 +17,8 @@ import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; + import net.minecraft.world.level.levelgen.LegacyRandomSource; + import net.minecraft.world.level.levelgen.WorldgenRandom; + import net.minecraft.world.level.levelgen.structure.StructureSet; ++import su.plo.matter.Globals; ++import su.plo.matter.WorldgenCryptoRandom; + + public abstract class StructurePlacement { + public static final Codec CODEC = BuiltInRegistries.STRUCTURE_PLACEMENT.byNameCodec().dispatch(StructurePlacement::type, StructurePlacementType::codec); +@@ -92,38 +94,43 @@ 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 - ignore here +- WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); +- worldgenRandom.setLargeFeatureWithSalt(seed, salt, chunkX, chunkZ); ++ // Matter start ++ WorldgenRandom worldgenRandom = new WorldgenCryptoRandom( ++ chunkX, chunkZ, Globals.Salt.UNDEFINED, salt ++ ); ++ // Matter end + return worldgenRandom.nextFloat() < frequency; + } + + private static boolean legacyProbabilityReducerWithDouble(long seed, int salt, int chunkX, int chunkZ, float frequency, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper +- WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); +- if (saltOverride == null) { // Paper +- worldgenRandom.setLargeFeatureSeed(seed, chunkX, chunkZ); +- // Paper start +- } else { +- worldgenRandom.setLargeFeatureWithSalt(seed, chunkX, chunkZ, saltOverride); +- } +- // Paper end ++ // Matter start ++ WorldgenRandom worldgenRandom = new WorldgenCryptoRandom( ++ chunkX, chunkZ, Globals.Salt.MINESHAFT_FEATURE, 0 ++ ); ++ // Matter end + return worldgenRandom.nextDouble() < (double)frequency; + } + + private static boolean legacyArbitrarySaltProbabilityReducer(long seed, int salt, int chunkX, int chunkZ, float frequency, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper +- WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); +- worldgenRandom.setLargeFeatureWithSalt(seed, chunkX, chunkZ, saltOverride != null ? saltOverride : HIGHLY_ARBITRARY_RANDOM_SALT); // Paper ++ // Matter start ++ WorldgenRandom worldgenRandom = new WorldgenCryptoRandom( ++ chunkX, chunkZ, Globals.Salt.BURIED_TREASURE_FEATURE, 0 ++ ); ++ // Matter end + return worldgenRandom.nextFloat() < frequency; + } + + private static boolean legacyPillagerOutpostReducer(long seed, int salt, int chunkX, int chunkZ, float frequency, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper - ignore here + int i = chunkX >> 4; + int j = chunkZ >> 4; +- WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); +- worldgenRandom.setSeed((long)(i ^ j << 4) ^ seed); ++ // Matter - Secure Feature Seed ++ WorldgenRandom worldgenRandom = new WorldgenCryptoRandom( ++ i, j, Globals.Salt.PILLAGER_OUTPOST_FEATURE, 0 ++ ); ++ // Matter end + worldgenRandom.nextInt(); + return worldgenRandom.nextInt((int)(1.0F / frequency)) == 0; + } +- + /** @deprecated */ + @Deprecated + public static record ExclusionZone(Holder otherSet, int chunkCount) { +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 daeed20c7fd0cc02d6915ab5064083c3c2a47ef0..e393ebdfe6d58f25e318a53f0df3e9aa68c4dd3d 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 +@@ -41,6 +41,11 @@ import net.minecraft.world.phys.shapes.VoxelShape; + import org.apache.commons.lang3.mutable.MutableObject; + import org.slf4j.Logger; + ++// Matter start ++import su.plo.matter.Globals; ++import su.plo.matter.WorldgenCryptoRandom; ++// Matter end ++ + public class JigsawPlacement { + static final Logger LOGGER = LogUtils.getLogger(); + +@@ -49,7 +54,11 @@ public class JigsawPlacement { + ChunkGenerator chunkGenerator = context.chunkGenerator(); + StructureTemplateManager structureTemplateManager = context.structureTemplateManager(); + LevelHeightAccessor levelHeightAccessor = context.heightAccessor(); +- WorldgenRandom worldgenRandom = context.random(); ++ // Matter start ++ WorldgenRandom worldgenRandom = new WorldgenCryptoRandom( ++ context.chunkPos().x, context.chunkPos().z, Globals.Salt.JIGSAW_PLACEMENT, 0 ++ ); ++ // Matter end + Registry registry = registryAccess.registryOrThrow(Registries.TEMPLATE_POOL); + Rotation rotation = Rotation.getRandom(worldgenRandom); + StructureTemplatePool structureTemplatePool = structurePool.value(); +@@ -247,18 +256,20 @@ public class JigsawPlacement { + if (!boundingBox2.isInside(blockInfo.pos().relative(JigsawBlock.getFrontFacing(blockInfo.state())))) { + return 0; + } else { +- ResourceKey resourceKey = readPoolName(blockInfo); +- Optional> optional = this.pools.getHolder(resourceKey); +- Optional> optional2 = optional.map((entry) -> { ++ // Mater start - compile fix ++ ResourceKey resourceKey1 = readPoolName(blockInfo); ++ Optional> optional1 = this.pools.getHolder(resourceKey1); ++ Optional> optional2 = optional1.map((entry) -> { + return entry.value().getFallback(); + }); +- int i = optional.map((entry) -> { ++ int i1 = optional1.map((entry) -> { + return entry.value().getMaxSize(this.structureTemplateManager); + }).orElse(0); +- int j = optional2.map((entry) -> { ++ int j1 = optional2.map((entry) -> { + return entry.value().getMaxSize(this.structureTemplateManager); + }).orElse(0); +- return Math.max(i, j); ++ return Math.max(i1, j1); ++ // Mater end - compile fix + } + }).max().orElse(0); + } else { +diff --git a/src/main/java/net/minecraft/world/level/levelgen/structure/structures/EndCityStructure.java b/src/main/java/net/minecraft/world/level/levelgen/structure/structures/EndCityStructure.java +index 2d4a14e8c3f876186c417b9e4284a95f65cf9cd5..991010f818dcb26581a09341201087379b52ee04 100644 +--- a/src/main/java/net/minecraft/world/level/levelgen/structure/structures/EndCityStructure.java ++++ b/src/main/java/net/minecraft/world/level/levelgen/structure/structures/EndCityStructure.java +@@ -10,6 +10,7 @@ import net.minecraft.world.level.levelgen.structure.Structure; + import net.minecraft.world.level.levelgen.structure.StructurePiece; + import net.minecraft.world.level.levelgen.structure.StructureType; + import net.minecraft.world.level.levelgen.structure.pieces.StructurePiecesBuilder; ++import su.plo.matter.WorldgenCryptoRandom; + + public class EndCityStructure extends Structure { + public static final Codec CODEC = simpleCodec(EndCityStructure::new); +diff --git a/src/main/java/net/minecraft/world/level/levelgen/structure/structures/MineshaftStructure.java b/src/main/java/net/minecraft/world/level/levelgen/structure/structures/MineshaftStructure.java +index 087a9c500b9ae1ca2f410dc53e2f251114c97015..b8a7aa83b26f786f082998312990afcb17924341 100644 +--- a/src/main/java/net/minecraft/world/level/levelgen/structure/structures/MineshaftStructure.java ++++ b/src/main/java/net/minecraft/world/level/levelgen/structure/structures/MineshaftStructure.java +@@ -19,6 +19,7 @@ import net.minecraft.world.level.levelgen.WorldgenRandom; + import net.minecraft.world.level.levelgen.structure.Structure; + import net.minecraft.world.level.levelgen.structure.StructureType; + import net.minecraft.world.level.levelgen.structure.pieces.StructurePiecesBuilder; ++import su.plo.matter.WorldgenCryptoRandom; + + public class MineshaftStructure extends Structure { + public static final Codec CODEC = RecordCodecBuilder.create((instance) -> { +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java +index 2b138ee73efb8365aa362e08bfb785e3c1aedabb..44b37d88a67e4140ce319d715c75dbeff7d60b85 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java +@@ -199,7 +199,8 @@ 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 ++ // Matter - Feature Secure Seed ++ return this.worldServer.paperConfig().entities.spawning.allChunksAreSlimeChunks ||worldServer.getChunk(this.getX(), this.getZ()).isSlimeChunk(); + } + + @Override +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index c5308e8104fe16d1c529e220e9714770a470fdbd..8abf09b400ade445c3cb6ea3adb44acfd043f87f 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -268,6 +268,7 @@ import net.md_5.bungee.api.chat.BaseComponent; // Spigot + + import javax.annotation.Nullable; // Paper + import javax.annotation.Nonnull; // Paper ++import su.plo.matter.Globals; // Matter - Feature Secure Seed + + public final class CraftServer implements Server { + private final String serverName = "Leaf"; // Paper // Gale - branding changes // Leaf +@@ -1322,7 +1323,8 @@ public final class CraftServer implements Server { + iregistry = pair.getSecond().dimensions(); + } else { + LevelSettings worldsettings; +- WorldOptions worldoptions = new WorldOptions(creator.seed(), creator.generateStructures(), false); ++ // Matter - Feature Secure Seed ++ WorldOptions worldoptions = new WorldOptions(creator.seed(), Globals.createRandomWorldSeed(), creator.generateStructures(), false); + 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/su/plo/matter/Globals.java b/src/main/java/su/plo/matter/Globals.java +new file mode 100644 +index 0000000000000000000000000000000000000000..89a9d8f7d0032f82c23eb346f688b6cb84ae940e +--- /dev/null ++++ b/src/main/java/su/plo/matter/Globals.java +@@ -0,0 +1,87 @@ ++package su.plo.matter; ++ ++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) { ++ 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; ++ } ++ ++ public static Optional parseSeed(String seedStr) { ++ if (seedStr.isEmpty()) return Optional.empty(); ++ ++ try { ++ long[] seed = new long[WORLD_SEED_LONGS]; ++ BigInteger seedBigInt = new BigInteger(seedStr); ++ if (seedBigInt.signum() < 0) { ++ seedBigInt = seedBigInt.and(BigInteger.ONE.shiftLeft(WORLD_SEED_BITS).subtract(BigInteger.ONE)); ++ } ++ for (int i = 0; i < WORLD_SEED_LONGS; i++) { ++ BigInteger[] divRem = seedBigInt.divideAndRemainder(BigInteger.ONE.shiftLeft(64)); ++ seed[i] = divRem[1].longValue(); ++ seedBigInt = divRem[0]; ++ } ++ return Optional.of(seed); ++ } catch (NumberFormatException ignored) { ++ return Optional.empty(); ++ } ++ } ++ ++ public static String seedToString(long[] seed) { ++ BigInteger seedBigInt = BigInteger.ZERO; ++ for (int i = WORLD_SEED_LONGS - 1; i >= 0; i--) { ++ BigInteger val = BigInteger.valueOf(seed[i]); ++ if (val.signum() < 0) { ++ val = val.add(BigInteger.ONE.shiftLeft(64)); ++ } ++ seedBigInt = seedBigInt.shiftLeft(64).add(val); ++ } ++ ++ return seedBigInt.toString(); ++ } ++} +diff --git a/src/main/java/su/plo/matter/Hashing.java b/src/main/java/su/plo/matter/Hashing.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d18b15ca0b6585f08b30b019c8c17a0f641c8be0 +--- /dev/null ++++ b/src/main/java/su/plo/matter/Hashing.java +@@ -0,0 +1,74 @@ ++package su.plo.matter; ++ ++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/su/plo/matter/WorldgenCryptoRandom.java b/src/main/java/su/plo/matter/WorldgenCryptoRandom.java +new file mode 100644 +index 0000000000000000000000000000000000000000..fab359afe44c573b8b315115810ddefd85b4d22b +--- /dev/null ++++ b/src/main/java/su/plo/matter/WorldgenCryptoRandom.java +@@ -0,0 +1,159 @@ ++package su.plo.matter; ++ ++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/patches/server/0036-Matter-Seed-Command.patch b/patches/server/0036-Matter-Seed-Command.patch new file mode 100644 index 000000000..2309559e6 --- /dev/null +++ b/patches/server/0036-Matter-Seed-Command.patch @@ -0,0 +1,53 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Apehum +Date: Thu, 16 Dec 2021 04:23:40 +0800 +Subject: [PATCH] Matter: Seed Command + +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 342362c217df5476a927eb54cef3cafcea3889fd..ac859a340dfb26de094a09e117e899a4da2e06a2 100644 +--- a/src/main/java/net/minecraft/server/commands/SeedCommand.java ++++ b/src/main/java/net/minecraft/server/commands/SeedCommand.java +@@ -5,17 +5,37 @@ import net.minecraft.commands.CommandSourceStack; + import net.minecraft.commands.Commands; + import net.minecraft.network.chat.Component; + import net.minecraft.network.chat.ComponentUtils; +- ++// Matter start ++import net.minecraft.ChatFormatting; ++import net.minecraft.network.chat.ClickEvent; ++import net.minecraft.network.chat.HoverEvent; ++import su.plo.matter.Globals; ++// Matter end + public class SeedCommand { + public static void register(CommandDispatcher dispatcher, boolean dedicated) { + dispatcher.register(Commands.literal("seed").requires((source) -> { + return !dedicated || source.hasPermission(2); + }).executes((context) -> { + long l = context.getSource().getLevel().getSeed(); +- Component component = ComponentUtils.copyOnClickText(String.valueOf(l)); ++ // Matter start - Seed Command ++ Globals.setupGlobals(context.getSource().getLevel()); ++ String seedStr = Globals.seedToString(Globals.worldSeed); ++ ++ Component seedComponent = ComponentUtils.wrapInSquareBrackets(Component.literal(String.valueOf(l)).withStyle((style) -> { ++ return style.withColor(ChatFormatting.GREEN).withClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, String.valueOf(l))).withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.translatable("chat.copy.click"))).withInsertion(String.valueOf(l)); ++ })); ++ ++ Component featureSeedComponent = ComponentUtils.wrapInSquareBrackets(Component.translatable("chat.copy.click").withStyle((style) -> { ++ return style.withColor(ChatFormatting.GREEN).withClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, seedStr)).withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.translatable("chat.copy.click"))).withInsertion(seedStr); ++ })); ++ ++ context.getSource().sendSuccess(() -> { ++ return Component.translatable("commands.seed.success", seedComponent); ++ }, false); + context.getSource().sendSuccess(() -> { +- return Component.translatable("commands.seed.success", component); ++ return Component.translatable(("Feature seed: %s"), featureSeedComponent); + }, false); ++ // Matter end + return (int)l; + })); + }