From a730659c14befbeaf0b8807ae542f4cb56e815ff Mon Sep 17 00:00:00 2001
From: Juuz <6596629+Juuxel@users.noreply.github.com>
Date: Thu, 12 Dec 2024 18:16:55 +0200
Subject: [PATCH] Add tag aliases (#4198)
* Add tag aliases
* Document and rename tag alias internals
* Make the tag alias directory singular to match Mojang's recent style
* Add a note about tag aliases to client tag documentation
* Support missing tags in alias groups
* Support tag aliases for dynamic and reloadable registries
* TagAliasGroup: Document naming conventions for c tag alias groups
* Add tag alias test mod
* Fix inline return checkstyle
* Add test for tag alias data generation
* Fix checkstyle (again)
* Add tag translations to tag API testmod
* Uncomment accidentally commented out code
* SimpleRegistryMixin: Improve a log message
* TagAliasTest: Improve assertion messages
* Fix tag aliases for dynamic registries not applying on /reload
* Clean up log message once again
* Address review feedback
* Make missing interfaces throw CCEs
* Add README
* Move TagAliasGroup into the impl package
---
.../fabric/api/tag/client/v1/ClientTags.java | 3 +
fabric-data-generation-api-v1/build.gradle | 1 +
.../v1/provider/FabricTagProvider.java | 85 +++++++++
.../impl/datagen/TagAliasGenerator.java | 49 +++++
.../mixin/datagen/TagProviderMixin.java | 51 ++++-
.../src/main/resources/fabric.mod.json | 3 +-
.../fabric/tag_aliases/block/flowers.json | 6 +
.../fabric/tag_aliases/block/flowers.json | 6 +
.../datagen/DataGeneratorTestEntrypoint.java | 5 +
fabric-tag-api-v1/README.md | 17 ++
fabric-tag-api-v1/build.gradle | 14 ++
.../fabric/api/tag/v1/package-info.java | 52 +++++
.../impl/tag/SimpleRegistryExtension.java | 22 +++
.../tag/TagAliasEnabledRegistryWrapper.java | 30 +++
.../fabric/impl/tag/TagAliasGroup.java | 48 +++++
.../fabric/impl/tag/TagAliasLoader.java | 177 ++++++++++++++++++
.../net/fabricmc/fabric/impl/tag/TagInit.java | 29 +++
.../mixin/tag/DataPackContentsMixin.java | 60 ++++++
.../mixin/tag/SimpleRegistry2Mixin.java | 44 +++++
.../mixin/tag/SimpleRegistry3Mixin.java | 47 +++++
.../fabric/mixin/tag/SimpleRegistryMixin.java | 124 ++++++++++++
.../tag/SimpleRegistryTagLookup2Accessor.java | 36 ++++
.../assets/fabric-tag-api-v1/icon.png | Bin 0 -> 1579 bytes
.../resources/fabric-tag-api-v1.accesswidener | 3 +
.../resources/fabric-tag-api-v1.mixins.json | 15 ++
.../src/main/resources/fabric.mod.json | 36 ++++
.../fabric/test/tag/TagAliasTest.java | 148 +++++++++++++++
.../fabric-tag-api-v1-testmod/lang/en_us.json | 15 ++
.../fabric/tag_alias/block/bricks_ab.json | 6 +
.../fabric/tag_alias/block/bricks_bc.json | 6 +
.../fabric/tag_alias/item/beetroots.json | 6 +
.../fabric/tag_alias/item/gems.json | 6 +
.../fabric/tag_alias/item/redstone_dusts.json | 6 +
.../tag_alias/loot_table/nether_bricks.json | 6 +
.../tag_alias/worldgen/biome/classic.json | 6 +
.../tags/block/brick_blocks.json | 6 +
.../tags/block/bricks.json | 7 +
.../tags/block/more_brick_blocks.json | 6 +
.../tags/item/beetroots.json | 6 +
.../tags/item/expensive_rocks.json | 6 +
.../tags/item/gems.json | 6 +
.../tags/item/redstone_dusts.json | 6 +
.../tags/item/redstone_powders.json | 5 +
.../tags/loot_table/nether_bricks_1.json | 6 +
.../tags/loot_table/nether_bricks_2.json | 6 +
.../tags/worldgen/biome/classic.json | 6 +
.../tags/worldgen/biome/traditional.json | 6 +
.../src/testmod/resources/fabric.mod.json | 16 ++
gradle.properties | 1 +
settings.gradle | 1 +
50 files changed, 1256 insertions(+), 2 deletions(-)
create mode 100644 fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/impl/datagen/TagAliasGenerator.java
create mode 100644 fabric-data-generation-api-v1/src/testmod/generated/data/fabric-data-gen-api-v1-testmod/fabric/tag_aliases/block/flowers.json
create mode 100644 fabric-data-generation-api-v1/src/testmod/generated/data/other_namespace/fabric/tag_aliases/block/flowers.json
create mode 100644 fabric-tag-api-v1/README.md
create mode 100644 fabric-tag-api-v1/build.gradle
create mode 100644 fabric-tag-api-v1/src/main/java/net/fabricmc/fabric/api/tag/v1/package-info.java
create mode 100644 fabric-tag-api-v1/src/main/java/net/fabricmc/fabric/impl/tag/SimpleRegistryExtension.java
create mode 100644 fabric-tag-api-v1/src/main/java/net/fabricmc/fabric/impl/tag/TagAliasEnabledRegistryWrapper.java
create mode 100644 fabric-tag-api-v1/src/main/java/net/fabricmc/fabric/impl/tag/TagAliasGroup.java
create mode 100644 fabric-tag-api-v1/src/main/java/net/fabricmc/fabric/impl/tag/TagAliasLoader.java
create mode 100644 fabric-tag-api-v1/src/main/java/net/fabricmc/fabric/impl/tag/TagInit.java
create mode 100644 fabric-tag-api-v1/src/main/java/net/fabricmc/fabric/mixin/tag/DataPackContentsMixin.java
create mode 100644 fabric-tag-api-v1/src/main/java/net/fabricmc/fabric/mixin/tag/SimpleRegistry2Mixin.java
create mode 100644 fabric-tag-api-v1/src/main/java/net/fabricmc/fabric/mixin/tag/SimpleRegistry3Mixin.java
create mode 100644 fabric-tag-api-v1/src/main/java/net/fabricmc/fabric/mixin/tag/SimpleRegistryMixin.java
create mode 100644 fabric-tag-api-v1/src/main/java/net/fabricmc/fabric/mixin/tag/SimpleRegistryTagLookup2Accessor.java
create mode 100644 fabric-tag-api-v1/src/main/resources/assets/fabric-tag-api-v1/icon.png
create mode 100644 fabric-tag-api-v1/src/main/resources/fabric-tag-api-v1.accesswidener
create mode 100644 fabric-tag-api-v1/src/main/resources/fabric-tag-api-v1.mixins.json
create mode 100644 fabric-tag-api-v1/src/main/resources/fabric.mod.json
create mode 100644 fabric-tag-api-v1/src/testmod/java/net/fabricmc/fabric/test/tag/TagAliasTest.java
create mode 100644 fabric-tag-api-v1/src/testmod/resources/assets/fabric-tag-api-v1-testmod/lang/en_us.json
create mode 100644 fabric-tag-api-v1/src/testmod/resources/data/fabric-tag-api-v1-testmod/fabric/tag_alias/block/bricks_ab.json
create mode 100644 fabric-tag-api-v1/src/testmod/resources/data/fabric-tag-api-v1-testmod/fabric/tag_alias/block/bricks_bc.json
create mode 100644 fabric-tag-api-v1/src/testmod/resources/data/fabric-tag-api-v1-testmod/fabric/tag_alias/item/beetroots.json
create mode 100644 fabric-tag-api-v1/src/testmod/resources/data/fabric-tag-api-v1-testmod/fabric/tag_alias/item/gems.json
create mode 100644 fabric-tag-api-v1/src/testmod/resources/data/fabric-tag-api-v1-testmod/fabric/tag_alias/item/redstone_dusts.json
create mode 100644 fabric-tag-api-v1/src/testmod/resources/data/fabric-tag-api-v1-testmod/fabric/tag_alias/loot_table/nether_bricks.json
create mode 100644 fabric-tag-api-v1/src/testmod/resources/data/fabric-tag-api-v1-testmod/fabric/tag_alias/worldgen/biome/classic.json
create mode 100644 fabric-tag-api-v1/src/testmod/resources/data/fabric-tag-api-v1-testmod/tags/block/brick_blocks.json
create mode 100644 fabric-tag-api-v1/src/testmod/resources/data/fabric-tag-api-v1-testmod/tags/block/bricks.json
create mode 100644 fabric-tag-api-v1/src/testmod/resources/data/fabric-tag-api-v1-testmod/tags/block/more_brick_blocks.json
create mode 100644 fabric-tag-api-v1/src/testmod/resources/data/fabric-tag-api-v1-testmod/tags/item/beetroots.json
create mode 100644 fabric-tag-api-v1/src/testmod/resources/data/fabric-tag-api-v1-testmod/tags/item/expensive_rocks.json
create mode 100644 fabric-tag-api-v1/src/testmod/resources/data/fabric-tag-api-v1-testmod/tags/item/gems.json
create mode 100644 fabric-tag-api-v1/src/testmod/resources/data/fabric-tag-api-v1-testmod/tags/item/redstone_dusts.json
create mode 100644 fabric-tag-api-v1/src/testmod/resources/data/fabric-tag-api-v1-testmod/tags/item/redstone_powders.json
create mode 100644 fabric-tag-api-v1/src/testmod/resources/data/fabric-tag-api-v1-testmod/tags/loot_table/nether_bricks_1.json
create mode 100644 fabric-tag-api-v1/src/testmod/resources/data/fabric-tag-api-v1-testmod/tags/loot_table/nether_bricks_2.json
create mode 100644 fabric-tag-api-v1/src/testmod/resources/data/fabric-tag-api-v1-testmod/tags/worldgen/biome/classic.json
create mode 100644 fabric-tag-api-v1/src/testmod/resources/data/fabric-tag-api-v1-testmod/tags/worldgen/biome/traditional.json
create mode 100644 fabric-tag-api-v1/src/testmod/resources/fabric.mod.json
diff --git a/fabric-client-tags-api-v1/src/client/java/net/fabricmc/fabric/api/tag/client/v1/ClientTags.java b/fabric-client-tags-api-v1/src/client/java/net/fabricmc/fabric/api/tag/client/v1/ClientTags.java
index f4186746ad..23407c94fc 100644
--- a/fabric-client-tags-api-v1/src/client/java/net/fabricmc/fabric/api/tag/client/v1/ClientTags.java
+++ b/fabric-client-tags-api-v1/src/client/java/net/fabricmc/fabric/api/tag/client/v1/ClientTags.java
@@ -37,6 +37,9 @@
*
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 60560e7edd..07d4bf621d 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 cac205fc07..de31526fc3 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 3ae2955117..2a1fcd0074 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.server.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 8f1a7a6ed0..cff1a04de7 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.8"
+ "fabricloader": ">=0.16.8",
+ "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 86af5fd0e2..488cdd679e 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
@@ -332,6 +332,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