From 93ce9e72f725e8d9ec54b59a4e10deb45560a9d7 Mon Sep 17 00:00:00 2001 From: Joseph B Date: Thu, 20 Jan 2022 13:46:08 -0600 Subject: [PATCH] Expand the LevelStem codec to allow dimension jsons to specify that the dimension's chunk generator should use the overworld/server's seed (#7955) --- .../level/dimension/LevelStem.java.patch | 25 +++++++++ .../levelgen/WorldGenSettings.java.patch | 8 +++ .../net/minecraftforge/common/ForgeHooks.java | 53 ++++++++++++++++++- .../resources/META-INF/accesstransformer.cfg | 2 + src/main/resources/forge.exc | 1 + .../dimension/fixed_seed_dimension.json | 12 +++++ .../dimension/server_seed_dimension.json | 13 +++++ 7 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 patches/minecraft/net/minecraft/world/level/dimension/LevelStem.java.patch create mode 100644 src/test/resources/data/dimension_seed_test/dimension/fixed_seed_dimension.json create mode 100644 src/test/resources/data/dimension_seed_test/dimension/server_seed_dimension.json diff --git a/patches/minecraft/net/minecraft/world/level/dimension/LevelStem.java.patch b/patches/minecraft/net/minecraft/world/level/dimension/LevelStem.java.patch new file mode 100644 index 0000000000..370ce316f0 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/level/dimension/LevelStem.java.patch @@ -0,0 +1,25 @@ +--- a/net/minecraft/world/level/dimension/LevelStem.java ++++ b/net/minecraft/world/level/dimension/LevelStem.java +@@ -24,7 +_,9 @@ + + public final class LevelStem { + public static final Codec f_63970_ = RecordCodecBuilder.create((p_63986_) -> { ++ return net.minecraftforge.common.ForgeHooks.expandLevelStemCodec(p_63986_, () -> { // forge: reroute the codec builder here to add extra json fields + return p_63986_.group(DimensionType.f_63853_.fieldOf("type").flatXmap(ExtraCodecs.m_181037_(), ExtraCodecs.m_181037_()).forGetter(LevelStem::m_63981_), ChunkGenerator.f_62136_.fieldOf("generator").forGetter(LevelStem::m_63990_)).apply(p_63986_, p_63986_.stable(LevelStem::new)); ++ }); // forge: we're wrapping the original codec builder in our expansion up there + }); + public static final ResourceKey f_63971_ = ResourceKey.m_135785_(Registry.f_122820_, new ResourceLocation("overworld")); + public static final ResourceKey f_63972_ = ResourceKey.m_135785_(Registry.f_122820_, new ResourceLocation("the_nether")); +@@ -34,6 +_,12 @@ + private final ChunkGenerator f_63976_; + + public LevelStem(Supplier p_63979_, ChunkGenerator p_63980_) { ++ this(p_63979_, p_63980_, false); // forge: by default, dimension jsons use the fixed seed required by the vanilla format ++ } // forge: reroute the vanilla constructor to our expanded constructor with default args ++ private final boolean useServerSeed; // forge: whether the dimension parser should use the server seed instead of the fixed seed ++ public boolean useServerSeed() { return this.useServerSeed; } ++ public LevelStem(Supplier p_63979_, ChunkGenerator p_63980_, boolean useServerSeed) { // forge: allow dimension jsons to specify that they should use the server seed instead of the fixed seed field ++ this.useServerSeed = useServerSeed; // forge + this.f_63975_ = p_63979_; + this.f_63976_ = p_63980_; + } diff --git a/patches/minecraft/net/minecraft/world/level/levelgen/WorldGenSettings.java.patch b/patches/minecraft/net/minecraft/world/level/levelgen/WorldGenSettings.java.patch index 8297f6ae6c..4d67fc7b89 100644 --- a/patches/minecraft/net/minecraft/world/level/levelgen/WorldGenSettings.java.patch +++ b/patches/minecraft/net/minecraft/world/level/levelgen/WorldGenSettings.java.patch @@ -1,5 +1,13 @@ --- a/net/minecraft/world/level/levelgen/WorldGenSettings.java +++ b/net/minecraft/world/level/levelgen/WorldGenSettings.java +@@ -39,6 +_,7 @@ + return p_64626_.group(Codec.LONG.fieldOf("seed").stable().forGetter(WorldGenSettings::m_64619_), Codec.BOOL.fieldOf("generate_features").orElse(true).stable().forGetter(WorldGenSettings::m_64657_), Codec.BOOL.fieldOf("bonus_chest").orElse(false).stable().forGetter(WorldGenSettings::m_64660_), MappedRegistry.m_122747_(Registry.f_122820_, Lifecycle.stable(), LevelStem.f_63970_).xmap(LevelStem::m_63987_, Function.identity()).fieldOf("dimensions").forGetter(WorldGenSettings::m_64663_), Codec.STRING.optionalFieldOf("legacy_custom_options").stable().forGetter((p_158959_) -> { + return p_158959_.f_64606_; + })).apply(p_64626_, p_64626_.stable(WorldGenSettings::new)); ++ }).xmap(net.minecraftforge.common.ForgeHooks::loadDimensionsWithServerSeed, wgs -> {return wgs; // forge: when loading/registering json dimensions, replace hardcoded seeds of custom dimensions with the server/overworld's seed, where requested; fixes MC-195717 + }).comapFlatMap(WorldGenSettings::m_64674_, Function.identity()); + private static final Logger f_64601_ = LogManager.getLogger(); + private final long f_64602_; @@ -198,7 +_,7 @@ String s3 = (String)p_64649_.get("level-type"); String s4 = Optional.ofNullable(s3).map((p_190047_) -> { diff --git a/src/main/java/net/minecraftforge/common/ForgeHooks.java b/src/main/java/net/minecraftforge/common/ForgeHooks.java index 570dce836b..32fa24e675 100644 --- a/src/main/java/net/minecraftforge/common/ForgeHooks.java +++ b/src/main/java/net/minecraftforge/common/ForgeHooks.java @@ -32,6 +32,8 @@ import java.util.Optional; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.Map.Entry; +import java.util.Optional; import java.util.function.Function; import java.util.function.Supplier; import java.util.regex.Matcher; @@ -46,9 +48,12 @@ import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; +import com.mojang.datafixers.kinds.App; +import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Codec; import com.mojang.serialization.Lifecycle; import com.mojang.serialization.codecs.RecordCodecBuilder; +import com.mojang.serialization.codecs.RecordCodecBuilder.Mu; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -100,6 +105,9 @@ import net.minecraft.core.Registry; import net.minecraft.core.MappedRegistry; import net.minecraft.world.*; import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.levelgen.NoiseGeneratorSettings; +import net.minecraft.world.level.levelgen.WorldGenSettings; import net.minecraft.world.level.levelgen.feature.StructureFeature; import net.minecraft.world.level.BaseSpawner; import net.minecraft.world.level.block.entity.FurnaceBlockEntity; @@ -1249,6 +1257,50 @@ public class ForgeHooks { MinecraftForge.EVENT_BUS.post(new EntityEvent.EnteringSection(entity, packedOldPos, packedNewPos)); } + + /** + * Called when dimension jsons are parsed. + * Creates a copy of the worldgen settings and its dimension registry, + * where dimensions that specify that they should use the server seed will use the server seed + * instead of the seed that mojang requires be specified in the json. + **/ + public static WorldGenSettings loadDimensionsWithServerSeed(WorldGenSettings wgs) + { + // get the original worldgen settings' settings + long seed = wgs.seed(); + boolean generateFeatures = wgs.generateFeatures(); + boolean generateBonusChest = wgs.generateBonusChest(); + MappedRegistry originalRegistry = wgs.dimensions(); + Optional legacyCustomOptions = wgs.legacyCustomOptions; + + // make a copy of the dimension registry; for dimensions that specify that they should use the server seed instead + // of the hardcoded json seed, recreate them with the correct seed + MappedRegistry seededRegistry = new MappedRegistry<>(Registry.LEVEL_STEM_REGISTRY, Lifecycle.experimental()); + for (Entry, LevelStem> entry : originalRegistry.entrySet()) + { + ResourceKey key = entry.getKey(); + LevelStem dimension = entry.getValue(); + if (dimension.useServerSeed()) + { + seededRegistry.register(key, new LevelStem(dimension.typeSupplier(), dimension.generator().withSeed(seed), true), originalRegistry.lifecycle(entry.getValue())); + } + else + { + seededRegistry.register(key, dimension, originalRegistry.lifecycle(dimension)); + } + } + + return new WorldGenSettings(seed, generateFeatures, generateBonusChest, seededRegistry, legacyCustomOptions); + } + + /** Called in the LevelStem codec builder to add extra fields to dimension jsons **/ + public static App, LevelStem> expandLevelStemCodec(RecordCodecBuilder.Instance builder, Supplier, LevelStem>> vanillaFieldsSupplier) + { + App, LevelStem> vanillaFields = vanillaFieldsSupplier.get(); + return builder.group(vanillaFields).and( + Codec.BOOL.optionalFieldOf("forge:use_server_seed", false).forGetter(levelStem -> levelStem.useServerSeed())) + .apply(builder, (stem, useServerSeed) -> new LevelStem(stem.typeSupplier(), stem.generator(), useServerSeed)); + } public static void writeAdditionalLevelSaveData(WorldData worldData, CompoundTag levelTag) { @@ -1331,5 +1383,4 @@ public class ForgeHooks LOGGER.error(WORLDPERSISTENCE, buf.toString()); } } - } diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index b30114c5c5..234e8db0f0 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -434,6 +434,8 @@ protected net.minecraft.world.level.levelgen.NoiseSampler f_158643_ # noiseSetti public net.minecraft.world.level.levelgen.NoiseSampler f_158644_ # blendedNoise public net.minecraft.world.level.levelgen.NoiseSampler f_158645_ # islandNoise protected net.minecraft.world.level.levelgen.NoiseSampler m_158667_(DI)D # applySlide +public net.minecraft.world.level.levelgen.WorldGenSettings f_64606_ # legacyCustomOptions +public net.minecraft.world.level.levelgen.WorldGenSettings (JZZLnet/minecraft/core/MappedRegistry;Ljava/util/Optional;)V # constructor public net.minecraft.world.level.levelgen.feature.foliageplacers.FoliagePlacerType (Lcom/mojang/serialization/Codec;)V # constructor public net.minecraft.world.level.levelgen.feature.stateproviders.BlockStateProviderType (Lcom/mojang/serialization/Codec;)V # constructor public net.minecraft.world.level.levelgen.feature.treedecorators.TreeDecoratorType (Lcom/mojang/serialization/Codec;)V # constructor diff --git a/src/main/resources/forge.exc b/src/main/resources/forge.exc index 90f0ce64cf..5f7b231a97 100644 --- a/src/main/resources/forge.exc +++ b/src/main/resources/forge.exc @@ -48,6 +48,7 @@ net/minecraft/world/level/block/FireBlock.tryCatchFire(Lnet/minecraft/world/leve net/minecraft/world/level/block/FlowerPotBlock.(Ljava/util/function/Supplier;Ljava/util/function/Supplier;Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V=|emptyPot,p_53528_,properties net/minecraft/world/level/block/LiquidBlock.(Ljava/util/function/Supplier;Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V=|p_54694_,p_54695_ net/minecraft/world/level/chunk/PalettedContainer.setBits(IZ)V=|p_63122_,forceBits +net/minecraft/world/level/dimension/LevelStem.(Ljava/util/function/Supplier;Lnet/minecraft/world/level/dimension/LevelStem;Z)V=|p_63979_,p_63980_,useServerSeed net/minecraft/world/level/entity/PersistentEntitySectionManager.addEntityWithoutEvent(Lnet/minecraft/world/level/entity/EntityAccess;Z)Z=|p_157539_,p_157540_ net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.processEntityInfos(Lnet/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate;Lnet/minecraft/world/level/LevelAccessor;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/levelgen/structure/templatesystem/StructurePlaceSettings;Ljava/util/List;)Ljava/util/List;=|template,p_215387_0_,p_215387_1_,p_215387_2_,p_215387_3_ net/minecraft/world/level/storage/loot/LootPool.([Lnet/minecraft/world/level/storage/loot/entries/LootPoolEntryContainer;[Lnet/minecraft/world/level/storage/loot/predicates/LootItemCondition;[Lnet/minecraft/world/level/storage/loot/functions/LootItemFunction;Lnet/minecraft/world/level/storage/loot/providers/number/NumberProvider;Lnet/minecraft/world/level/storage/loot/providers/number/NumberProvider;Ljava/lang/String;)V=|p_165128_,p_165129_,p_165130_,p_165131_,p_165132_,name \ No newline at end of file diff --git a/src/test/resources/data/dimension_seed_test/dimension/fixed_seed_dimension.json b/src/test/resources/data/dimension_seed_test/dimension/fixed_seed_dimension.json new file mode 100644 index 0000000000..bf1851255a --- /dev/null +++ b/src/test/resources/data/dimension_seed_test/dimension/fixed_seed_dimension.json @@ -0,0 +1,12 @@ +{ + "type": "minecraft:overworld", + "generator": { + "biome_source": { + "preset": "minecraft:overworld", + "type": "minecraft:multi_noise" + }, + "seed": 12345, + "settings": "minecraft:overworld", + "type": "minecraft:noise" + } +} \ No newline at end of file diff --git a/src/test/resources/data/dimension_seed_test/dimension/server_seed_dimension.json b/src/test/resources/data/dimension_seed_test/dimension/server_seed_dimension.json new file mode 100644 index 0000000000..89d7920aec --- /dev/null +++ b/src/test/resources/data/dimension_seed_test/dimension/server_seed_dimension.json @@ -0,0 +1,13 @@ +{ + "type": "minecraft:overworld", + "generator": { + "biome_source": { + "preset": "minecraft:overworld", + "type": "minecraft:multi_noise" + }, + "seed": 12345, + "settings": "minecraft:overworld", + "type": "minecraft:noise" + }, + "forge:use_server_seed": true +} \ No newline at end of file