mirror of
https://github.com/FabricMC/fabric.git
synced 2025-03-24 22:11:18 -04:00
Allow FabricCodecDataProvider
to access dynamic registries (#3522)
* Add CompletableFuture to FabricCodecDataProvider * Deprecate the old method and constructor, and fix the style * Fix the style for real this time * Add exceptions to the configure methods * Apply suggestions from code review --------- Co-authored-by: modmuss <modmuss50@gmail.com>
This commit is contained in:
parent
707e4d1bfd
commit
5c013344f0
2 changed files with 112 additions and 3 deletions
fabric-data-generation-api-v1/src
main/java/net/fabricmc/fabric/api/datagen/v1/provider
testmod/java/net/fabricmc/fabric/test/datagen
|
@ -19,38 +19,74 @@ package net.fabricmc.fabric.api.datagen.v1.provider;
|
|||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.DataResult;
|
||||
import com.mojang.serialization.DynamicOps;
|
||||
import com.mojang.serialization.JsonOps;
|
||||
|
||||
import net.minecraft.data.DataOutput;
|
||||
import net.minecraft.data.DataProvider;
|
||||
import net.minecraft.data.DataWriter;
|
||||
import net.minecraft.registry.RegistryOps;
|
||||
import net.minecraft.registry.RegistryWrapper;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
|
||||
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
|
||||
|
||||
/**
|
||||
* Extend this class and implement {@link FabricCodecDataProvider#configure}.
|
||||
* Extend this class and implement {@link FabricCodecDataProvider#configure(BiConsumer, RegistryWrapper.WrapperLookup)}.
|
||||
*
|
||||
* <p>Register an instance of the class with {@link FabricDataGenerator.Pack#addProvider} in a {@link net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint}.
|
||||
*/
|
||||
public abstract class FabricCodecDataProvider<T> implements DataProvider {
|
||||
private final DataOutput.PathResolver pathResolver;
|
||||
private final CompletableFuture<RegistryWrapper.WrapperLookup> registriesFuture;
|
||||
private final Codec<T> codec;
|
||||
|
||||
/**
|
||||
* @deprecated Please use {@link FabricCodecDataProvider#FabricCodecDataProvider(FabricDataOutput, CompletableFuture, DataOutput.OutputType, String, Codec)}.
|
||||
*/
|
||||
@Deprecated()
|
||||
protected FabricCodecDataProvider(FabricDataOutput dataOutput, DataOutput.OutputType outputType, String directoryName, Codec<T> codec) {
|
||||
this.pathResolver = dataOutput.getResolver(outputType, directoryName);
|
||||
this.registriesFuture = null;
|
||||
this.codec = codec;
|
||||
}
|
||||
|
||||
protected FabricCodecDataProvider(FabricDataOutput dataOutput, CompletableFuture<RegistryWrapper.WrapperLookup> registriesFuture, DataOutput.OutputType outputType, String directoryName, Codec<T> codec) {
|
||||
this.pathResolver = dataOutput.getResolver(outputType, directoryName);
|
||||
this.registriesFuture = Objects.requireNonNull(registriesFuture);
|
||||
this.codec = codec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> run(DataWriter writer) {
|
||||
// TODO: Remove the null check once the deprecated method and constructor are removed.
|
||||
if (this.registriesFuture != null) {
|
||||
return this.registriesFuture.thenCompose(lookup -> {
|
||||
Map<Identifier, JsonElement> entries = new HashMap<>();
|
||||
RegistryOps<JsonElement> ops = RegistryOps.of(JsonOps.INSTANCE, lookup);
|
||||
|
||||
BiConsumer<Identifier, T> provider = (id, value) -> {
|
||||
JsonElement json = this.convert(id, value, ops);
|
||||
JsonElement existingJson = entries.put(id, json);
|
||||
|
||||
if (existingJson != null) {
|
||||
throw new IllegalArgumentException("Duplicate entry " + id);
|
||||
}
|
||||
};
|
||||
|
||||
this.configure(provider, lookup);
|
||||
return this.write(writer, entries);
|
||||
});
|
||||
}
|
||||
|
||||
Map<Identifier, JsonElement> entries = new HashMap<>();
|
||||
BiConsumer<Identifier, T> provider = (id, value) -> {
|
||||
JsonElement json = this.convert(id, value);
|
||||
|
@ -69,11 +105,29 @@ public abstract class FabricCodecDataProvider<T> implements DataProvider {
|
|||
* Implement this method to register entries to generate.
|
||||
*
|
||||
* @param provider A consumer that accepts an {@link Identifier} and a value to register.
|
||||
* @deprecated Please use {@link FabricCodecDataProvider#configure(BiConsumer, RegistryWrapper.WrapperLookup)}.
|
||||
*/
|
||||
protected abstract void configure(BiConsumer<Identifier, T> provider);
|
||||
@Deprecated()
|
||||
protected void configure(BiConsumer<Identifier, T> provider) {
|
||||
throw new UnsupportedOperationException("Override this method.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this method to register entries to generate using a {@link RegistryWrapper.WrapperLookup}.
|
||||
* @param provider A consumer that accepts an {@link Identifier} and a value to register.
|
||||
* @param lookup A lookup for registries.
|
||||
*/
|
||||
protected void configure(BiConsumer<Identifier, T> provider, RegistryWrapper.WrapperLookup lookup) {
|
||||
// TODO: Make abstract once the deprecated method is removed.
|
||||
throw new UnsupportedOperationException("Override this method.");
|
||||
}
|
||||
|
||||
private JsonElement convert(Identifier id, T value) {
|
||||
DataResult<JsonElement> dataResult = this.codec.encodeStart(JsonOps.INSTANCE, value);
|
||||
return this.convert(id, value, JsonOps.INSTANCE);
|
||||
}
|
||||
|
||||
private JsonElement convert(Identifier id, T value, DynamicOps<JsonElement> ops) {
|
||||
DataResult<JsonElement> dataResult = this.codec.encodeStart(ops, value);
|
||||
return dataResult.get()
|
||||
.mapRight(partial -> "Invalid entry %s: %s".formatted(id, partial.message()))
|
||||
.orThrow();
|
||||
|
|
|
@ -34,6 +34,8 @@ import java.util.concurrent.CompletableFuture;
|
|||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -41,7 +43,10 @@ import net.minecraft.advancement.Advancement;
|
|||
import net.minecraft.advancement.AdvancementEntry;
|
||||
import net.minecraft.advancement.AdvancementFrame;
|
||||
import net.minecraft.advancement.criterion.OnKilledCriterion;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockKeys;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.data.DataOutput;
|
||||
import net.minecraft.data.client.BlockStateModelGenerator;
|
||||
import net.minecraft.data.client.ItemModelGenerator;
|
||||
import net.minecraft.data.server.recipe.RecipeExporter;
|
||||
|
@ -53,6 +58,9 @@ import net.minecraft.item.Items;
|
|||
import net.minecraft.loot.LootPool;
|
||||
import net.minecraft.loot.LootTable;
|
||||
import net.minecraft.loot.LootTables;
|
||||
import net.minecraft.loot.condition.BlockStatePropertyLootCondition;
|
||||
import net.minecraft.loot.condition.LootCondition;
|
||||
import net.minecraft.loot.condition.LootConditionTypes;
|
||||
import net.minecraft.loot.context.LootContextTypes;
|
||||
import net.minecraft.loot.entry.ItemEntry;
|
||||
import net.minecraft.loot.provider.number.ConstantLootNumberProvider;
|
||||
|
@ -60,8 +68,11 @@ import net.minecraft.recipe.Ingredient;
|
|||
import net.minecraft.recipe.book.RecipeCategory;
|
||||
import net.minecraft.registry.Registerable;
|
||||
import net.minecraft.registry.RegistryBuilder;
|
||||
import net.minecraft.registry.RegistryEntryLookup;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.registry.RegistryWrapper;
|
||||
import net.minecraft.registry.entry.RegistryEntry;
|
||||
import net.minecraft.registry.entry.RegistryFixedCodec;
|
||||
import net.minecraft.registry.tag.BlockTags;
|
||||
import net.minecraft.registry.tag.ItemTags;
|
||||
import net.minecraft.registry.tag.TagKey;
|
||||
|
@ -77,6 +88,7 @@ import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
|
|||
import net.fabricmc.fabric.api.datagen.v1.JsonKeySortOrderCallback;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricAdvancementProvider;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricBlockLootTableProvider;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricCodecDataProvider;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricDynamicRegistryProvider;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricLanguageProvider;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricModelProvider;
|
||||
|
@ -110,6 +122,8 @@ public class DataGeneratorTestEntrypoint implements DataGeneratorEntrypoint {
|
|||
pack.addProvider(ExistingEnglishLangProvider::new);
|
||||
pack.addProvider(JapaneseLangProvider::new);
|
||||
pack.addProvider(TestDynamicRegistryProvider::new);
|
||||
pack.addProvider(TestPredicateProvider::new);
|
||||
pack.addProvider(TestCustomCodecProvider::new);
|
||||
|
||||
TestBlockTagProvider blockTagProvider = pack.addProvider(TestBlockTagProvider::new);
|
||||
pack.addProvider((output, registries) -> new TestItemTagProvider(output, registries, blockTagProvider));
|
||||
|
@ -410,4 +424,45 @@ public class DataGeneratorTestEntrypoint implements DataGeneratorEntrypoint {
|
|||
return "Test Dynamic Registry";
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestPredicateProvider extends FabricCodecDataProvider<LootCondition> {
|
||||
private TestPredicateProvider(FabricDataOutput dataOutput, CompletableFuture<RegistryWrapper.WrapperLookup> registriesFuture) {
|
||||
super(dataOutput, registriesFuture, DataOutput.OutputType.DATA_PACK, "predicates", LootConditionTypes.CODEC);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(BiConsumer<Identifier, LootCondition> provider, RegistryWrapper.WrapperLookup lookup) {
|
||||
RegistryEntryLookup<Block> blocks = lookup.createRegistryLookup().getOrThrow(RegistryKeys.BLOCK);
|
||||
provider.accept(new Identifier(MOD_ID, "predicate_test"), BlockStatePropertyLootCondition.builder(
|
||||
blocks.getOrThrow(BlockKeys.MELON).value()).build()); // Pretend this actually does something and we cannot access the blocks directly
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Predicates";
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestCustomCodecProvider extends FabricCodecDataProvider<TestCustomCodecProvider.Entry> {
|
||||
private TestCustomCodecProvider(FabricDataOutput dataOutput, CompletableFuture<RegistryWrapper.WrapperLookup> registriesFuture) {
|
||||
super(dataOutput, registriesFuture, DataOutput.OutputType.DATA_PACK, "biome_entry", Entry.CODEC);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(BiConsumer<Identifier, Entry> provider, RegistryWrapper.WrapperLookup lookup) {
|
||||
RegistryEntryLookup<Biome> biomes = lookup.createRegistryLookup().getOrThrow(RegistryKeys.BIOME);
|
||||
provider.accept(new Identifier(MOD_ID, "custom_codec_test"), new Entry(biomes.getOrThrow(BiomeKeys.PLAINS)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Codec Test Using Dynamic Registry";
|
||||
}
|
||||
|
||||
private record Entry(RegistryEntry<Biome> biome) {
|
||||
private static final Codec<Entry> CODEC = RecordCodecBuilder.create(instance -> instance.group(
|
||||
RegistryFixedCodec.of(RegistryKeys.BIOME).fieldOf("biome").forGetter(Entry::biome)
|
||||
).apply(instance, Entry::new));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue