From 600a9555f487e6fcfd6b1bf867fd8e8cf8c09f0f Mon Sep 17 00:00:00 2001 From: Luke Bemish Date: Wed, 12 Jun 2024 23:46:40 -0500 Subject: [PATCH] Fancy wrapping logic to not make level files incompatible --- .../biomesquisher/impl/BiomeSquisher.java | 22 ++++--- .../impl/WrappingRuleSource.java | 60 ++++++++++++++++--- .../impl/injected/KnowsOriginalKey.java | 9 +++ .../impl/mixin/MinecraftServerMixin.java | 3 +- .../mixin/NoiseGeneratorSettingsMixin.java | 38 ++++++++++++ .../impl/mixin/RuleSourceMixin.java | 41 +++++++++++++ .../impl/mixin/WorldLoaderMixin.java | 4 +- src/main/resources/biomesquisher.mixins.json | 4 +- 8 files changed, 161 insertions(+), 20 deletions(-) create mode 100644 src/main/java/dev/lukebemish/biomesquisher/impl/injected/KnowsOriginalKey.java create mode 100644 src/main/java/dev/lukebemish/biomesquisher/impl/mixin/NoiseGeneratorSettingsMixin.java create mode 100644 src/main/java/dev/lukebemish/biomesquisher/impl/mixin/RuleSourceMixin.java diff --git a/src/main/java/dev/lukebemish/biomesquisher/impl/BiomeSquisher.java b/src/main/java/dev/lukebemish/biomesquisher/impl/BiomeSquisher.java index 0c4451d..d394db8 100644 --- a/src/main/java/dev/lukebemish/biomesquisher/impl/BiomeSquisher.java +++ b/src/main/java/dev/lukebemish/biomesquisher/impl/BiomeSquisher.java @@ -1,6 +1,7 @@ package dev.lukebemish.biomesquisher.impl; import dev.lukebemish.biomesquisher.BiomeSquisherRegistries; +import dev.lukebemish.biomesquisher.impl.injected.KnowsOriginalKey; import dev.lukebemish.biomesquisher.impl.injected.Squishable; import dev.lukebemish.biomesquisher.impl.mixin.MultiNoiseBiomeSourceAccessor; import dev.lukebemish.biomesquisher.impl.mixin.NoiseBasedChunkGeneratorAccessor; @@ -59,22 +60,25 @@ public static void squishBiomeSource(ResourceManager resourceManager, @Nullable } } - public static void setupSurfaceRuleModification(NoiseGeneratorSettings generator, ResourceKey key) { + public static void setupOriginalKeyAwareGenerators(NoiseGeneratorSettings generator, ResourceKey key) { + //noinspection DataFlowIssue + ((KnowsOriginalKey) (Object) generator).biomesquisher_generatorKey(key); + } + + public static void modifySurfaceRules(NoiseGeneratorSettings generator, RegistryAccess access, ResourceKey backupKey) { var surfaceRulesSource = generator.surfaceRule(); - WrappingRuleSource.NotifyingOps.NotifyingJsonOps ops = WrappingRuleSource.NotifyingOps.NotifyingJsonOps.create(wrapped -> wrapped.generator(key)); + //noinspection DataFlowIssue + var key = ((KnowsOriginalKey) (Object) generator).biomesquisher_generatorKey(); + WrappingRuleSource.NotifyingOps.NotifyingJsonOps ops = WrappingRuleSource.NotifyingOps.NotifyingJsonOps.create(wrapped -> wrapped.modifiers(loadRuleModifiers(key == null ? wrapped.generator() : key, access))); SurfaceRules.RuleSource.CODEC.encodeStart(ops, surfaceRulesSource); if (!ops.isWrapped()) { - var wrappedSource = WrappingRuleSource.create(surfaceRulesSource, key); + var realKey = key == null ? backupKey : key; + var wrappedSource = WrappingRuleSource.create(surfaceRulesSource, realKey); + wrappedSource.modifiers(loadRuleModifiers(realKey, access)); setSurfaceRule(generator, wrappedSource); } } - public static void modifySurfaceRules(NoiseGeneratorSettings generator, RegistryAccess access) { - var surfaceRulesSource = generator.surfaceRule(); - WrappingRuleSource.NotifyingOps.NotifyingJsonOps ops = WrappingRuleSource.NotifyingOps.NotifyingJsonOps.create(wrapped -> wrapped.modifiers(loadRuleModifiers(wrapped.generator(), access))); - SurfaceRules.RuleSource.CODEC.encodeStart(ops, surfaceRulesSource); - } - @SuppressWarnings("unused") @Open( targetClass = NoiseGeneratorSettings.class, diff --git a/src/main/java/dev/lukebemish/biomesquisher/impl/WrappingRuleSource.java b/src/main/java/dev/lukebemish/biomesquisher/impl/WrappingRuleSource.java index 7fab8b2..326fbf9 100644 --- a/src/main/java/dev/lukebemish/biomesquisher/impl/WrappingRuleSource.java +++ b/src/main/java/dev/lukebemish/biomesquisher/impl/WrappingRuleSource.java @@ -1,5 +1,7 @@ package dev.lukebemish.biomesquisher.impl; +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; import com.mojang.serialization.DynamicOps; import com.mojang.serialization.JsonOps; @@ -7,6 +9,7 @@ import com.mojang.serialization.MapLike; import com.mojang.serialization.RecordBuilder; import com.mojang.serialization.codecs.RecordCodecBuilder; +import dev.lukebemish.biomesquisher.impl.injected.KnowsOriginalKey; import dev.lukebemish.biomesquisher.surface.RuleModifier; import dev.lukebemish.biomesquisher.surface.SurfaceRuleInjection; import dev.lukebemish.opensesame.annotations.Coerce; @@ -62,6 +65,51 @@ public RecordBuilder encode(WrappingRuleSource input, DynamicOps ops, } }); + static Codec wrap(Codec original) { + return new Codec<>() { + private static final String BIOMESQUISHER_GENERATOR_KEY = "biome_squisher_generator_key"; + private static final Codec> RESOURCE_KEY_CODEC = ResourceKey.codec(Registries.NOISE_SETTINGS); + + @SuppressWarnings("DataFlowIssue") + @Override + public DataResult> decode(DynamicOps ops, T input) { + return original.decode(ops, input).flatMap(pair -> { + ops.getMap(input).result().ifPresent(mapLike -> { + var generatorKey = mapLike.get(BIOMESQUISHER_GENERATOR_KEY); + if (generatorKey != null) { + var key = RESOURCE_KEY_CODEC.parse(ops, generatorKey).result(); + key.ifPresent(s -> ((KnowsOriginalKey) (Object) pair.getFirst()).biomesquisher_generatorKey(s)); + } + }); + + return DataResult.success(pair); + }); + } + + @SuppressWarnings("DataFlowIssue") + @Override + public DataResult encode(NoiseGeneratorSettings input, DynamicOps ops, T prefix) { + return original.encode(input, ops, prefix).flatMap(it -> { + var key = ((KnowsOriginalKey) (Object) input).biomesquisher_generatorKey(); + + if (key == null) { + // capture the key from a wrapped codec if it's null + //noinspection unchecked + ResourceKey[] keyHolder = new ResourceKey[1]; + WrappingRuleSource.NotifyingOps.NotifyingJsonOps notifyingOps = WrappingRuleSource.NotifyingOps.NotifyingJsonOps.create(wrapped -> keyHolder[0] = wrapped.generator()); + SurfaceRules.RuleSource.CODEC.encodeStart(notifyingOps, input.surfaceRule()); + key = keyHolder[0]; + } + + if (key != null) { + return RESOURCE_KEY_CODEC.encode(key, ops, ops.empty()).flatMap(rk -> ops.mergeToMap(it, ops.createString(BIOMESQUISHER_GENERATOR_KEY), rk)); + } + return DataResult.success(it); + }); + } + }; + } + interface NotifyingOps { void wrapped(WrappingRuleSource source); @@ -76,8 +124,10 @@ protected NotifyingJsonOps(boolean compressed, Consumer acti @Override public void wrapped(WrappingRuleSource source) { - this.action.accept(source); - this.wrapped = true; + if (!this.wrapped) { + this.action.accept(source); + this.wrapped = true; + } } public boolean isWrapped() { @@ -92,18 +142,14 @@ public static NotifyingJsonOps create(Consumer consumer) { @Constructor @SuppressWarnings("unused") - static WrappingRuleSource create(@Field("delegate") @Field.Final SurfaceRules.RuleSource delegate, @Field("generator") ResourceKey generator) { + static WrappingRuleSource create(@Field("delegate") @Field.Final SurfaceRules.RuleSource delegate, @Field("generator") @Field.Final ResourceKey generator) { throw new UnsupportedOperationException("Replaced by OpenSesame at compile time"); } @Field("generator") ResourceKey generator(); - @Field("generator") - void generator(ResourceKey generator); - @Field("delegate") - @Field.Final SurfaceRules.RuleSource delegate(); @Field("modifiers") diff --git a/src/main/java/dev/lukebemish/biomesquisher/impl/injected/KnowsOriginalKey.java b/src/main/java/dev/lukebemish/biomesquisher/impl/injected/KnowsOriginalKey.java new file mode 100644 index 0000000..83f871e --- /dev/null +++ b/src/main/java/dev/lukebemish/biomesquisher/impl/injected/KnowsOriginalKey.java @@ -0,0 +1,9 @@ +package dev.lukebemish.biomesquisher.impl.injected; + +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.levelgen.NoiseGeneratorSettings; + +public interface KnowsOriginalKey { + ResourceKey biomesquisher_generatorKey(); + void biomesquisher_generatorKey(ResourceKey key); +} diff --git a/src/main/java/dev/lukebemish/biomesquisher/impl/mixin/MinecraftServerMixin.java b/src/main/java/dev/lukebemish/biomesquisher/impl/mixin/MinecraftServerMixin.java index 914dd53..3d90c3a 100644 --- a/src/main/java/dev/lukebemish/biomesquisher/impl/mixin/MinecraftServerMixin.java +++ b/src/main/java/dev/lukebemish/biomesquisher/impl/mixin/MinecraftServerMixin.java @@ -53,8 +53,9 @@ private void biomesquisher_load( Utils.LOGGER.info("Not squishing {}; not a MultiNoiseBiomeSource", key.location()); } + var backupKey = ResourceKey.create(Registries.NOISE_SETTINGS, key.location()); var settings = generator.generatorSettings().value(); - BiomeSquisher.modifySurfaceRules(settings, access); + BiomeSquisher.modifySurfaceRules(settings, access, backupKey); } else { Utils.LOGGER.info("Not squishing {}; not a NoiseBasedChunkGenerator", key.location()); } diff --git a/src/main/java/dev/lukebemish/biomesquisher/impl/mixin/NoiseGeneratorSettingsMixin.java b/src/main/java/dev/lukebemish/biomesquisher/impl/mixin/NoiseGeneratorSettingsMixin.java new file mode 100644 index 0000000..6bc9e14 --- /dev/null +++ b/src/main/java/dev/lukebemish/biomesquisher/impl/mixin/NoiseGeneratorSettingsMixin.java @@ -0,0 +1,38 @@ +package dev.lukebemish.biomesquisher.impl.mixin; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.mojang.serialization.Codec; +import dev.lukebemish.biomesquisher.impl.WrappingRuleSource; +import dev.lukebemish.biomesquisher.impl.injected.KnowsOriginalKey; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.levelgen.NoiseGeneratorSettings; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(NoiseGeneratorSettings.class) +public class NoiseGeneratorSettingsMixin implements KnowsOriginalKey { + @Unique + private ResourceKey biomesquisher_generatorKey; + + @Override + public ResourceKey biomesquisher_generatorKey() { + return biomesquisher_generatorKey; + } + + @Override + public synchronized void biomesquisher_generatorKey(ResourceKey key) { + this.biomesquisher_generatorKey = key; + } + + @ModifyExpressionValue( + method = "()V", + at = @At( + value = "INVOKE", + target = "Lcom/mojang/serialization/codecs/RecordCodecBuilder;create(Ljava/util/function/Function;)Lcom/mojang/serialization/Codec;" + ) + ) + private static Codec biome_squisher$wrapCodec(Codec original) { + return WrappingRuleSource.wrap(original); + } +} diff --git a/src/main/java/dev/lukebemish/biomesquisher/impl/mixin/RuleSourceMixin.java b/src/main/java/dev/lukebemish/biomesquisher/impl/mixin/RuleSourceMixin.java new file mode 100644 index 0000000..5f43536 --- /dev/null +++ b/src/main/java/dev/lukebemish/biomesquisher/impl/mixin/RuleSourceMixin.java @@ -0,0 +1,41 @@ +package dev.lukebemish.biomesquisher.impl.mixin; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; +import dev.lukebemish.biomesquisher.impl.WrappingRuleSource; +import net.minecraft.world.level.levelgen.SurfaceRules; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(SurfaceRules.RuleSource.class) +public interface RuleSourceMixin { + @ModifyExpressionValue( + method = "()V", + at = @At( + value = "INVOKE", + target = "Lcom/mojang/serialization/Codec;dispatch(Ljava/util/function/Function;Ljava/util/function/Function;)Lcom/mojang/serialization/Codec;" + ) + ) + private static Codec biome_squisher$wrapCodec(Codec original) { + return new Codec<>() { + @Override + public DataResult> decode(DynamicOps ops, T input) { + return original.decode(ops, input); + } + + @Override + public DataResult encode(SurfaceRules.RuleSource input, DynamicOps ops, T prefix) { + if (input instanceof WrappingRuleSource wrapped) { + if (ops instanceof WrappingRuleSource.NotifyingOps notifying) { + notifying.wrapped(wrapped); + } + return encode(wrapped.delegate(), ops, prefix); + } + return original.encode(input, ops, prefix); + } + }; + } +} diff --git a/src/main/java/dev/lukebemish/biomesquisher/impl/mixin/WorldLoaderMixin.java b/src/main/java/dev/lukebemish/biomesquisher/impl/mixin/WorldLoaderMixin.java index bef6620..b0468f8 100644 --- a/src/main/java/dev/lukebemish/biomesquisher/impl/mixin/WorldLoaderMixin.java +++ b/src/main/java/dev/lukebemish/biomesquisher/impl/mixin/WorldLoaderMixin.java @@ -29,8 +29,8 @@ public abstract class WorldLoaderMixin { noiseSettingsRegistry.forEach(value -> { ResourceKey key = noiseSettingsRegistry.getResourceKey(value).orElseThrow(); Utils.LOGGER.info("Modifying surface rules in {}", key.location()); - BiomeSquisher.setupSurfaceRuleModification(value, key); - BiomeSquisher.modifySurfaceRules(value, access); + BiomeSquisher.setupOriginalKeyAwareGenerators(value, key); + BiomeSquisher.modifySurfaceRules(value, access, key); }); return original; diff --git a/src/main/resources/biomesquisher.mixins.json b/src/main/resources/biomesquisher.mixins.json index dd35fcb..e130819 100644 --- a/src/main/resources/biomesquisher.mixins.json +++ b/src/main/resources/biomesquisher.mixins.json @@ -8,7 +8,9 @@ "ParameterListMixin", "MinecraftServerMixin", "MultiNoiseBiomeSourceMixin", - "WorldLoaderMixin" + "WorldLoaderMixin", + "RuleSourceMixin", + "NoiseGeneratorSettingsMixin" ], "injectors": { "defaultRequire": 1