From 8536527b6967a9553db21dd14548b86b969cce13 Mon Sep 17 00:00:00 2001 From: qouteall Date: Sun, 13 Aug 2023 23:01:27 +0800 Subject: [PATCH] Make DFU error-tolerant to mod custom generator types (#3213) * Make DFU error-tolerant to mod custom generator types. * Fix license. Rename mixin. * Fix license. * Disable remap in Schema2832Mixin --- .../impl/dimension/TaggedChoiceExtension.java | 21 ++++++ .../dimension/TaggedChoiceTypeExtension.java | 21 ++++++ .../mixin/dimension/Schema2832Mixin.java | 56 +++++++++++++++ .../mixin/dimension/TaggedChoiceMixin.java | 57 +++++++++++++++ .../dimension/TaggedChoiceTypeMixin.java | 69 +++++++++++++++++++ .../fabric-dimensions-v1.mixins.json | 5 +- .../dimension/void_earlier_in_hash_map.json | 22 ++++++ 7 files changed, 250 insertions(+), 1 deletion(-) create mode 100644 fabric-dimensions-v1/src/main/java/net/fabricmc/fabric/impl/dimension/TaggedChoiceExtension.java create mode 100644 fabric-dimensions-v1/src/main/java/net/fabricmc/fabric/impl/dimension/TaggedChoiceTypeExtension.java create mode 100644 fabric-dimensions-v1/src/main/java/net/fabricmc/fabric/mixin/dimension/Schema2832Mixin.java create mode 100644 fabric-dimensions-v1/src/main/java/net/fabricmc/fabric/mixin/dimension/TaggedChoiceMixin.java create mode 100644 fabric-dimensions-v1/src/main/java/net/fabricmc/fabric/mixin/dimension/TaggedChoiceTypeMixin.java create mode 100644 fabric-dimensions-v1/src/testmod/resources/data/fabric_dimension/dimension/void_earlier_in_hash_map.json diff --git a/fabric-dimensions-v1/src/main/java/net/fabricmc/fabric/impl/dimension/TaggedChoiceExtension.java b/fabric-dimensions-v1/src/main/java/net/fabricmc/fabric/impl/dimension/TaggedChoiceExtension.java new file mode 100644 index 0000000000..2bb7340afb --- /dev/null +++ b/fabric-dimensions-v1/src/main/java/net/fabricmc/fabric/impl/dimension/TaggedChoiceExtension.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.impl.dimension; + +public interface TaggedChoiceExtension { + void fabric$setFailSoft(boolean cond); +} diff --git a/fabric-dimensions-v1/src/main/java/net/fabricmc/fabric/impl/dimension/TaggedChoiceTypeExtension.java b/fabric-dimensions-v1/src/main/java/net/fabricmc/fabric/impl/dimension/TaggedChoiceTypeExtension.java new file mode 100644 index 0000000000..8e041d1b0f --- /dev/null +++ b/fabric-dimensions-v1/src/main/java/net/fabricmc/fabric/impl/dimension/TaggedChoiceTypeExtension.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.impl.dimension; + +public interface TaggedChoiceTypeExtension { + void fabric$setFailSoft(boolean cond); +} diff --git a/fabric-dimensions-v1/src/main/java/net/fabricmc/fabric/mixin/dimension/Schema2832Mixin.java b/fabric-dimensions-v1/src/main/java/net/fabricmc/fabric/mixin/dimension/Schema2832Mixin.java new file mode 100644 index 0000000000..c441aee85d --- /dev/null +++ b/fabric-dimensions-v1/src/main/java/net/fabricmc/fabric/mixin/dimension/Schema2832Mixin.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.mixin.dimension; + +import java.util.Map; +import java.util.function.Supplier; + +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.types.Type; +import com.mojang.datafixers.types.templates.TaggedChoice; +import com.mojang.datafixers.types.templates.TypeTemplate; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import net.minecraft.datafixer.schema.Schema2832; + +import net.fabricmc.fabric.impl.dimension.TaggedChoiceExtension; + +@Mixin(Schema2832.class) +public class Schema2832Mixin { + /** + * Make the DSL.taggedChoiceLazy to ignore mod custom generator types and not cause deserialization failure. + */ + @Redirect( + method = { + "method_38837", "method_38838" + }, + at = @At( + value = "INVOKE", + target = "Lcom/mojang/datafixers/DSL;taggedChoiceLazy(Ljava/lang/String;Lcom/mojang/datafixers/types/Type;Ljava/util/Map;)Lcom/mojang/datafixers/types/templates/TaggedChoice;", + remap = false + ) + ) + private static TaggedChoice redirectTaggedChoiceLazy( + String name, Type keyType, Map> templates + ) { + TaggedChoice result = DSL.taggedChoiceLazy(name, keyType, templates); + ((TaggedChoiceExtension) (Object) result).fabric$setFailSoft(true); + return result; + } +} diff --git a/fabric-dimensions-v1/src/main/java/net/fabricmc/fabric/mixin/dimension/TaggedChoiceMixin.java b/fabric-dimensions-v1/src/main/java/net/fabricmc/fabric/mixin/dimension/TaggedChoiceMixin.java new file mode 100644 index 0000000000..e1da627b68 --- /dev/null +++ b/fabric-dimensions-v1/src/main/java/net/fabricmc/fabric/mixin/dimension/TaggedChoiceMixin.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.mixin.dimension; + +import com.mojang.datafixers.types.Type; +import com.mojang.datafixers.types.templates.TaggedChoice; +import com.mojang.datafixers.util.Pair; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import net.fabricmc.fabric.impl.dimension.TaggedChoiceExtension; +import net.fabricmc.fabric.impl.dimension.TaggedChoiceTypeExtension; + +@Mixin(value = TaggedChoice.class, remap = false) +public class TaggedChoiceMixin implements TaggedChoiceExtension { + @Unique + boolean failSoft = false; + + @Override + public void fabric$setFailSoft(boolean cond) { + failSoft = cond; + } + + /** + * Pass the failSoft information into TaggedChoice.TaggedChoiceType. + */ + @SuppressWarnings("rawtypes") + @Inject( + method = "lambda$apply$0", at = @At("RETURN"), remap = false + ) + private void onApply(Pair key, CallbackInfoReturnable cir) { + if (failSoft) { + Type returnValue = cir.getReturnValue(); + + if (returnValue instanceof TaggedChoice.TaggedChoiceType taggedChoiceType) { + ((TaggedChoiceTypeExtension) (Object) taggedChoiceType).fabric$setFailSoft(true); + } + } + } +} diff --git a/fabric-dimensions-v1/src/main/java/net/fabricmc/fabric/mixin/dimension/TaggedChoiceTypeMixin.java b/fabric-dimensions-v1/src/main/java/net/fabricmc/fabric/mixin/dimension/TaggedChoiceTypeMixin.java new file mode 100644 index 0000000000..f0339e408e --- /dev/null +++ b/fabric-dimensions-v1/src/main/java/net/fabricmc/fabric/mixin/dimension/TaggedChoiceTypeMixin.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.mixin.dimension; + +import com.mojang.datafixers.types.Type; +import com.mojang.datafixers.types.templates.TaggedChoice; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import net.fabricmc.fabric.impl.dimension.TaggedChoiceTypeExtension; + +@Mixin(value = TaggedChoice.TaggedChoiceType.class, remap = false) +public class TaggedChoiceTypeMixin implements TaggedChoiceTypeExtension { + @Unique + private static final Logger LOGGER = LoggerFactory.getLogger("TaggedChoiceType_DimDataFix"); + + @Shadow(remap = false) + @Final + protected Object2ObjectMap> types; + + @Unique + private boolean failSoft; + + /** + * Make the DSL.taggedChoiceLazy to ignore mod custom generator types and not cause deserialization failure. + * The Codec.PASSTHROUGH will not make Dynamic to be deserialized and serialized to Dynamic. + * This will avoid deserialization failure from DFU when upgrading level.dat that contains mod custom generator types. + */ + @Inject( + method = "getCodec", at = @At("HEAD"), cancellable = true, remap = false + ) + private void onGetCodec(K k, CallbackInfoReturnable>> cir) { + if (failSoft) { + if (!types.containsKey(k)) { + LOGGER.warn("Not recognizing key {}. Using pass-through codec. {}", k, this); + cir.setReturnValue(DataResult.success(Codec.PASSTHROUGH)); + } + } + } + + @Override + public void fabric$setFailSoft(boolean cond) { + failSoft = cond; + } +} diff --git a/fabric-dimensions-v1/src/main/resources/fabric-dimensions-v1.mixins.json b/fabric-dimensions-v1/src/main/resources/fabric-dimensions-v1.mixins.json index bc4fcdd35b..74f20a425f 100644 --- a/fabric-dimensions-v1/src/main/resources/fabric-dimensions-v1.mixins.json +++ b/fabric-dimensions-v1/src/main/resources/fabric-dimensions-v1.mixins.json @@ -4,7 +4,10 @@ "compatibilityLevel": "JAVA_16", "mixins": [ "EntityMixin", - "RegistryCodecsMixin" + "RegistryCodecsMixin", + "Schema2832Mixin", + "TaggedChoiceMixin", + "TaggedChoiceTypeMixin" ], "injectors": { "defaultRequire": 1 diff --git a/fabric-dimensions-v1/src/testmod/resources/data/fabric_dimension/dimension/void_earlier_in_hash_map.json b/fabric-dimensions-v1/src/testmod/resources/data/fabric_dimension/dimension/void_earlier_in_hash_map.json new file mode 100644 index 0000000000..c071854bb0 --- /dev/null +++ b/fabric-dimensions-v1/src/testmod/resources/data/fabric_dimension/dimension/void_earlier_in_hash_map.json @@ -0,0 +1,22 @@ +{ + "$comments": [ + "CompoundListCodec is sensitive to the order of elements in hash map in NbtCompound.", + "In CompoundListCodec, when one entry fails deserialization,", + "entries before it will remain but entries after it will be lost.", + "Coincidentally, fabric_dimension:void's position in hash map is after all vanilla dimensions.", + "When fabric_dimension:void fails deserialization in DFU, the vanilla dimensions will still be deserialized.", + "But a mod dimension's id could be before the vanilla dimensions in the hash map,", + "which will make it deserialize before vanilla dimensions and cause vanilla dimension lost.", + "This dimension fabric_dimension:void_earilier_in_hash_map has a different hashcode ", + "and is before minecraft:the_nether in the hash map, so it can reproduce that issue." + ], + "generator": { + "type": "fabric_dimension:void", + "custom_bool": true, + "biome_source": { + "type": "minecraft:fixed", + "biome": "minecraft:plains" + } + }, + "type": "fabric_dimension:void_type" +}