This commit is contained in:
modmuss50 2024-02-14 19:17:38 +00:00
parent 44ed7ba466
commit 548b1e1b9e
15 changed files with 97 additions and 72 deletions

View file

@ -16,12 +16,33 @@
package net.fabricmc.fabric.test.biome;
import net.minecraft.registry.Registerable;
import net.minecraft.registry.RegistryBuilder;
import net.minecraft.registry.RegistryEntryLookup;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.util.Identifier;
import net.minecraft.world.gen.feature.ConfiguredFeature;
import net.minecraft.world.gen.feature.ConfiguredFeatures;
import net.minecraft.world.gen.feature.Feature;
import net.minecraft.world.gen.feature.PlacedFeature;
import net.minecraft.world.gen.feature.PlacedFeatures;
import net.minecraft.world.gen.placementmodifier.BiomePlacementModifier;
import net.minecraft.world.gen.placementmodifier.SquarePlacementModifier;
import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
public class DataGeneratorEntrypoint implements net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint {
public static final RegistryKey<ConfiguredFeature<?, ?>> COMMON_DESERT_WELL = RegistryKey.of(
RegistryKeys.CONFIGURED_FEATURE,
new Identifier(FabricBiomeTest.MOD_ID, "fab_desert_well")
);
public static final RegistryKey<PlacedFeature> PLACED_COMMON_DESERT_WELL = RegistryKey.of(
RegistryKeys.PLACED_FEATURE,
new Identifier(FabricBiomeTest.MOD_ID, "fab_desert_well")
);
@Override
public void onInitializeDataGenerator(FabricDataGenerator dataGenerator) {
FabricDataGenerator.Pack pack = dataGenerator.createPack();
@ -31,6 +52,24 @@ public class DataGeneratorEntrypoint implements net.fabricmc.fabric.api.datagen.
@Override
public void buildRegistry(RegistryBuilder registryBuilder) {
registryBuilder.addRegistry(RegistryKeys.CONFIGURED_FEATURE, this::bootstrapConfiguredFeatures);
registryBuilder.addRegistry(RegistryKeys.PLACED_FEATURE, this::bootstrapPlacedFeatures);
registryBuilder.addRegistry(RegistryKeys.BIOME, TestBiomes::bootstrap);
}
private void bootstrapConfiguredFeatures(Registerable<ConfiguredFeature<?, ?>> registerable) {
ConfiguredFeatures.register(registerable, COMMON_DESERT_WELL, Feature.DESERT_WELL);
}
private void bootstrapPlacedFeatures(Registerable<PlacedFeature> registerable) {
RegistryEntryLookup<ConfiguredFeature<?, ?>> configuredFeatures = registerable.getRegistryLookup(RegistryKeys.CONFIGURED_FEATURE);
RegistryEntry<ConfiguredFeature<?, ?>> commonDesertWell = configuredFeatures.getOrThrow(COMMON_DESERT_WELL);
// The placement config is taken from the vanilla desert well, but no randomness
PlacedFeatures.register(registerable, PLACED_COMMON_DESERT_WELL, commonDesertWell,
SquarePlacementModifier.of(),
PlacedFeatures.MOTION_BLOCKING_HEIGHTMAP,
BiomePlacementModifier.of()
);
}
}

View file

@ -16,6 +16,8 @@
package net.fabricmc.fabric.test.biome;
import static net.fabricmc.fabric.test.biome.DataGeneratorEntrypoint.PLACED_COMMON_DESERT_WELL;
import com.google.common.base.Preconditions;
import net.minecraft.registry.RegistryKey;
@ -25,8 +27,6 @@ import net.minecraft.util.Identifier;
import net.minecraft.world.biome.BiomeKeys;
import net.minecraft.world.biome.source.util.MultiNoiseUtil;
import net.minecraft.world.gen.GenerationStep;
import net.minecraft.world.gen.feature.ConfiguredFeature;
import net.minecraft.world.gen.feature.PlacedFeature;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.biome.v1.BiomeModifications;
@ -48,15 +48,6 @@ import net.fabricmc.fabric.api.biome.v1.TheEndBiomes;
public class FabricBiomeTest implements ModInitializer {
public static final String MOD_ID = "fabric-biome-api-v1-testmod";
public static final RegistryKey<ConfiguredFeature<?, ?>> COMMON_DESERT_WELL = RegistryKey.of(
RegistryKeys.CONFIGURED_FEATURE,
new Identifier(FabricBiomeTest.MOD_ID, "fab_desert_well")
);
public static final RegistryKey<PlacedFeature> PLACED_COMMON_DESERT_WELL = RegistryKey.of(
RegistryKeys.PLACED_FEATURE,
new Identifier(FabricBiomeTest.MOD_ID, "fab_desert_well")
);
@Override
public void onInitialize() {
Preconditions.checkArgument(NetherBiomes.canGenerateInNether(BiomeKeys.NETHER_WASTES));

View file

@ -16,20 +16,10 @@
package net.fabricmc.fabric.test.biome;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.RegistryWrapper;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.feature.ConfiguredFeature;
import net.minecraft.world.gen.feature.DefaultFeatureConfig;
import net.minecraft.world.gen.feature.Feature;
import net.minecraft.world.gen.feature.PlacedFeature;
import net.minecraft.world.gen.feature.PlacedFeatures;
import net.minecraft.world.gen.placementmodifier.BiomePlacementModifier;
import net.minecraft.world.gen.placementmodifier.SquarePlacementModifier;
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricDynamicRegistryProvider;
@ -41,17 +31,9 @@ public class WorldgenProvider extends FabricDynamicRegistryProvider {
@Override
protected void configure(RegistryWrapper.WrapperLookup registries, Entries entries) {
final RegistryWrapper.Impl<Biome> biomeRegistry = registries.getWrapperOrThrow(RegistryKeys.BIOME);
entries.addAll(biomeRegistry);
ConfiguredFeature<?, ?> COMMON_DESERT_WELL = new ConfiguredFeature<>(Feature.DESERT_WELL, DefaultFeatureConfig.INSTANCE);
RegistryEntry<ConfiguredFeature<?, ?>> featureRef = entries.add(FabricBiomeTest.COMMON_DESERT_WELL, COMMON_DESERT_WELL);
// The placement config is taken from the vanilla desert well, but no randomness
PlacedFeature PLACED_COMMON_DESERT_WELL = new PlacedFeature(featureRef, List.of(SquarePlacementModifier.of(), PlacedFeatures.MOTION_BLOCKING_HEIGHTMAP, BiomePlacementModifier.of()));
entries.add(FabricBiomeTest.PLACED_COMMON_DESERT_WELL, PLACED_COMMON_DESERT_WELL);
entries.addAll(registries.getWrapperOrThrow(RegistryKeys.BIOME));
entries.add(registries.getWrapperOrThrow(RegistryKeys.CONFIGURED_FEATURE), DataGeneratorEntrypoint.COMMON_DESERT_WELL);
entries.add(registries.getWrapperOrThrow(RegistryKeys.PLACED_FEATURE), DataGeneratorEntrypoint.PLACED_COMMON_DESERT_WELL);
}
@Override

View file

@ -16,11 +16,11 @@
package net.fabricmc.fabric.mixin.content.registry;
import com.llamalad7.mixinextras.sugar.Local;
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 org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import net.minecraft.block.BlockState;
import net.minecraft.entity.ai.pathing.LandPathNodeMaker;
@ -36,8 +36,8 @@ public class LandPathNodeMakerMixin {
/**
* Overrides the node type for the specified position, if the position is a direct target in a path.
*/
@Inject(method = "getCommonNodeType", at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/world/BlockView;getBlockState(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/block/BlockState;"), locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true)
private static void getCommonNodeType(BlockView world, BlockPos pos, CallbackInfoReturnable<PathNodeType> cir, BlockState state) {
@Inject(method = "getCommonNodeType", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/BlockState;getBlock()Lnet/minecraft/block/Block;"), cancellable = true)
private static void getCommonNodeType(BlockView world, BlockPos pos, CallbackInfoReturnable<PathNodeType> cir, @Local BlockState state) {
PathNodeType nodeType = LandPathNodeTypesRegistry.getPathNodeType(state, world, pos, false);
if (nodeType != null) {
@ -48,9 +48,9 @@ public class LandPathNodeMakerMixin {
/**
* Overrides the node type for the specified position, if the position is found as neighbor block in a path.
*/
@Inject(method = "getNodeTypeFromNeighbors", at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/world/BlockView;getBlockState(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/block/BlockState;"), locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true)
private static void getNodeTypeFromNeighbors(BlockView world, BlockPos.Mutable pos, PathNodeType nodeType, CallbackInfoReturnable<PathNodeType> cir, int i, int j, int k, int l, int m, int n, BlockState state) {
PathNodeType neighborNodeType = LandPathNodeTypesRegistry.getPathNodeType(state, world, pos, true);
@Inject(method = "getNodeTypeFromNeighbors", at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/entity/ai/pathing/LandPathNodeMaker;getCommonNodeType(Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/entity/ai/pathing/PathNodeType;"), cancellable = true)
private static void getNodeTypeFromNeighbors(BlockView world, BlockPos.Mutable pos, PathNodeType nodeType, CallbackInfoReturnable<PathNodeType> cir) {
PathNodeType neighborNodeType = LandPathNodeTypesRegistry.getPathNodeType(world.getBlockState(pos), world, pos, true);
if (neighborNodeType != null) {
cir.setReturnValue(neighborNodeType);

View file

@ -50,7 +50,7 @@ public class AttachmentSerializingImpl {
Codec<Object> codec = (Codec<Object>) type.persistenceCodec();
if (codec != null) {
RegistryOps<NbtElement> registryOps = RegistryOps.of(NbtOps.INSTANCE, wrapperLookup);
RegistryOps<NbtElement> registryOps = wrapperLookup.method_57093(NbtOps.INSTANCE);
codec.encodeStart(registryOps, entry.getValue())
.get()
.ifRight(partial -> {
@ -81,7 +81,7 @@ public class AttachmentSerializingImpl {
Codec<?> codec = type.persistenceCodec();
if (codec != null) {
RegistryOps<NbtElement> registryOps = RegistryOps.of(NbtOps.INSTANCE, wrapperLookup);
RegistryOps<NbtElement> registryOps = wrapperLookup.method_57093(NbtOps.INSTANCE);
codec.parse(registryOps, compound.get(key))
.get()
.ifRight(partial -> {

View file

@ -24,9 +24,9 @@ import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import java.util.IdentityHashMap;
import java.util.Map;
@ -45,9 +45,13 @@ import net.minecraft.entity.EntityType;
import net.minecraft.entity.MarkerEntity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement;
import net.minecraft.nbt.NbtOps;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.registry.RegistryOps;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.chunk.ProtoChunk;
import net.minecraft.world.chunk.WorldChunk;
@ -134,11 +138,11 @@ public class CommonAttachmentTests {
map.put(dummy, 0.5d);
var fakeSave = new NbtCompound();
AttachmentSerializingImpl.serializeAttachmentData(fakeSave, null, map);
AttachmentSerializingImpl.serializeAttachmentData(fakeSave, mockDRM(), map);
assertTrue(fakeSave.contains(AttachmentTarget.NBT_ATTACHMENT_KEY, NbtElement.COMPOUND_TYPE));
assertTrue(fakeSave.getCompound(AttachmentTarget.NBT_ATTACHMENT_KEY).contains(dummy.identifier().toString()));
map = AttachmentSerializingImpl.deserializeAttachmentData(fakeSave, null);
map = AttachmentSerializingImpl.deserializeAttachmentData(fakeSave, mockDRM());
assertEquals(1, map.size());
Map.Entry<AttachmentType<?>, Object> entry = map.entrySet().stream().findFirst().orElseThrow();
// in this case the key should be the exact same object
@ -150,19 +154,19 @@ public class CommonAttachmentTests {
@Test
void deserializeNull() {
var nbt = new NbtCompound();
assertNull(AttachmentSerializingImpl.deserializeAttachmentData(nbt, null));
assertNull(AttachmentSerializingImpl.deserializeAttachmentData(nbt, mockDRM()));
nbt.put(new Identifier("test").toString(), new NbtCompound());
assertNull(AttachmentSerializingImpl.deserializeAttachmentData(nbt, null));
assertNull(AttachmentSerializingImpl.deserializeAttachmentData(nbt, mockDRM()));
}
@Test
void serializeNullOrEmpty() {
var nbt = new NbtCompound();
AttachmentSerializingImpl.serializeAttachmentData(nbt, null, null);
AttachmentSerializingImpl.serializeAttachmentData(nbt, mockDRM(), null);
assertFalse(nbt.contains(AttachmentTarget.NBT_ATTACHMENT_KEY));
AttachmentSerializingImpl.serializeAttachmentData(nbt, null, new IdentityHashMap<>());
AttachmentSerializingImpl.serializeAttachmentData(nbt, mockDRM(), new IdentityHashMap<>());
assertFalse(nbt.contains(AttachmentTarget.NBT_ATTACHMENT_KEY));
}
@ -192,7 +196,10 @@ public class CommonAttachmentTests {
@Test
void testEntityPersistence() {
Entity entity = new MarkerEntity(EntityType.MARKER, mock());
DynamicRegistryManager drm = mockDRM();
World mockWorld = mock(World.class);
when(mockWorld.getRegistryManager()).thenReturn(drm);
Entity entity = new MarkerEntity(EntityType.MARKER, mockWorld);
assertFalse(entity.hasAttached(PERSISTENT));
int expected = 1;
@ -200,9 +207,8 @@ public class CommonAttachmentTests {
NbtCompound fakeSave = new NbtCompound();
entity.writeNbt(fakeSave);
entity = spy(new MarkerEntity(EntityType.MARKER, mock())); // fresh object, like on restart
entity = new MarkerEntity(EntityType.MARKER, mockWorld); // fresh object, like on restart
entity.setChangeListener(mock());
doNothing().when(entity).calculateDimensions();
entity.readNbt(fakeSave);
assertTrue(entity.hasAttached(PERSISTENT));
assertEquals(expected, entity.getAttached(PERSISTENT));
@ -215,9 +221,9 @@ public class CommonAttachmentTests {
int expected = 1;
blockEntity.setAttached(PERSISTENT, expected);
NbtCompound fakeSave = blockEntity.createNbtWithId(null);
NbtCompound fakeSave = blockEntity.createNbtWithId(mockDRM());
blockEntity = BlockEntity.createFromNbt(BlockPos.ORIGIN, mock(), fakeSave, null);
blockEntity = BlockEntity.createFromNbt(BlockPos.ORIGIN, mock(), fakeSave, mockDRM());
assertNotNull(blockEntity);
assertTrue(blockEntity.hasAttached(PERSISTENT));
assertEquals(expected, blockEntity.getAttached(PERSISTENT));
@ -232,10 +238,10 @@ public class CommonAttachmentTests {
int expected = 1;
world.setAttached(PERSISTENT, expected);
NbtCompound fakeSave = state.writeNbt(new NbtCompound(), null);
NbtCompound fakeSave = state.writeNbt(new NbtCompound(), mockDRM());
world = mock(ServerWorld.class, CALLS_REAL_METHODS);
AttachmentPersistentState.read(world, fakeSave, null);
AttachmentPersistentState.read(world, fakeSave, mockDRM());
assertTrue(world.hasAttached(PERSISTENT));
assertEquals(expected, world.getAttached(PERSISTENT));
}
@ -244,4 +250,10 @@ public class CommonAttachmentTests {
* Chunk serializing is coupled with world saving in ChunkSerializer which is too much of a pain to mock,
* so testing is handled by the testmod instead.
*/
private static DynamicRegistryManager mockDRM() {
DynamicRegistryManager drm = mock(DynamicRegistryManager.class);
when(drm.method_57093(any())).thenReturn((RegistryOps<Object>) (Object) RegistryOps.of(NbtOps.INSTANCE, drm));
return drm;
}
}

View file

@ -86,7 +86,7 @@ public abstract class FabricAdvancementProvider implements DataProvider {
generateAdvancement(advancements::add);
return this.registryLookup.thenCompose(lookup -> {
RegistryOps<JsonElement> ops = RegistryOps.of(JsonOps.INSTANCE, lookup);
RegistryOps<JsonElement> ops = lookup.method_57093(JsonOps.INSTANCE);
final List<CompletableFuture<?>> futures = new ArrayList<>();
for (AdvancementEntry advancement : advancements) {

View file

@ -59,7 +59,7 @@ public abstract class FabricCodecDataProvider<T> implements DataProvider {
public CompletableFuture<?> run(DataWriter writer) {
return this.registriesFuture.thenCompose(lookup -> {
Map<Identifier, JsonElement> entries = new HashMap<>();
RegistryOps<JsonElement> ops = RegistryOps.of(JsonOps.INSTANCE, lookup);
RegistryOps<JsonElement> ops = lookup.method_57093(JsonOps.INSTANCE);
BiConsumer<Identifier, T> provider = (id, value) -> {
JsonElement json = this.convert(id, value, ops);

View file

@ -208,7 +208,7 @@ public abstract class FabricDynamicRegistryProvider implements DataProvider {
return entries;
})
.thenCompose(entries -> {
final RegistryOps<JsonElement> dynamicOps = RegistryOps.of(JsonOps.INSTANCE, registries);
final RegistryOps<JsonElement> dynamicOps = registries.method_57093(JsonOps.INSTANCE);
ArrayList<CompletableFuture<?>> futures = new ArrayList<>();
for (RegistryEntries<?> registryEntries : entries.queuedEntries.values()) {

View file

@ -99,7 +99,7 @@ public abstract class FabricRecipeProvider extends RecipeProvider {
throw new IllegalStateException("Duplicate recipe " + identifier);
}
RegistryOps<JsonElement> registryOps = RegistryOps.of(JsonOps.INSTANCE, wrapperLookup);
RegistryOps<JsonElement> registryOps = wrapperLookup.method_57093(JsonOps.INSTANCE);
JsonObject recipeJson = Util.getResult(Recipe.CODEC.encodeStart(registryOps, recipe), IllegalStateException::new).getAsJsonObject();
ConditionJsonProvider[] conditions = FabricDataGenHelper.consumeConditions(recipe);
ConditionJsonProvider.write(recipeJson, conditions);

View file

@ -68,7 +68,7 @@ public final class FabricLootTableProviderImpl {
});
return registryLookup.thenCompose(lookup -> {
RegistryOps<JsonElement> ops = RegistryOps.of(JsonOps.INSTANCE, lookup);
RegistryOps<JsonElement> ops = lookup.method_57093(JsonOps.INSTANCE);
final List<CompletableFuture<?>> futures = new ArrayList<>();
for (Map.Entry<Identifier, LootTable> entry : builders.entrySet()) {

View file

@ -56,6 +56,7 @@ public abstract class TestFunctionsMixin {
gameTest.required(),
gameTest.requiredSuccesses(),
gameTest.maxAttempts(),
gameTest.method_57098(),
FabricGameTestHelper.getTestMethodInvoker(method)
);

View file

@ -38,12 +38,12 @@ public class BoxBlockEntity extends LootableContainerBlockEntity implements Exte
}
@Override
protected DefaultedList<ItemStack> method_11282() {
protected DefaultedList<ItemStack> getHeldStacks() {
return items;
}
@Override
protected void setInvStackList(DefaultedList<ItemStack> list) {
protected void setHeldStacks(DefaultedList<ItemStack> list) {
this.items = list;
}

View file

@ -282,7 +282,7 @@ public class VanillaStorageTests {
/**
* Regression test for <a href="https://github.com/FabricMC/fabric/issues/2810">double chest wrapper only updating modified halves</a>.
*/
@GameTest(templateName = "fabric-transfer-api-v1-testmod:double_chest_comparators")
@GameTest(templateName = "fabric-transfer-api-v1-testmod:double_chest_comparators", method_57098 = true)
public void testDoubleChestComparator(TestContext context) {
BlockPos chestPos = new BlockPos(2, 2, 2);
Storage<ItemVariant> storage = ItemStorage.SIDED.find(context.getWorld(), context.getAbsolutePos(chestPos), Direction.UP);

View file

@ -2,9 +2,9 @@ org.gradle.jvmargs=-Xmx2560M
org.gradle.parallel=true
fabric.loom.multiProjectOptimisation=true
version=0.96.1
minecraft_version=24w06a
yarn_version=+build.6
version=0.96.2
minecraft_version=24w07a
yarn_version=+build.1
loader_version=0.15.6
installer_version=0.11.1
@ -14,23 +14,23 @@ curseforge_minecraft_version=1.20.5-Snapshot
# Do not manually update, use the bumpversions task:
fabric-api-base-version=0.4.38
fabric-api-lookup-api-v1-version=1.6.53
fabric-biome-api-v1-version=13.0.19
fabric-biome-api-v1-version=13.0.20
fabric-block-api-v1-version=1.0.18
fabric-block-view-api-v2-version=1.0.6
fabric-blockrenderlayer-v1-version=1.1.48
fabric-command-api-v1-version=1.2.43
fabric-command-api-v2-version=2.2.22
fabric-commands-v0-version=0.2.60
fabric-content-registries-v0-version=6.0.7
fabric-content-registries-v0-version=6.0.8
fabric-crash-report-info-v1-version=0.2.25
fabric-data-attachment-api-v1-version=1.1.1
fabric-data-attachment-api-v1-version=1.1.2
fabric-data-generation-api-v1-version=16.0.2
fabric-dimensions-v1-version=2.1.64
fabric-entity-events-v1-version=1.6.2
fabric-events-interaction-v0-version=0.7.4
fabric-events-lifecycle-v0-version=0.2.79
fabric-game-rule-api-v1-version=1.0.48
fabric-gametest-api-v1-version=1.3.6
fabric-gametest-api-v1-version=1.3.7
fabric-item-api-v1-version=4.0.2
fabric-item-group-api-v1-version=4.0.27
fabric-key-binding-api-v1-version=1.0.43
@ -56,9 +56,9 @@ fabric-rendering-v1-version=4.2.0
fabric-resource-conditions-api-v1-version=2.3.17
fabric-resource-loader-v0-version=0.11.22
fabric-screen-api-v1-version=2.0.19
fabric-screen-handler-api-v1-version=1.3.60
fabric-screen-handler-api-v1-version=1.3.61
fabric-sound-api-v1-version=1.0.19
fabric-transfer-api-v1-version=4.0.15
fabric-transfer-api-v1-version=4.0.16
fabric-transitive-access-wideners-v1-version=6.0.5
fabric-convention-tags-v1-version=1.5.13
fabric-client-tags-api-v1-version=1.1.9