From 5e85fc0a0928466458ab76dd910779beb45b552f Mon Sep 17 00:00:00 2001 From: deirn <foreirn@gmail.com> Date: Wed, 18 Aug 2021 01:07:12 +0700 Subject: [PATCH] Tag Factory API (#1562) * Tag factory * Add static biome tag factory to the API * Use SERVER_STARTING event * Use the ctor directly * Use the default BIOME factory * AccessorDynamicRegistryManager -> DynamicRegistryManagerAccess * Return Tag.Identified * Load dynamic registry tags right after datapack entries loaded * DynamicRegistryManagerAccess -> DynamicRegistryManagerAccessor * Fix grammar --- fabric-tag-extensions-v0/build.gradle | 6 + .../fabricmc/fabric/api/tag/TagFactory.java | 66 ++++++++++ .../fabricmc/fabric/api/tag/TagRegistry.java | 33 +++-- .../tag/extension/FabricTagManagerHooks.java | 25 ++++ .../impl/tag/extension/TagFactoryImpl.java | 119 ++++++++++++++++++ ...va => DynamicRegistryManagerAccessor.java} | 18 +-- .../tag/extension/MixinMinecraftServer.java | 46 +++++++ .../mixin/tag/extension/MixinRegistryOps.java | 41 ++++++ .../MixinRequiredTagListRegistry.java | 41 ++++++ .../mixin/tag/extension/MixinTagManager.java | 54 ++++++++ .../tag/extension/MixinTagManagerLoader.java | 44 +++++++ .../fabric-tag-extensions-v0.mixins.json | 9 +- .../test/tag/extension/TagExtensionTest.java | 61 +++++++++ .../tag/extension/TagExtensionTestClient.java | 44 +++++++ .../tags/biomes/factory_test.json | 8 ++ .../tags/biomes/json_only_test.json | 7 ++ .../worldgen/biome/test.json | 20 +++ .../src/testmod/resources/fabric.mod.json | 19 +++ 18 files changed, 642 insertions(+), 19 deletions(-) create mode 100644 fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/api/tag/TagFactory.java create mode 100644 fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/impl/tag/extension/FabricTagManagerHooks.java create mode 100644 fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/impl/tag/extension/TagFactoryImpl.java rename fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/{AccessorFluidTags.java => DynamicRegistryManagerAccessor.java} (66%) create mode 100644 fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/MixinMinecraftServer.java create mode 100644 fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/MixinRegistryOps.java create mode 100644 fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/MixinRequiredTagListRegistry.java create mode 100644 fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/MixinTagManager.java create mode 100644 fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/MixinTagManagerLoader.java create mode 100644 fabric-tag-extensions-v0/src/testmod/java/net/fabricmc/fabric/test/tag/extension/TagExtensionTest.java create mode 100644 fabric-tag-extensions-v0/src/testmod/java/net/fabricmc/fabric/test/tag/extension/TagExtensionTestClient.java create mode 100644 fabric-tag-extensions-v0/src/testmod/resources/data/fabric-tag-extensions-v0-testmod/tags/biomes/factory_test.json create mode 100644 fabric-tag-extensions-v0/src/testmod/resources/data/fabric-tag-extensions-v0-testmod/tags/biomes/json_only_test.json create mode 100644 fabric-tag-extensions-v0/src/testmod/resources/data/fabric-tag-extensions-v0-testmod/worldgen/biome/test.json create mode 100644 fabric-tag-extensions-v0/src/testmod/resources/fabric.mod.json diff --git a/fabric-tag-extensions-v0/build.gradle b/fabric-tag-extensions-v0/build.gradle index fdcce387c..c96e26c44 100644 --- a/fabric-tag-extensions-v0/build.gradle +++ b/fabric-tag-extensions-v0/build.gradle @@ -5,3 +5,9 @@ moduleDependencies(project, [ 'fabric-api-base', 'fabric-resource-loader-v0' ]) + +dependencies { + testmodImplementation project(path: ':fabric-command-api-v1', configuration: 'dev') + testmodImplementation project(path: ':fabric-key-binding-api-v1', configuration: 'dev') + testmodImplementation project(path: ':fabric-lifecycle-events-v1', configuration: 'dev') +} diff --git a/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/api/tag/TagFactory.java b/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/api/tag/TagFactory.java new file mode 100644 index 000000000..1b5f4fffb --- /dev/null +++ b/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/api/tag/TagFactory.java @@ -0,0 +1,66 @@ +/* + * 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.api.tag; + +import java.util.function.Supplier; + +import net.minecraft.block.Block; +import net.minecraft.entity.EntityType; +import net.minecraft.fluid.Fluid; +import net.minecraft.item.Item; +import net.minecraft.tag.BlockTags; +import net.minecraft.tag.EntityTypeTags; +import net.minecraft.tag.FluidTags; +import net.minecraft.tag.GameEventTags; +import net.minecraft.tag.ItemTags; +import net.minecraft.tag.Tag; +import net.minecraft.tag.TagGroup; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.registry.RegistryKey; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.event.GameEvent; + +import net.fabricmc.fabric.impl.tag.extension.TagFactoryImpl; + +/** + * A factory for accessing datapack tags. + */ +public interface TagFactory<T> { + TagFactory<Item> ITEM = of(ItemTags::getTagGroup); + TagFactory<Block> BLOCK = of(BlockTags::getTagGroup); + TagFactory<Fluid> FLUID = of(FluidTags::getTagGroup); + TagFactory<GameEvent> GAME_EVENT = of(GameEventTags::getTagGroup); + TagFactory<EntityType<?>> ENTITY_TYPE = of(EntityTypeTags::getTagGroup); + TagFactory<Biome> BIOME = of(Registry.BIOME_KEY, "tags/biomes"); + + /** + * Create a new tag factory for specified registry. + * + * @param registryKey the key of the registry. + * @param dataType the data type of this tag group, vanilla uses "tags/[plural]" format for built-in groups. + */ + static <T> TagFactory<T> of(RegistryKey<? extends Registry<T>> registryKey, String dataType) { + return TagFactoryImpl.of(registryKey, dataType); + } + + static <T> TagFactory<T> of(Supplier<TagGroup<T>> tagGroupSupplier) { + return TagFactoryImpl.of(tagGroupSupplier); + } + + Tag.Identified<T> create(Identifier id); +} diff --git a/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/api/tag/TagRegistry.java b/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/api/tag/TagRegistry.java index 2500ae382..9e0dccf7d 100644 --- a/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/api/tag/TagRegistry.java +++ b/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/api/tag/TagRegistry.java @@ -19,22 +19,21 @@ package net.fabricmc.fabric.api.tag; import java.util.function.Supplier; import net.minecraft.block.Block; -import net.minecraft.tag.TagGroup; import net.minecraft.entity.EntityType; import net.minecraft.fluid.Fluid; import net.minecraft.item.Item; -import net.minecraft.tag.BlockTags; -import net.minecraft.tag.EntityTypeTags; -import net.minecraft.tag.ItemTags; import net.minecraft.tag.Tag; +import net.minecraft.tag.TagGroup; import net.minecraft.util.Identifier; import net.fabricmc.fabric.impl.tag.extension.TagDelegate; -import net.fabricmc.fabric.mixin.tag.extension.AccessorFluidTags; /** * Helper methods for registering Tags. + * + * @deprecated use {@link TagFactory} instead. */ +@Deprecated public final class TagRegistry { private TagRegistry() { } @@ -42,19 +41,35 @@ public final class TagRegistry { return new TagDelegate<>(id, containerSupplier); } + /** + * @deprecated use {@link TagFactory#BLOCK} + */ + @Deprecated public static Tag<Block> block(Identifier id) { - return create(id, BlockTags::getTagGroup); + return TagFactory.BLOCK.create(id); } + /** + * @deprecated use {@link TagFactory#ENTITY_TYPE} + */ + @Deprecated public static Tag<EntityType<?>> entityType(Identifier id) { - return create(id, EntityTypeTags::getTagGroup); + return TagFactory.ENTITY_TYPE.create(id); } + /** + * @deprecated use {@link TagFactory#FLUID} + */ + @Deprecated public static Tag<Fluid> fluid(Identifier id) { - return create(id, () -> AccessorFluidTags.getRequiredTags().getGroup()); + return TagFactory.FLUID.create(id); } + /** + * @deprecated use {@link TagFactory#ITEM} + */ + @Deprecated public static Tag<Item> item(Identifier id) { - return create(id, ItemTags::getTagGroup); + return TagFactory.ITEM.create(id); } } diff --git a/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/impl/tag/extension/FabricTagManagerHooks.java b/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/impl/tag/extension/FabricTagManagerHooks.java new file mode 100644 index 000000000..005a473c2 --- /dev/null +++ b/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/impl/tag/extension/FabricTagManagerHooks.java @@ -0,0 +1,25 @@ +/* + * 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.tag.extension; + +import net.minecraft.tag.TagGroup; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.registry.RegistryKey; + +public interface FabricTagManagerHooks { + void fabric_addTagGroup(RegistryKey<? extends Registry<?>> registryKey, TagGroup<?> tagGroup); +} diff --git a/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/impl/tag/extension/TagFactoryImpl.java b/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/impl/tag/extension/TagFactoryImpl.java new file mode 100644 index 000000000..7e055fb5b --- /dev/null +++ b/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/impl/tag/extension/TagFactoryImpl.java @@ -0,0 +1,119 @@ +/* + * 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.tag.extension; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.function.Supplier; + +import com.google.common.base.Preconditions; +import com.google.common.base.Stopwatch; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import net.minecraft.resource.ResourceManager; +import net.minecraft.resource.ServerResourceManager; +import net.minecraft.server.Main; +import net.minecraft.tag.RequiredTagList; +import net.minecraft.tag.RequiredTagListRegistry; +import net.minecraft.tag.ServerTagManagerHolder; +import net.minecraft.tag.Tag; +import net.minecraft.tag.TagGroup; +import net.minecraft.tag.TagGroupLoader; +import net.minecraft.util.Identifier; +import net.minecraft.util.dynamic.RegistryOps; +import net.minecraft.util.registry.DynamicRegistryManager; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.registry.RegistryKey; + +import net.fabricmc.fabric.api.tag.TagFactory; +import net.fabricmc.fabric.mixin.tag.extension.DynamicRegistryManagerAccessor; + +@SuppressWarnings("ClassCanBeRecord") +public final class TagFactoryImpl<T> implements TagFactory<T> { + private static final Logger LOGGER = LogManager.getLogger(); + + public static final Map<RegistryKey<? extends Registry<?>>, RequiredTagList<?>> TAG_LISTS = new HashMap<>(); + public static final Set<RequiredTagList<?>> DYNAMICS = new HashSet<>(); + + public static <T> TagFactory<T> of(Supplier<TagGroup<T>> tagGroupSupplier) { + return new TagFactoryImpl<>(tagGroupSupplier); + } + + @SuppressWarnings("unchecked") + public static <T> TagFactory<T> of(RegistryKey<? extends Registry<T>> registryKey, String dataType) { + RequiredTagList<T> tagList; + + // Use already registered tag list for the registry if it has the same dataType, in case multiple mods tried to do it. + if (TAG_LISTS.containsKey(registryKey)) { + tagList = (RequiredTagList<T>) TAG_LISTS.get(registryKey); + // Throw an exception if the tagList has different dataType. + Preconditions.checkArgument(tagList.getDataType().equals(dataType), "Tag list for registry %s is already existed with data type %s", registryKey.getValue(), tagList.getDataType()); + } else { + tagList = RequiredTagListRegistry.register(registryKey, dataType); + TAG_LISTS.put(registryKey, tagList); + + // Check whether the registry dynamic. + if (DynamicRegistryManagerAccessor.getInfos().containsKey(registryKey)) { + DYNAMICS.add(tagList); + } + } + + return of(tagList::getGroup); + } + + /** + * Manually load tags for dynamic registries and add the resulting tag group to the tag list. + * + * <p>Minecraft loads the resource manager before dynamic registries, making tags for them fail to load + * if it mentions datapack entries. The solution is to manually load tags after the registry is loaded. + * + * <p>Look at server's {@link Main#main} function calls for {@link ServerResourceManager#reload} and + * {@link RegistryOps#method_36574} for the relevant code. + */ + public static void loadDynamicRegistryTags(DynamicRegistryManager registryManager, ResourceManager resourceManager) { + Stopwatch stopwatch = Stopwatch.createStarted(); + int loadedTags = 0; + + for (RequiredTagList<?> tagList : DYNAMICS) { + RegistryKey<? extends Registry<?>> registryKey = tagList.getRegistryKey(); + Registry<?> registry = registryManager.get(registryKey); + TagGroupLoader<?> tagGroupLoader = new TagGroupLoader<>(registry::getOrEmpty, tagList.getDataType()); + TagGroup<?> tagGroup = tagGroupLoader.load(resourceManager); + ((FabricTagManagerHooks) ServerTagManagerHolder.getTagManager()).fabric_addTagGroup(registryKey, tagGroup); + tagList.updateTagManager(ServerTagManagerHolder.getTagManager()); + loadedTags += tagGroup.getTags().size(); + } + + if (loadedTags > 0) { + LOGGER.info("Loaded {} dynamic registry tags in {}", loadedTags, stopwatch); + } + } + + private final Supplier<TagGroup<T>> tagGroupSupplier; + + private TagFactoryImpl(Supplier<TagGroup<T>> tagGroupSupplier) { + this.tagGroupSupplier = tagGroupSupplier; + } + + @Override + public Tag.Identified<T> create(Identifier id) { + return new TagDelegate<>(id, tagGroupSupplier); + } +} diff --git a/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/AccessorFluidTags.java b/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/DynamicRegistryManagerAccessor.java similarity index 66% rename from fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/AccessorFluidTags.java rename to fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/DynamicRegistryManagerAccessor.java index 8a594d177..614221590 100644 --- a/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/AccessorFluidTags.java +++ b/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/DynamicRegistryManagerAccessor.java @@ -16,17 +16,19 @@ package net.fabricmc.fabric.mixin.tag.extension; +import java.util.Map; + import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -import net.minecraft.fluid.Fluid; -import net.minecraft.tag.FluidTags; -import net.minecraft.tag.RequiredTagList; +import net.minecraft.util.registry.DynamicRegistryManager; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.registry.RegistryKey; -@Mixin(FluidTags.class) -public interface AccessorFluidTags { - @Accessor("REQUIRED_TAGS") - static RequiredTagList<Fluid> getRequiredTags() { - throw new UnsupportedOperationException(); +@Mixin(DynamicRegistryManager.class) +public interface DynamicRegistryManagerAccessor { + @Accessor("INFOS") + static Map<RegistryKey<? extends Registry<?>>, ?> getInfos() { + throw new AssertionError(); } } diff --git a/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/MixinMinecraftServer.java b/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/MixinMinecraftServer.java new file mode 100644 index 000000000..4a23e1312 --- /dev/null +++ b/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/MixinMinecraftServer.java @@ -0,0 +1,46 @@ +/* + * 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.tag.extension; + +import java.util.Collection; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.resource.ServerResourceManager; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.registry.DynamicRegistryManager; + +import net.fabricmc.fabric.impl.tag.extension.TagFactoryImpl; + +@Mixin(MinecraftServer.class) +public abstract class MixinMinecraftServer { + @Shadow + @Final + protected DynamicRegistryManager.Impl registryManager; + + @SuppressWarnings("UnresolvedMixinReference") + @Inject(method = "method_29440", at = @At(value = "INVOKE", target = "Lnet/minecraft/resource/ServerResourceManager;loadRegistryTags()V", shift = At.Shift.AFTER)) + private void method_29440(Collection<?> collection, ServerResourceManager serverResourceManager, CallbackInfo ci) { + // Load dynamic registry tags on datapack reload. + TagFactoryImpl.loadDynamicRegistryTags(registryManager, serverResourceManager.getResourceManager()); + } +} diff --git a/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/MixinRegistryOps.java b/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/MixinRegistryOps.java new file mode 100644 index 000000000..2c73ca40f --- /dev/null +++ b/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/MixinRegistryOps.java @@ -0,0 +1,41 @@ +/* + * 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.tag.extension; + +import com.mojang.serialization.DynamicOps; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import net.minecraft.resource.ResourceManager; +import net.minecraft.util.dynamic.RegistryOps; +import net.minecraft.util.registry.DynamicRegistryManager; + +import net.fabricmc.fabric.impl.tag.extension.TagFactoryImpl; + +/** + * This mixin loads dynamic registry tags right after datapack entries loaded. + * Needs a higher priority so it will be called before biome modifications. + */ +@Mixin(value = RegistryOps.class, priority = 900) +public class MixinRegistryOps { + @Inject(method = "method_36574", at = @At("RETURN")) + private static <T> void afterDynamicRegistryLoaded(DynamicOps<T> dynamicOps, ResourceManager resourceManager, DynamicRegistryManager registryManager, CallbackInfoReturnable<RegistryOps<T>> cir) { + TagFactoryImpl.loadDynamicRegistryTags(registryManager, resourceManager); + } +} diff --git a/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/MixinRequiredTagListRegistry.java b/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/MixinRequiredTagListRegistry.java new file mode 100644 index 000000000..eec8f6254 --- /dev/null +++ b/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/MixinRequiredTagListRegistry.java @@ -0,0 +1,41 @@ +/* + * 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.tag.extension; + +import java.util.HashSet; +import java.util.Set; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import net.minecraft.tag.RequiredTagList; +import net.minecraft.tag.RequiredTagListRegistry; + +import net.fabricmc.fabric.impl.tag.extension.TagFactoryImpl; + +@Mixin(RequiredTagListRegistry.class) +public class MixinRequiredTagListRegistry { + @Inject(method = "getBuiltinTags", at = @At("TAIL"), cancellable = true) + private static void getBuiltinTags(CallbackInfoReturnable<Set<RequiredTagList<?>>> cir) { + // Add tag lists registered on fabric to the map. + Set<RequiredTagList<?>> set = new HashSet<>(cir.getReturnValue()); + set.addAll(TagFactoryImpl.TAG_LISTS.values()); + cir.setReturnValue(set); + } +} diff --git a/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/MixinTagManager.java b/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/MixinTagManager.java new file mode 100644 index 000000000..e3b6b6497 --- /dev/null +++ b/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/MixinTagManager.java @@ -0,0 +1,54 @@ +/* + * 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.tag.extension; + +import java.util.HashMap; +import java.util.Map; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.tag.TagGroup; +import net.minecraft.tag.TagManager; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.registry.RegistryKey; + +import net.fabricmc.fabric.impl.tag.extension.FabricTagManagerHooks; + +@Mixin(TagManager.class) +public class MixinTagManager implements FabricTagManagerHooks { + @Shadow + @Mutable + @Final + private Map<RegistryKey<? extends Registry<?>>, TagGroup<?>> tagGroups; + + @Inject(method = "<init>", at = @At("TAIL")) + private void init(Map<RegistryKey<? extends Registry<?>>, TagGroup<?>> tagGroups, CallbackInfo ci) { + // Make it mutable so we can add dynamic registry tags later. + this.tagGroups = new HashMap<>(tagGroups); + } + + @Override + public void fabric_addTagGroup(RegistryKey<? extends Registry<?>> registryKey, TagGroup<?> tagGroup) { + tagGroups.put(registryKey, tagGroup); + } +} diff --git a/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/MixinTagManagerLoader.java b/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/MixinTagManagerLoader.java new file mode 100644 index 000000000..5562365ba --- /dev/null +++ b/fabric-tag-extensions-v0/src/main/java/net/fabricmc/fabric/mixin/tag/extension/MixinTagManagerLoader.java @@ -0,0 +1,44 @@ +/* + * 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.tag.extension; + +import java.util.List; +import java.util.concurrent.Executor; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.resource.ResourceManager; +import net.minecraft.tag.RequiredTagList; +import net.minecraft.tag.TagManagerLoader; + +import net.fabricmc.fabric.impl.tag.extension.TagFactoryImpl; + +@Mixin(TagManagerLoader.class) +public abstract class MixinTagManagerLoader { + // RequiredTagListRegistry.forEach in reload. + @SuppressWarnings("UnresolvedMixinReference") + @Inject(method = "method_33179", at = @At("HEAD"), cancellable = true) + private void method_33179(ResourceManager resourceManager, Executor executor, List<?> list, RequiredTagList<?> requiredTagList, CallbackInfo ci) { + // Don't load dynamic registry tags now, we need to load them after the dynamic registry. + if (TagFactoryImpl.DYNAMICS.contains(requiredTagList)) { + ci.cancel(); + } + } +} diff --git a/fabric-tag-extensions-v0/src/main/resources/fabric-tag-extensions-v0.mixins.json b/fabric-tag-extensions-v0/src/main/resources/fabric-tag-extensions-v0.mixins.json index 2b234483f..4822ac834 100644 --- a/fabric-tag-extensions-v0/src/main/resources/fabric-tag-extensions-v0.mixins.json +++ b/fabric-tag-extensions-v0/src/main/resources/fabric-tag-extensions-v0.mixins.json @@ -3,10 +3,15 @@ "package": "net.fabricmc.fabric.mixin.tag.extension", "compatibilityLevel": "JAVA_16", "mixins": [ - "AccessorFluidTags", + "DynamicRegistryManagerAccessor", + "MixinMinecraftServer", "MixinObjectBuilder", + "MixinRegistryOps", + "MixinRequiredTagListRegistry", + "MixinTagBuilder", "MixinTagImpl", - "MixinTagBuilder" + "MixinTagManager", + "MixinTagManagerLoader" ], "injectors": { "defaultRequire": 1 diff --git a/fabric-tag-extensions-v0/src/testmod/java/net/fabricmc/fabric/test/tag/extension/TagExtensionTest.java b/fabric-tag-extensions-v0/src/testmod/java/net/fabricmc/fabric/test/tag/extension/TagExtensionTest.java new file mode 100644 index 000000000..b080c794f --- /dev/null +++ b/fabric-tag-extensions-v0/src/testmod/java/net/fabricmc/fabric/test/tag/extension/TagExtensionTest.java @@ -0,0 +1,61 @@ +/* + * 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.test.tag.extension; + +import static net.minecraft.server.command.CommandManager.literal; + +import java.util.Map; +import java.util.Optional; + +import net.minecraft.tag.Tag; +import net.minecraft.text.LiteralText; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.registry.RegistryKey; +import net.minecraft.world.biome.Biome; + +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; +import net.fabricmc.fabric.api.tag.TagFactory; + +public class TagExtensionTest implements ModInitializer { + static final Tag<Biome> FACTORY_TEST = TagFactory.BIOME.create(new Identifier("fabric-tag-extensions-v0-testmod:factory_test")); + + @Override + public void onInitialize() { + CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> dispatcher.register(literal("biome_tag_test") + .then(literal("factory").executes(context -> { + FACTORY_TEST.values().forEach(biome -> { + Identifier id = context.getSource().getRegistryManager().get(Registry.BIOME_KEY).getId(biome); + context.getSource().sendFeedback(new LiteralText(id.toString()), false); + }); + return 1; + })) + .then(literal("list_all").executes(context -> { + Map<Identifier, Tag<Biome>> tags = context.getSource().getServer().getTagManager().getOrCreateTagGroup(Registry.BIOME_KEY).getTags(); + tags.forEach((tagId, tag) -> { + LiteralText text = new LiteralText(tagId.toString() + ":"); + tag.values().forEach(biome -> { + Optional<RegistryKey<Biome>> biomeKey = context.getSource().getRegistryManager().get(Registry.BIOME_KEY).getKey(biome); + biomeKey.ifPresent(key -> text.append(" " + key.getValue())); + }); + context.getSource().sendFeedback(text, false); + }); + return 1; + })))); + } +} diff --git a/fabric-tag-extensions-v0/src/testmod/java/net/fabricmc/fabric/test/tag/extension/TagExtensionTestClient.java b/fabric-tag-extensions-v0/src/testmod/java/net/fabricmc/fabric/test/tag/extension/TagExtensionTestClient.java new file mode 100644 index 000000000..22295a972 --- /dev/null +++ b/fabric-tag-extensions-v0/src/testmod/java/net/fabricmc/fabric/test/tag/extension/TagExtensionTestClient.java @@ -0,0 +1,44 @@ +/* + * 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.test.tag.extension; + +import org.lwjgl.glfw.GLFW; + +import net.minecraft.client.option.KeyBinding; +import net.minecraft.text.LiteralText; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; + +public class TagExtensionTestClient implements ClientModInitializer { + static final KeyBinding KEY_BINDING = KeyBindingHelper.registerKeyBinding(new KeyBinding("tag_test", GLFW.GLFW_KEY_EQUAL, "tag_test")); + + @Override + public void onInitializeClient() { + ClientTickEvents.END_CLIENT_TICK.register(client -> { + if (KEY_BINDING.isPressed()) { + TagExtensionTest.FACTORY_TEST.values().forEach(biome -> { + Identifier id = client.getNetworkHandler().getRegistryManager().get(Registry.BIOME_KEY).getId(biome); + client.player.sendMessage(new LiteralText(id.toString()), false); + }); + } + }); + } +} diff --git a/fabric-tag-extensions-v0/src/testmod/resources/data/fabric-tag-extensions-v0-testmod/tags/biomes/factory_test.json b/fabric-tag-extensions-v0/src/testmod/resources/data/fabric-tag-extensions-v0-testmod/tags/biomes/factory_test.json new file mode 100644 index 000000000..f6fea9b4f --- /dev/null +++ b/fabric-tag-extensions-v0/src/testmod/resources/data/fabric-tag-extensions-v0-testmod/tags/biomes/factory_test.json @@ -0,0 +1,8 @@ +{ + "replace": false, + "values": [ + "minecraft:plains", + "minecraft:desert", + "fabric-tag-extensions-v0-testmod:test" + ] +} diff --git a/fabric-tag-extensions-v0/src/testmod/resources/data/fabric-tag-extensions-v0-testmod/tags/biomes/json_only_test.json b/fabric-tag-extensions-v0/src/testmod/resources/data/fabric-tag-extensions-v0-testmod/tags/biomes/json_only_test.json new file mode 100644 index 000000000..06552ac6a --- /dev/null +++ b/fabric-tag-extensions-v0/src/testmod/resources/data/fabric-tag-extensions-v0-testmod/tags/biomes/json_only_test.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "values": [ + "minecraft:forest", + "minecraft:taiga" + ] +} diff --git a/fabric-tag-extensions-v0/src/testmod/resources/data/fabric-tag-extensions-v0-testmod/worldgen/biome/test.json b/fabric-tag-extensions-v0/src/testmod/resources/data/fabric-tag-extensions-v0-testmod/worldgen/biome/test.json new file mode 100644 index 000000000..c3aefe3a1 --- /dev/null +++ b/fabric-tag-extensions-v0/src/testmod/resources/data/fabric-tag-extensions-v0-testmod/worldgen/biome/test.json @@ -0,0 +1,20 @@ +{ + "surface_builder": "minecraft:grass", + "depth": 0.125, + "scale": 0.05, + "temperature": 0.8, + "downfall": 0.4, + "precipitation": "rain", + "category": "plains", + "effects": { + "sky_color": 7907327, + "fog_color": 12638463, + "water_color": 4159204, + "water_fog_color": 329011 + }, + "starts": [], + "spawners": {}, + "spawn_costs": {}, + "carvers": {}, + "features": [] +} diff --git a/fabric-tag-extensions-v0/src/testmod/resources/fabric.mod.json b/fabric-tag-extensions-v0/src/testmod/resources/fabric.mod.json new file mode 100644 index 000000000..4c94b66df --- /dev/null +++ b/fabric-tag-extensions-v0/src/testmod/resources/fabric.mod.json @@ -0,0 +1,19 @@ +{ + "schemaVersion": 1, + "id": "fabric-tag-extensions-v0-testmod", + "name": "Fabric Tag Extensions (v0) Test Mod", + "version": "1.0.0", + "environment": "*", + "license": "Apache-2.0", + "depends": { + "fabric-tag-extensions-v0": "*" + }, + "entrypoints": { + "main": [ + "net.fabricmc.fabric.test.tag.extension.TagExtensionTest" + ], + "client": [ + "net.fabricmc.fabric.test.tag.extension.TagExtensionTestClient" + ] + } +}