Client Tags resolve that issue by lazily reading the tag json files within the mods on the side of the caller,
* directly, allowing for mods to query tags such as {@link net.fabricmc.fabric.api.tag.convention.v2.ConventionalBlockTags}
* even when connected to a vanilla server.
+ *
+ *
Note that locally read client tags don't currently support Fabric's tag aliases. The aliasing system is only
+ * implemented on servers.
*/
public final class ClientTags {
private ClientTags() {
diff --git a/fabric-data-generation-api-v1/build.gradle b/fabric-data-generation-api-v1/build.gradle
index 7cf9d443bf..83b2c7945e 100644
--- a/fabric-data-generation-api-v1/build.gradle
+++ b/fabric-data-generation-api-v1/build.gradle
@@ -7,6 +7,7 @@ moduleDependencies(project, [
'fabric-networking-api-v1',
'fabric-resource-conditions-api-v1',
'fabric-item-group-api-v1',
+ 'fabric-tag-api-v1',
'fabric-recipe-api-v1'
])
diff --git a/fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/api/datagen/v1/provider/FabricTagProvider.java b/fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/api/datagen/v1/provider/FabricTagProvider.java
index 0207f5089a..e18cac43e5 100644
--- a/fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/api/datagen/v1/provider/FabricTagProvider.java
+++ b/fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/api/datagen/v1/provider/FabricTagProvider.java
@@ -16,6 +16,11 @@
package net.fabricmc.fabric.api.datagen.v1.provider;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@@ -70,6 +75,9 @@
* @see EntityTypeTagProvider
*/
public abstract class FabricTagProvider extends TagProvider {
+ private final FabricDataOutput output;
+ private final Map aliasGroupBuilders = new HashMap<>();
+
/**
* Constructs a new {@link FabricTagProvider} with the default computed path.
*
@@ -80,6 +88,7 @@ public abstract class FabricTagProvider extends TagProvider {
*/
public FabricTagProvider(FabricDataOutput output, RegistryKey extends Registry> registryKey, CompletableFuture registriesFuture) {
super(output, registryKey, registriesFuture);
+ this.output = output;
}
/**
@@ -116,6 +125,34 @@ protected FabricTagBuilder getOrCreateTagBuilder(TagKey tag) {
return new FabricTagBuilder(super.getOrCreateTagBuilder(tag));
}
+ /**
+ * Gets an {@link AliasGroupBuilder} with the given ID.
+ *
+ * @param groupId the group ID
+ * @return the alias group builder
+ */
+ protected AliasGroupBuilder aliasGroup(Identifier groupId) {
+ return aliasGroupBuilders.computeIfAbsent(groupId, key -> new AliasGroupBuilder());
+ }
+
+ /**
+ * Gets an {@link AliasGroupBuilder} with the given ID.
+ *
+ * @param group the group name
+ * @return the alias group builder
+ */
+ protected AliasGroupBuilder aliasGroup(String group) {
+ Identifier groupId = Identifier.of(output.getModId(), group);
+ return aliasGroupBuilders.computeIfAbsent(groupId, key -> new AliasGroupBuilder());
+ }
+
+ /**
+ * {@return a read-only map of alias group builders by the alias group ID}.
+ */
+ public Map getAliasGroupBuilders() {
+ return Collections.unmodifiableMap(aliasGroupBuilders);
+ }
+
/**
* Extend this class to create {@link Block} tags in the "/blocks" tag directory.
*/
@@ -396,4 +433,52 @@ public final FabricTagBuilder add(RegistryKey... registryKeys) {
return this;
}
}
+
+ /**
+ * A builder for tag alias groups.
+ */
+ public final class AliasGroupBuilder {
+ private final List> tags = new ArrayList<>();
+
+ private AliasGroupBuilder() {
+ }
+
+ /**
+ * {@return a read-only list of the tags in this alias group}.
+ */
+ public List> getTags() {
+ return Collections.unmodifiableList(tags);
+ }
+
+ public AliasGroupBuilder add(TagKey tag) {
+ if (tag.registryRef() != registryRef) {
+ throw new IllegalArgumentException("Tag " + tag + " isn't from the registry " + registryRef);
+ }
+
+ this.tags.add(tag);
+ return this;
+ }
+
+ @SafeVarargs
+ public final AliasGroupBuilder add(TagKey... tags) {
+ for (TagKey tag : tags) {
+ add(tag);
+ }
+
+ return this;
+ }
+
+ public AliasGroupBuilder add(Identifier tag) {
+ this.tags.add(TagKey.of(registryRef, tag));
+ return this;
+ }
+
+ public AliasGroupBuilder add(Identifier... tags) {
+ for (Identifier tag : tags) {
+ this.tags.add(TagKey.of(registryRef, tag));
+ }
+
+ return this;
+ }
+ }
}
diff --git a/fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/impl/datagen/TagAliasGenerator.java b/fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/impl/datagen/TagAliasGenerator.java
new file mode 100644
index 0000000000..9e818b5773
--- /dev/null
+++ b/fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/impl/datagen/TagAliasGenerator.java
@@ -0,0 +1,49 @@
+/*
+ * 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.datagen;
+
+import java.nio.file.Path;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+import net.minecraft.data.DataOutput;
+import net.minecraft.data.DataProvider;
+import net.minecraft.data.DataWriter;
+import net.minecraft.registry.Registry;
+import net.minecraft.registry.RegistryKey;
+import net.minecraft.registry.tag.TagKey;
+import net.minecraft.util.Identifier;
+
+import net.fabricmc.fabric.impl.tag.TagAliasGroup;
+
+public final class TagAliasGenerator {
+ public static String getDirectory(RegistryKey extends Registry>> registryKey) {
+ String directory = "fabric/tag_aliases/";
+ Identifier registryId = registryKey.getValue();
+
+ if (!Identifier.DEFAULT_NAMESPACE.equals(registryId.getNamespace())) {
+ directory += registryId.getNamespace() + '/';
+ }
+
+ return directory + registryId.getPath();
+ }
+
+ public static CompletableFuture> writeTagAlias(DataWriter writer, DataOutput.PathResolver pathResolver, RegistryKey extends Registry> registryRef, Identifier groupId, List> tags) {
+ Path path = pathResolver.resolveJson(groupId);
+ return DataProvider.writeCodecToPath(writer, TagAliasGroup.codec(registryRef), new TagAliasGroup<>(tags), path);
+ }
+}
diff --git a/fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/mixin/datagen/TagProviderMixin.java b/fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/mixin/datagen/TagProviderMixin.java
index 9bd3b10376..4684ff6262 100644
--- a/fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/mixin/datagen/TagProviderMixin.java
+++ b/fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/mixin/datagen/TagProviderMixin.java
@@ -16,18 +16,48 @@
package net.fabricmc.fabric.mixin.datagen;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+
+import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
+import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.llamalad7.mixinextras.sugar.Local;
+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.ModifyArg;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import net.minecraft.data.DataOutput;
+import net.minecraft.data.DataWriter;
import net.minecraft.data.tag.TagProvider;
+import net.minecraft.registry.Registry;
+import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.tag.TagBuilder;
+import net.minecraft.util.Identifier;
+import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider;
import net.fabricmc.fabric.impl.datagen.FabricTagBuilder;
+import net.fabricmc.fabric.impl.datagen.TagAliasGenerator;
@Mixin(TagProvider.class)
-public class TagProviderMixin {
+public class TagProviderMixin {
+ @Shadow
+ @Final
+ protected RegistryKey extends Registry> registryRef;
+
+ @Unique
+ private DataOutput.PathResolver tagAliasPathResolver;
+
+ @Inject(method = "(Lnet/minecraft/data/DataOutput;Lnet/minecraft/registry/RegistryKey;Ljava/util/concurrent/CompletableFuture;Ljava/util/concurrent/CompletableFuture;)V", at = @At("RETURN"))
+ private void initPathResolver(DataOutput output, RegistryKey extends Registry> registryRef, CompletableFuture> registriesFuture, CompletableFuture> parentTagLookupFuture, CallbackInfo info) {
+ tagAliasPathResolver = output.getResolver(DataOutput.OutputType.DATA_PACK, TagAliasGenerator.getDirectory(registryRef));
+ }
+
@ModifyArg(method = "method_27046", at = @At(value = "INVOKE", target = "Lnet/minecraft/registry/tag/TagFile;(Ljava/util/List;Z)V"), index = 1)
private boolean addReplaced(boolean replaced, @Local TagBuilder tagBuilder) {
if (tagBuilder instanceof FabricTagBuilder fabricTagBuilder) {
@@ -36,4 +66,23 @@ private boolean addReplaced(boolean replaced, @Local TagBuilder tagBuilder) {
return replaced;
}
+
+ @SuppressWarnings("unchecked")
+ @WrapOperation(method = "method_49659", at = @At(value = "INVOKE", target = "Ljava/util/concurrent/CompletableFuture;allOf([Ljava/util/concurrent/CompletableFuture;)Ljava/util/concurrent/CompletableFuture;"))
+ private CompletableFuture addTagAliasGroupBuilders(CompletableFuture>[] futures, Operation> original, @Local(argsOnly = true) DataWriter writer) {
+ if ((Object) this instanceof FabricTagProvider>) {
+ // Note: no pattern matching instanceof so that we can cast directly to FabricTagProvider instead of a wildcard
+ Map.AliasGroupBuilder> builders = ((FabricTagProvider) (Object) this).getAliasGroupBuilders();
+ CompletableFuture>[] newFutures = Arrays.copyOf(futures, futures.length + builders.size());
+ int index = futures.length;
+
+ for (Map.Entry.AliasGroupBuilder> entry : builders.entrySet()) {
+ newFutures[index++] = TagAliasGenerator.writeTagAlias(writer, tagAliasPathResolver, registryRef, entry.getKey(), entry.getValue().getTags());
+ }
+
+ return original.call((Object) newFutures);
+ } else {
+ return original.call((Object) futures);
+ }
+ }
}
diff --git a/fabric-data-generation-api-v1/src/main/resources/fabric.mod.json b/fabric-data-generation-api-v1/src/main/resources/fabric.mod.json
index f88ed89256..008c45e30e 100644
--- a/fabric-data-generation-api-v1/src/main/resources/fabric.mod.json
+++ b/fabric-data-generation-api-v1/src/main/resources/fabric.mod.json
@@ -16,7 +16,8 @@
"FabricMC"
],
"depends": {
- "fabricloader": ">=0.16.9"
+ "fabricloader": ">=0.16.9",
+ "fabric-tag-api-v1": "*"
},
"description": "Allows for automatic data generation.",
"mixins": [
diff --git a/fabric-data-generation-api-v1/src/testmod/generated/data/fabric-data-gen-api-v1-testmod/fabric/tag_aliases/block/flowers.json b/fabric-data-generation-api-v1/src/testmod/generated/data/fabric-data-gen-api-v1-testmod/fabric/tag_aliases/block/flowers.json
new file mode 100644
index 0000000000..69f4ed3098
--- /dev/null
+++ b/fabric-data-generation-api-v1/src/testmod/generated/data/fabric-data-gen-api-v1-testmod/fabric/tag_aliases/block/flowers.json
@@ -0,0 +1,6 @@
+{
+ "tags": [
+ "minecraft:flowers",
+ "minecraft:flower_pots"
+ ]
+}
\ No newline at end of file
diff --git a/fabric-data-generation-api-v1/src/testmod/generated/data/other_namespace/fabric/tag_aliases/block/flowers.json b/fabric-data-generation-api-v1/src/testmod/generated/data/other_namespace/fabric/tag_aliases/block/flowers.json
new file mode 100644
index 0000000000..69f4ed3098
--- /dev/null
+++ b/fabric-data-generation-api-v1/src/testmod/generated/data/other_namespace/fabric/tag_aliases/block/flowers.json
@@ -0,0 +1,6 @@
+{
+ "tags": [
+ "minecraft:flowers",
+ "minecraft:flower_pots"
+ ]
+}
\ No newline at end of file
diff --git a/fabric-data-generation-api-v1/src/testmod/java/net/fabricmc/fabric/test/datagen/DataGeneratorTestEntrypoint.java b/fabric-data-generation-api-v1/src/testmod/java/net/fabricmc/fabric/test/datagen/DataGeneratorTestEntrypoint.java
index 438542e972..fc852df56b 100644
--- a/fabric-data-generation-api-v1/src/testmod/java/net/fabricmc/fabric/test/datagen/DataGeneratorTestEntrypoint.java
+++ b/fabric-data-generation-api-v1/src/testmod/java/net/fabricmc/fabric/test/datagen/DataGeneratorTestEntrypoint.java
@@ -306,6 +306,11 @@ protected void configure(RegistryWrapper.WrapperLookup registries) {
getOrCreateTagBuilder(BlockTags.FIRE).setReplace(true).add(SIMPLE_BLOCK);
getOrCreateTagBuilder(BlockTags.DIRT).add(SIMPLE_BLOCK);
getOrCreateTagBuilder(BlockTags.ACACIA_LOGS).forceAddTag(BlockTags.ANIMALS_SPAWNABLE_ON);
+
+ aliasGroup("flowers")
+ .add(BlockTags.FLOWERS, BlockTags.FLOWER_POTS);
+ aliasGroup(Identifier.of("other_namespace", "flowers"))
+ .add(BlockTags.FLOWERS, BlockTags.FLOWER_POTS);
}
}
diff --git a/fabric-tag-api-v1/README.md b/fabric-tag-api-v1/README.md
new file mode 100644
index 0000000000..b2b4c3dfa2
--- /dev/null
+++ b/fabric-tag-api-v1/README.md
@@ -0,0 +1,17 @@
+# Fabric Tag API (v1)
+
+This module contains APIs for working with data pack tags.
+
+## Tag aliases
+
+*Tag alias groups* merge tags that refer to the same set of registry entries.
+The contained tags will be linked together and get the combined set of entries
+of all the aliased tags in a group.
+
+Tag alias groups can be defined in data packs in the `data//fabric/tag_alias/`
+directory. `` is the path of the registry's ID, prefixed with `/` if it's
+not `minecraft`.
+
+The JSON format of tag alias groups is an object with a `tags` list containing plain tag IDs.
+
+See the module javadoc for more information about tag aliases.
diff --git a/fabric-tag-api-v1/build.gradle b/fabric-tag-api-v1/build.gradle
new file mode 100644
index 0000000000..35d162fff0
--- /dev/null
+++ b/fabric-tag-api-v1/build.gradle
@@ -0,0 +1,14 @@
+version = getSubprojectVersion(project)
+
+loom {
+ accessWidenerPath = file('src/main/resources/fabric-tag-api-v1.accesswidener')
+}
+
+moduleDependencies(project, [
+ 'fabric-api-base',
+ 'fabric-resource-loader-v0'
+])
+
+testDependencies(project, [
+ ':fabric-lifecycle-events-v1',
+])
diff --git a/fabric-tag-api-v1/src/main/java/net/fabricmc/fabric/api/tag/v1/package-info.java b/fabric-tag-api-v1/src/main/java/net/fabricmc/fabric/api/tag/v1/package-info.java
new file mode 100644
index 0000000000..1f5147f1d1
--- /dev/null
+++ b/fabric-tag-api-v1/src/main/java/net/fabricmc/fabric/api/tag/v1/package-info.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+/**
+ * The Fabric Tag API for working with {@linkplain net.minecraft.registry.tag.TagKey tags}.
+ *
+ *
Aliasing tags
+ * Tag alias groups are lists of tags that refer to the same set of registry entries.
+ * The contained tags will be linked together and get the combined set of entries
+ * of all the aliased tags in a group.
+ *
+ *
Tag alias groups can be defined in data packs in the {@code data//fabric/tag_alias/}
+ * directory. {@code } is the path of the registry's ID, prefixed with {@code /} if it's
+ * not {@value net.minecraft.util.Identifier#DEFAULT_NAMESPACE}. For example, an alias group for block tags would be placed
+ * in {@code data//fabric/tag_alias/block/}.
+ *
+ *
The JSON format of tag alias groups is an object with a {@code tags} list. The list contains plain tag IDs with
+ * no {@code #} prefix.
+ *
+ *
If multiple tag alias groups include a tag, the groups will be combined and each tag will be an alias
+ * for the same contents.
+ *
+ *
Tag aliases in the {@code c} namespace
+ *
+ *
For the names of shared {@code c} tag alias groups, it's important that you use a short and descriptive name.
+ * A good way to do this is reusing the name of a contained {@code c} tag that follows the naming conventions.
+ * For example, if the tag alias group contains the tags {@code c:flowers/tall} and {@code minecraft:tall_flowers},
+ * the tag alias file should be named {@code flowers/tall.json}, like the contained {@code c} tag.
+ *
+ *
Tag alias groups in the {@code c} namespace are primarily intended for merging a {@code c} tag
+ * with an equivalent vanilla tag with no potentially unwanted gameplay behavior. If a vanilla tag affects
+ * game mechanics (such as the water tag affecting swimming), don't alias it as a {@code c} tag.
+ *
+ *
If you want to have the contents of a {@code c} tag in your own tag, prefer including the {@code c} tag
+ * in your tag file directly. That way, data packs can modify your tag separately. Tag aliases make their contained
+ * tags almost fully indistinguishable since they get the exact same content, and you have to override the alias group
+ * in a higher-priority data pack to unlink them.
+ */
+package net.fabricmc.fabric.api.tag.v1;
diff --git a/fabric-tag-api-v1/src/main/java/net/fabricmc/fabric/impl/tag/SimpleRegistryExtension.java b/fabric-tag-api-v1/src/main/java/net/fabricmc/fabric/impl/tag/SimpleRegistryExtension.java
new file mode 100644
index 0000000000..815a21a01d
--- /dev/null
+++ b/fabric-tag-api-v1/src/main/java/net/fabricmc/fabric/impl/tag/SimpleRegistryExtension.java
@@ -0,0 +1,22 @@
+/*
+ * 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;
+
+public interface SimpleRegistryExtension {
+ void fabric_applyPendingTagAliases();
+ void fabric_refreshTags();
+}
diff --git a/fabric-tag-api-v1/src/main/java/net/fabricmc/fabric/impl/tag/TagAliasEnabledRegistryWrapper.java b/fabric-tag-api-v1/src/main/java/net/fabricmc/fabric/impl/tag/TagAliasEnabledRegistryWrapper.java
new file mode 100644
index 0000000000..ee41ea66f7
--- /dev/null
+++ b/fabric-tag-api-v1/src/main/java/net/fabricmc/fabric/impl/tag/TagAliasEnabledRegistryWrapper.java
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+import java.util.Map;
+import java.util.Set;
+
+import net.minecraft.registry.tag.TagKey;
+
+/**
+ * Implemented on {@code RegistryWrapper.Impl} instances used during data loading
+ * to give access to the underlying registry.
+ */
+public interface TagAliasEnabledRegistryWrapper {
+ void fabric_loadTagAliases(Map, Set>> aliasGroups);
+}
diff --git a/fabric-tag-api-v1/src/main/java/net/fabricmc/fabric/impl/tag/TagAliasGroup.java b/fabric-tag-api-v1/src/main/java/net/fabricmc/fabric/impl/tag/TagAliasGroup.java
new file mode 100644
index 0000000000..494e7f779e
--- /dev/null
+++ b/fabric-tag-api-v1/src/main/java/net/fabricmc/fabric/impl/tag/TagAliasGroup.java
@@ -0,0 +1,48 @@
+/*
+ * 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;
+
+import java.util.List;
+
+import com.mojang.serialization.Codec;
+
+import net.minecraft.registry.Registry;
+import net.minecraft.registry.RegistryKey;
+import net.minecraft.registry.tag.TagKey;
+
+/**
+ * A wrapper record for tag alias groups.
+ *
+ * @param tags the tags in the group, must be from the same registry
+ * @param the type of registry entries in the tags
+ */
+public record TagAliasGroup(List> tags) {
+ /**
+ * Creates a codec for tag alias groups in the specified registry.
+ *
+ * @param registryKey the key of the registry where the tags are from
+ * @param the entry type
+ * @return the codec
+ */
+ public static Codec> codec(RegistryKey extends Registry> registryKey) {
+ return TagKey.unprefixedCodec(registryKey)
+ .listOf()
+ .fieldOf("tags")
+ .xmap(TagAliasGroup::new, TagAliasGroup::tags)
+ .codec();
+ }
+}
diff --git a/fabric-tag-api-v1/src/main/java/net/fabricmc/fabric/impl/tag/TagAliasLoader.java b/fabric-tag-api-v1/src/main/java/net/fabricmc/fabric/impl/tag/TagAliasLoader.java
new file mode 100644
index 0000000000..d26e8860c2
--- /dev/null
+++ b/fabric-tag-api-v1/src/main/java/net/fabricmc/fabric/impl/tag/TagAliasLoader.java
@@ -0,0 +1,177 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonParser;
+import com.mojang.serialization.Codec;
+import com.mojang.serialization.DataResult;
+import com.mojang.serialization.JsonOps;
+import com.mojang.serialization.Lifecycle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.minecraft.registry.CombinedDynamicRegistries;
+import net.minecraft.registry.DynamicRegistryManager;
+import net.minecraft.registry.Registry;
+import net.minecraft.registry.RegistryKey;
+import net.minecraft.registry.RegistryWrapper;
+import net.minecraft.registry.tag.TagKey;
+import net.minecraft.resource.Resource;
+import net.minecraft.resource.ResourceFinder;
+import net.minecraft.resource.ResourceManager;
+import net.minecraft.resource.SinglePreparationResourceReloader;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.profiler.Profiler;
+
+import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener;
+
+public final class TagAliasLoader extends SinglePreparationResourceReloader