diff --git a/fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/api/datagen/v1/provider/FabricCodecDataProvider.java b/fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/api/datagen/v1/provider/FabricCodecDataProvider.java new file mode 100644 index 000000000..86190079b --- /dev/null +++ b/fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/api/datagen/v1/provider/FabricCodecDataProvider.java @@ -0,0 +1,88 @@ +/* + * 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.datagen.v1.provider; + +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +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.JsonOps; + +import net.minecraft.data.DataOutput; +import net.minecraft.data.DataProvider; +import net.minecraft.data.DataWriter; +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}. + * + * <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 Codec<T> codec; + + protected FabricCodecDataProvider(FabricDataOutput dataOutput, DataOutput.OutputType outputType, String directoryName, Codec<T> codec) { + this.pathResolver = dataOutput.getResolver(outputType, directoryName); + this.codec = codec; + } + + @Override + public CompletableFuture<?> run(DataWriter writer) { + Map<Identifier, JsonElement> entries = new HashMap<>(); + BiConsumer<Identifier, T> provider = (id, value) -> { + JsonElement json = this.convert(id, value); + JsonElement existingJson = entries.put(id, json); + + if (existingJson != null) { + throw new IllegalArgumentException("Duplicate entry " + id); + } + }; + + this.configure(provider); + return this.write(writer, entries); + } + + /** + * Implement this method to register entries to generate. + * + * @param provider A consumer that accepts an {@link Identifier} and a value to register. + */ + protected abstract void configure(BiConsumer<Identifier, T> provider); + + private JsonElement convert(Identifier id, T value) { + DataResult<JsonElement> dataResult = this.codec.encodeStart(JsonOps.INSTANCE, value); + return dataResult.get() + .mapRight(partial -> "Invalid entry %s: %s".formatted(id, partial.message())) + .orThrow(); + } + + private CompletableFuture<?> write(DataWriter writer, Map<Identifier, JsonElement> entries) { + return CompletableFuture.allOf(entries.entrySet().stream().map(entry -> { + Path path = this.pathResolver.resolveJson(entry.getKey()); + return DataProvider.writeToPath(writer, entry.getValue(), path); + }).toArray(CompletableFuture[]::new)); + } +} 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 13491c112..bb63ccfe1 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 @@ -26,6 +26,7 @@ import static net.fabricmc.fabric.test.datagen.DataGeneratorTestContent.SIMPLE_I import java.io.IOException; import java.nio.file.Path; +import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; @@ -38,7 +39,11 @@ import net.minecraft.advancement.Advancement; import net.minecraft.advancement.AdvancementFrame; import net.minecraft.advancement.criterion.OnKilledCriterion; import net.minecraft.block.Blocks; +import net.minecraft.client.texture.atlas.AtlasSource; +import net.minecraft.client.texture.atlas.AtlasSourceManager; +import net.minecraft.client.texture.atlas.DirectoryAtlasSource; import net.minecraft.registry.RegistryKeys; +import net.minecraft.data.DataOutput; import net.minecraft.data.client.BlockStateModelGenerator; import net.minecraft.data.client.ItemModelGenerator; import net.minecraft.data.server.recipe.RecipeJsonProvider; @@ -69,6 +74,7 @@ import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator; import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; 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.FabricLanguageProvider; import net.fabricmc.fabric.api.datagen.v1.provider.FabricModelProvider; import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider; @@ -98,6 +104,7 @@ public class DataGeneratorTestEntrypoint implements DataGeneratorEntrypoint { TestBlockTagProvider blockTagProvider = pack.addProvider(TestBlockTagProvider::new); pack.addProvider((output, registries) -> new TestItemTagProvider(output, registries, blockTagProvider)); pack.addProvider(TestBiomeTagProvider::new); + pack.addProvider(TestAtlasSourceProvider::new); } private static class TestRecipeProvider extends FabricRecipeProvider { @@ -350,4 +357,20 @@ public class DataGeneratorTestEntrypoint implements DataGeneratorEntrypoint { ); } } + + private static class TestAtlasSourceProvider extends FabricCodecDataProvider<List<AtlasSource>> { + private TestAtlasSourceProvider(FabricDataOutput dataOutput) { + super(dataOutput, DataOutput.OutputType.RESOURCE_PACK, "atlases", AtlasSourceManager.LIST_CODEC); + } + + @Override + protected void configure(BiConsumer<Identifier, List<AtlasSource>> provider) { + provider.accept(new Identifier(MOD_ID, "atlas_source_test"), List.of(new DirectoryAtlasSource("example", "example/"))); + } + + @Override + public String getName() { + return "Atlas Sources"; + } + } }