From 204dc73a11b1603580f7cb4a8de39aec68370354 Mon Sep 17 00:00:00 2001 From: Matyrobbrt Date: Thu, 6 Apr 2023 19:12:46 +0300 Subject: [PATCH 1/9] Datagen stuff --- .../cgl/api/extension/ItemExtensions.groovy | 52 +++++ .../api/extension/StaticNBTExtensions.groovy | 1 + ...rg.codehaus.groovy.runtime.ExtensionModule | 2 +- .../api/datagen/recipe/GRecipeProvider.groovy | 172 ++++++++++++++++ .../recipe/GShapedRecipeBuilder.groovy | 185 ++++++++++++++++++ .../recipe/GShapelessRecipeBuilder.groovy | 122 ++++++++++++ Forge/build.gradle | 4 +- .../9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e | 6 + .../recipes/decorations/yesyes.json | 34 ++++ .../data/anothermod/recipes/yesyes.json | 10 + .../data/cgltest/recipes/my_stairs.json | 18 ++ .../data/cgltest/recipes/sprucex12.json | 13 ++ .../data/minecraft/recipes/acacia_boat.json | 13 ++ Forge/src/test/groovy/cgltest/Datagen.groovy | 69 +++++++ 14 files changed, 698 insertions(+), 3 deletions(-) create mode 100644 Common/src/extension/groovy/io/github/groovymc/cgl/api/extension/ItemExtensions.groovy create mode 100644 Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GRecipeProvider.groovy create mode 100644 Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GShapedRecipeBuilder.groovy create mode 100644 Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GShapelessRecipeBuilder.groovy create mode 100644 Forge/src/test/generated/resources/cgltest/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e create mode 100644 Forge/src/test/generated/resources/cgltest/data/anothermod/advancements/recipes/decorations/yesyes.json create mode 100644 Forge/src/test/generated/resources/cgltest/data/anothermod/recipes/yesyes.json create mode 100644 Forge/src/test/generated/resources/cgltest/data/cgltest/recipes/my_stairs.json create mode 100644 Forge/src/test/generated/resources/cgltest/data/cgltest/recipes/sprucex12.json create mode 100644 Forge/src/test/generated/resources/cgltest/data/minecraft/recipes/acacia_boat.json create mode 100644 Forge/src/test/groovy/cgltest/Datagen.groovy diff --git a/Common/src/extension/groovy/io/github/groovymc/cgl/api/extension/ItemExtensions.groovy b/Common/src/extension/groovy/io/github/groovymc/cgl/api/extension/ItemExtensions.groovy new file mode 100644 index 0000000..fad7589 --- /dev/null +++ b/Common/src/extension/groovy/io/github/groovymc/cgl/api/extension/ItemExtensions.groovy @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2022 GroovyMC and contributors + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + +package io.github.groovymc.cgl.api.extension + +import groovy.transform.CompileStatic +import net.minecraft.nbt.CompoundTag +import net.minecraft.tags.TagKey +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.crafting.Ingredient +import net.minecraft.world.level.ItemLike + +@CompileStatic +class ItemExtensions { + static ItemStack count(ItemLike self, int count) { + new ItemStack(self, count) + } + + static ItemStack multiply(ItemLike self, int count) { + new ItemStack(self, count) + } + + static ItemStack multiply(Integer count, ItemLike item) { + new ItemStack(item, count) + } + + static ItemStack count(ItemStack self, int count) { + self.setCount(count) + return self + } + + static ItemStack tag(ItemStack self, CompoundTag tag) { + self.setTag(tag) + return self + } + + static ItemStack tag(ItemStack self, Object tag) { + self.setTag(StaticNBTExtensions.from(null, tag) as CompoundTag) + return self + } + + static Ingredient ingredient(ItemLike item) { + return Ingredient.of(item) + } + + static Ingredient ingredient(TagKey tag) { + return Ingredient.of(tag) + } +} diff --git a/Common/src/extension/groovy/io/github/groovymc/cgl/api/extension/StaticNBTExtensions.groovy b/Common/src/extension/groovy/io/github/groovymc/cgl/api/extension/StaticNBTExtensions.groovy index ae6a203..326b15a 100644 --- a/Common/src/extension/groovy/io/github/groovymc/cgl/api/extension/StaticNBTExtensions.groovy +++ b/Common/src/extension/groovy/io/github/groovymc/cgl/api/extension/StaticNBTExtensions.groovy @@ -45,6 +45,7 @@ final class StaticNBTExtensions { case Collection -> new ListTag().tap { list -> toConvert.each { list.add(from(null, it)) } } + case Map -> of(null, toConvert as Map) default -> (Tag) null } } diff --git a/Common/src/extension/resources/META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule b/Common/src/extension/resources/META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule index 92d50e2..bee32f7 100644 --- a/Common/src/extension/resources/META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule +++ b/Common/src/extension/resources/META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule @@ -1,4 +1,4 @@ moduleName=CGL Extensions moduleVersion=0.2.0 -extensionClasses=io.github.groovymc.cgl.api.extension.registry.RegistryExtension,io.github.groovymc.cgl.api.extension.math.ArithmeticExtension,io.github.groovymc.cgl.api.extension.chat.ComponentExtension,io.github.groovymc.cgl.api.extension.TagExtensions,io.github.groovymc.cgl.api.extension.client.MinecraftExtensions,io.github.groovymc.cgl.api.extension.NBTExtensions,io.github.groovymc.cgl.api.extension.CodecExtensions,io.github.groovymc.cgl.api.extension.brigadier.CommandExtensions,io.github.groovymc.cgl.api.extension.brigadier.CommandContextExtensions,io.github.groovymc.cgl.api.extension.brigadier.ArgumentExtensions,io.github.groovymc.cgl.api.extension.GeneralExtensions +extensionClasses=io.github.groovymc.cgl.api.extension.registry.RegistryExtension,io.github.groovymc.cgl.api.extension.math.ArithmeticExtension,io.github.groovymc.cgl.api.extension.chat.ComponentExtension,io.github.groovymc.cgl.api.extension.TagExtensions,io.github.groovymc.cgl.api.extension.client.MinecraftExtensions,io.github.groovymc.cgl.api.extension.NBTExtensions,io.github.groovymc.cgl.api.extension.CodecExtensions,io.github.groovymc.cgl.api.extension.brigadier.CommandExtensions,io.github.groovymc.cgl.api.extension.brigadier.CommandContextExtensions,io.github.groovymc.cgl.api.extension.brigadier.ArgumentExtensions,io.github.groovymc.cgl.api.extension.GeneralExtensions,io.github.groovymc.cgl.api.extension.ItemExtensions staticExtensionClasses=io.github.groovymc.cgl.api.extension.StaticGeneralExtensions,io.github.groovymc.cgl.api.extension.chat.StyleExtension,io.github.groovymc.cgl.api.extension.StaticNBTExtensions diff --git a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GRecipeProvider.groovy b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GRecipeProvider.groovy new file mode 100644 index 0000000..ab43aa8 --- /dev/null +++ b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GRecipeProvider.groovy @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2022 GroovyMC and contributors + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + +package io.github.groovymc.cgl.api.datagen.recipe + +import groovy.transform.CompileStatic +import groovy.transform.stc.ClosureParams +import groovy.transform.stc.FirstParam +import groovy.transform.stc.SimpleType +import net.minecraft.advancements.Advancement +import net.minecraft.advancements.CriterionTriggerInstance +import net.minecraft.core.registries.BuiltInRegistries +import net.minecraft.data.PackOutput +import net.minecraft.data.recipes.CraftingRecipeBuilder +import net.minecraft.data.recipes.FinishedRecipe +import net.minecraft.data.recipes.RecipeBuilder +import net.minecraft.data.recipes.RecipeCategory +import net.minecraft.data.recipes.RecipeProvider +import net.minecraft.resources.ResourceLocation +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.crafting.CraftingBookCategory +import net.minecraft.world.level.ItemLike +import org.jetbrains.annotations.Nullable + +import java.util.function.Consumer + +@CompileStatic +abstract class GRecipeProvider extends RecipeProvider { + public Consumer writer + protected final String defaultNamespace + GRecipeProvider(PackOutput packOutput, String defaultNamespace = 'minecraft') { + super(packOutput) + this.defaultNamespace = defaultNamespace + } + + @Override + protected void buildRecipes(Consumer writer) { + this.writer = writer + this.buildRecipes() + this.writer = null + } + + protected abstract void buildRecipes() + + protected GShapedRecipeBuilder shaped(@DelegatesTo(value = GShapedRecipeBuilder, strategy = Closure.DELEGATE_FIRST) @ClosureParams(value = SimpleType, options = 'io.github.groovymc.cgl.api.datagen.GShapedRecipeBuilder') Closure clos) { + recipe(new GShapedRecipeBuilder(), clos) + } + + protected GShapelessRecipeBuilder shapeless(@DelegatesTo(value = GShapelessRecipeBuilder,strategy = Closure.DELEGATE_FIRST) @ClosureParams(value = SimpleType, options = 'io.github.groovymc.cgl.api.datagen.GShapelessRecipeBuilder') Closure clos) { + recipe(new GShapelessRecipeBuilder(), clos) + } + + protected T recipe(@DelegatesTo.Target('recipe') Class recipeClass, @DelegatesTo(target = 'recipe', genericTypeIndex = 0, strategy = Closure.DELEGATE_FIRST) @ClosureParams(FirstParam.FirstGenericType) Closure clos) { + return recipe(recipeClass.getDeclaredConstructor().newInstance(), clos) + } + + protected T recipe(@DelegatesTo.Target('recipe') T recipe, @DelegatesTo(target = 'recipe', strategy = Closure.DELEGATE_FIRST) @ClosureParams(FirstParam) Closure clos) { + recipe.provider = this + clos.resolveStrategy = Closure.DELEGATE_FIRST + clos.delegate = recipe + clos(recipe) + return recipe + } + + protected SaveableRecipe normal(@DelegatesTo.Target('recipe') T recipe, @DelegatesTo(target = 'recipe', strategy = Closure.DELEGATE_FIRST) @ClosureParams(FirstParam) Closure clos = {}) { + clos.resolveStrategy = Closure.DELEGATE_FIRST + clos.delegate = recipe + clos(recipe) + return new SaveableRecipe() { + @Override + void save(ResourceLocation location) { + recipe.save(getProvider().writer, location) + } + + @Override + GRecipeProvider getProvider() { + return GRecipeProvider.this + } + + @Override + void setProvider(GRecipeProvider provider) { + + } + } + } +} + +@CompileStatic +trait SaveableRecipe { + GRecipeProvider provider + + void save(String location) { + this.save(location.contains(':') ? new ResourceLocation(location) : new ResourceLocation(provider.defaultNamespace, location)) + } + + abstract void save(ResourceLocation location) +} + +@CompileStatic +trait BaseRecipeBuilder extends SaveableRecipe implements RecipeBuilder { + ItemStack result + + ItemStack result(ItemLike result) { + this.result = result.asItem().getDefaultInstance() + return this.result + } + + ItemStack result(ItemStack result) { + this.result = result + return this.result + } + + Item getResult() { + return this.@result.item + } + + ItemStack getResultStack() { + return this.@result + } + + void save() { + this.save(BuiltInRegistries.ITEM.getKey(getResult())) + } + + void save(ResourceLocation location) { + this.save(this.getProvider().writer, location) + } +} + +@CompileStatic +trait SimpleRecipeBuilder extends BaseRecipeBuilder { + private RecipeCategory category = RecipeCategory.MISC + @Nullable + private String group + private final Advancement.Builder advancement = Advancement.Builder.advancement() + + RecipeCategory getCategory() { + return this.@category + } + T setCategory(RecipeCategory category) { + this.@category = category + return (T) this + } + + T category(RecipeCategory category) { + return setCategory(category) + } + + String getGroup() { + return this.@group + } + T setGroup(String group) { + this.@group = group + return (T) this + } + + T group(String group) { + return setGroup(group) + } + + T unlockedBy(String criterionName, CriterionTriggerInstance criterionTrigger) { + this.@advancement.addCriterion(criterionName, criterionTrigger) + return (T) this + } + + Advancement.Builder getAdvancement() { + return this.@advancement + } +} \ No newline at end of file diff --git a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GShapedRecipeBuilder.groovy b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GShapedRecipeBuilder.groovy new file mode 100644 index 0000000..bc5dd20 --- /dev/null +++ b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GShapedRecipeBuilder.groovy @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2022 GroovyMC and contributors + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + +package io.github.groovymc.cgl.api.datagen.recipe + +import com.google.common.collect.Lists +import com.google.common.collect.Maps +import com.google.common.collect.Sets +import com.google.gson.JsonArray +import com.google.gson.JsonObject +import groovy.transform.CompileStatic +import net.minecraft.advancements.Advancement +import net.minecraft.advancements.AdvancementRewards +import net.minecraft.advancements.RequirementsStrategy +import net.minecraft.advancements.critereon.RecipeUnlockedTrigger +import net.minecraft.core.registries.BuiltInRegistries +import net.minecraft.data.recipes.CraftingRecipeBuilder +import net.minecraft.data.recipes.FinishedRecipe +import net.minecraft.resources.ResourceLocation +import net.minecraft.tags.TagKey +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.crafting.CraftingBookCategory +import net.minecraft.world.item.crafting.Ingredient +import net.minecraft.world.item.crafting.RecipeSerializer +import net.minecraft.world.level.ItemLike +import org.jetbrains.annotations.Nullable + +import java.util.function.Consumer + +@CompileStatic +class GShapedRecipeBuilder extends CraftingRecipeBuilder implements SimpleRecipeBuilder { + private final List rows = Lists.newArrayList() + private final Map key = Maps.newLinkedHashMap() + private boolean showNotification = true + + GShapedRecipeBuilder define(String symbol, TagKey tag) { + this.define(symbol, Ingredient.of(tag)) + } + + GShapedRecipeBuilder define(String symbol, ItemLike item) { + this.define(symbol, Ingredient.of(item)) + } + + GShapedRecipeBuilder define(String symbol, Ingredient ingredient) { + if (this.key.containsKey(symbol)) { + throw new IllegalArgumentException("Symbol '$symbol' is already defined!") + } else if (symbol == ' ') { + throw new IllegalArgumentException("Symbol ' ' (whitespace) is reserved and cannot be defined") + } else { + this.key.put(symbol.charAt(0), ingredient) + return this + } + } + + GShapedRecipeBuilder pattern(String... patterns) { + patterns.each { pattern -> + if (!this.rows.isEmpty() && pattern.length() !== this.rows[0].length()) { + throw new IllegalArgumentException('Pattern must be the same width on every line!') + } else { + this.rows.add(pattern) + } + } + return this + } + + GShapedRecipeBuilder showNotification(boolean showNotification) { + this.@showNotification = showNotification + return this + } + + GShapedRecipeBuilder setShowNotification(boolean showNotification) { + this.@showNotification = showNotification + return this + } + + void save(Consumer finishedRecipeConsumer, ResourceLocation recipeId) { + this.ensureValid(recipeId) + this.advancement.parent(ROOT_RECIPE_ADVANCEMENT).addCriterion("has_the_recipe", RecipeUnlockedTrigger.unlocked(recipeId)).rewards(AdvancementRewards.Builder.recipe(recipeId)).requirements(RequirementsStrategy.OR) + finishedRecipeConsumer.accept(new Result(recipeId, this.resultStack, this.group == null ? "" : this.group, determineBookCategory(this.category), this.rows, this.key, this.advancement, recipeId.withPrefix("recipes/" + this.category.getFolderName() + "/"), this.showNotification)) + } + + private void ensureValid(ResourceLocation id) { + if (this.rows.isEmpty()) { + throw new IllegalStateException("No pattern is defined for shaped recipe $id!") + } else { + Set set = Sets.newHashSet(this.key.keySet()) + set.remove(' ') + final itr = this.rows.iterator() + + while (itr.hasNext()) { + final row = itr.next() + + for (int i = 0; i < row.length(); ++i) { + char c = row.charAt(i) + if (!this.key.containsKey(c) && c !== ' ' as char) { + throw new IllegalStateException("Pattern in recipe $id uses undefined symbol '$c'") + } + + set.remove(c) + } + } + + if (!set.isEmpty()) { + throw new IllegalStateException("Ingredients are defined but not used in pattern for recipe $id") + } + } + } + + private static class Result extends CraftingRecipeBuilder.CraftingResult { + private final ResourceLocation id + private final ItemStack result + private final String group + private final List pattern + private final Map key + private final Advancement.Builder advancement + private final ResourceLocation advancementId + private final boolean showNotification + + Result(ResourceLocation resourceLocation, ItemStack result, String string, CraftingBookCategory craftingBookCategory, List list, Map map, Advancement.Builder builder, ResourceLocation resourceLocation2, boolean bl) { + super(craftingBookCategory) + this.id = resourceLocation + this.result = result + this.group = string + this.pattern = list + this.key = map + this.advancement = builder + this.advancementId = resourceLocation2 + this.showNotification = bl + } + + void serializeRecipeData(JsonObject json) { + super.serializeRecipeData(json) + if (!this.group.isEmpty()) { + json.addProperty('group', this.group) + } + + JsonArray jsonArray = new JsonArray() + this.pattern.each { + jsonArray.add(it) + } + + json.add('pattern', jsonArray) + + JsonObject jsonObject = new JsonObject() + Iterator itr = this.key.entrySet().iterator() + + while (itr.hasNext()) { + Map.Entry entry = (Map.Entry)itr.next() + jsonObject.add(String.valueOf(entry.getKey()), ((Ingredient)entry.getValue()).toJson()) + } + + json.add('key', jsonObject) + JsonObject jsonObject2 = new JsonObject() + jsonObject2.addProperty('item', BuiltInRegistries.ITEM.getKey(this.result.item).toString()) + if (this.result.count > 1) { + jsonObject2.addProperty('count', this.result.count) + } + // TODO - tag support + + json.add('result', jsonObject2) + json.addProperty('show_notification', this.showNotification) + } + + RecipeSerializer getType() { + RecipeSerializer.SHAPED_RECIPE + } + + ResourceLocation getId() { + return this.id + } + + @Nullable + JsonObject serializeAdvancement() { + return this.advancement.criteria.size() == 1 ? null : this.advancement.serializeToJson() + } + + @Nullable + ResourceLocation getAdvancementId() { + return this.advancementId + } + } +} diff --git a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GShapelessRecipeBuilder.groovy b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GShapelessRecipeBuilder.groovy new file mode 100644 index 0000000..336a010 --- /dev/null +++ b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GShapelessRecipeBuilder.groovy @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2022 GroovyMC and contributors + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + +package io.github.groovymc.cgl.api.datagen.recipe + +import com.google.common.collect.Lists +import com.google.gson.JsonArray +import com.google.gson.JsonObject +import groovy.transform.CompileStatic +import net.minecraft.advancements.Advancement +import net.minecraft.advancements.RequirementsStrategy +import net.minecraft.advancements.critereon.RecipeUnlockedTrigger +import net.minecraft.core.registries.BuiltInRegistries +import net.minecraft.data.recipes.CraftingRecipeBuilder +import net.minecraft.data.recipes.FinishedRecipe +import net.minecraft.resources.ResourceLocation +import net.minecraft.tags.TagKey +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.crafting.CraftingBookCategory +import net.minecraft.world.item.crafting.Ingredient +import net.minecraft.world.item.crafting.RecipeSerializer +import net.minecraft.world.level.ItemLike +import org.jetbrains.annotations.Nullable + +import java.util.function.Consumer + +@CompileStatic +class GShapelessRecipeBuilder extends CraftingRecipeBuilder implements SimpleRecipeBuilder { + private final List ingredients = Lists.newArrayList() + + GShapelessRecipeBuilder requires(TagKey tag) { + this.requires(Ingredient.of(tag)) + } + + GShapelessRecipeBuilder requires(ItemLike item) { + this.requires(item, 1) + } + + GShapelessRecipeBuilder requires(ItemLike item, int quantity) { + for (int i = 0; i < quantity; ++i) { + this.requires(Ingredient.of(new ItemLike[]{item})) + } + return this + } + + GShapelessRecipeBuilder requires(Ingredient ingredient) { + return this.requires(ingredient, 1) + } + + GShapelessRecipeBuilder requires(Ingredient ingredient, int quantity) { + for (int i = 0; i < quantity; ++i) { + this.ingredients.add(ingredient) + } + return this + } + + void save(Consumer finishedRecipeConsumer, ResourceLocation recipeId) { + this.advancement.parent(ROOT_RECIPE_ADVANCEMENT).addCriterion("has_the_recipe", RecipeUnlockedTrigger.unlocked(recipeId)).rewards(net.minecraft.advancements.AdvancementRewards.Builder.recipe(recipeId)).requirements(RequirementsStrategy.OR); + finishedRecipeConsumer.accept(new Result(recipeId, this.resultStack, this.group == null ? "" : this.group, determineBookCategory(this.category), this.ingredients, this.advancement, recipeId.withPrefix("recipes/" + this.category.getFolderName() + "/"))); + } + + static class Result extends CraftingRecipeBuilder.CraftingResult { + private final ResourceLocation id; + private final ItemStack result + private final String group; + private final List ingredients; + private final Advancement.Builder advancement; + private final ResourceLocation advancementId; + + Result(ResourceLocation resourceLocation, ItemStack result, String string, CraftingBookCategory craftingBookCategory, List list, Advancement.Builder builder, ResourceLocation resourceLocation2) { + super(craftingBookCategory) + this.id = resourceLocation + this.result = result + this.group = string + this.ingredients = list + this.advancement = builder + this.advancementId = resourceLocation2 + } + + void serializeRecipeData(JsonObject json) { + super.serializeRecipeData(json) + if (!this.group.isEmpty()) { + json.addProperty('group', this.group) + } + + JsonArray jsonArray = new JsonArray() + this.ingredients.each { + jsonArray.add(it.toJson()) + } + + json.add('ingredients', jsonArray) + final jsonObject = new JsonObject() + jsonObject.addProperty('item', BuiltInRegistries.ITEM.getKey(this.result.item).toString()) + if (this.result.count > 1) { + jsonObject.addProperty('count', this.result.count) + } + + json.add('result', jsonObject) + } + + RecipeSerializer getType() { + return RecipeSerializer.SHAPELESS_RECIPE + } + + ResourceLocation getId() { + return this.id + } + + @Nullable + JsonObject serializeAdvancement() { + return this.advancement.criteria.size() === 1 ? null : this.advancement.serializeToJson() + } + + @Nullable + ResourceLocation getAdvancementId() { + return this.advancementId + } + } +} diff --git a/Forge/build.gradle b/Forge/build.gradle index 0fb456e..a5b350d 100644 --- a/Forge/build.gradle +++ b/Forge/build.gradle @@ -46,13 +46,13 @@ minecraft { data { ideaModule "${rootProject.name}.${project.name}.main" - args '--mod', mod_id, '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/') + args '--mod', mod_id, '--mod', 'cgltest', '--all', '--output', file('src/test/generated/resources/'), '--existing', file('src/main/resources/') taskName 'Data' } } } -sourceSets.main.resources.srcDir 'src/generated/resources' +sourceSets.main.resources.srcDir 'src/test/generated/resources' sourceSets { transform {} diff --git a/Forge/src/test/generated/resources/cgltest/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e b/Forge/src/test/generated/resources/cgltest/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e new file mode 100644 index 0000000..73fbc59 --- /dev/null +++ b/Forge/src/test/generated/resources/cgltest/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e @@ -0,0 +1,6 @@ +// 1.19.4 2023-04-06T19:07:45.0927493 Recipes +c77d782a99edd1c0996eb899f82184cc8d44f41a data/anothermod/advancements/recipes/decorations/yesyes.json +7087581372cb9ebf895e12bcc3eb4430609d6788 data/anothermod/recipes/yesyes.json +0c5b9caa42269c7d408273d712efad02abb504c0 data/cgltest/recipes/my_stairs.json +520af23a1b5c56b3529c20f7ee1083e55efaaa30 data/cgltest/recipes/sprucex12.json +792fac73fab43971d32004ce6fba4744ca787e95 data/minecraft/recipes/acacia_boat.json diff --git a/Forge/src/test/generated/resources/cgltest/data/anothermod/advancements/recipes/decorations/yesyes.json b/Forge/src/test/generated/resources/cgltest/data/anothermod/advancements/recipes/decorations/yesyes.json new file mode 100644 index 0000000..7e0bf98 --- /dev/null +++ b/Forge/src/test/generated/resources/cgltest/data/anothermod/advancements/recipes/decorations/yesyes.json @@ -0,0 +1,34 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_the_recipe": { + "conditions": { + "recipe": "anothermod:yesyes" + }, + "trigger": "minecraft:recipe_unlocked" + }, + "yes": { + "conditions": { + "items": [ + { + "items": [ + "minecraft:allium" + ] + } + ] + }, + "trigger": "minecraft:inventory_changed" + } + }, + "requirements": [ + [ + "yes", + "has_the_recipe" + ] + ], + "rewards": { + "recipes": [ + "anothermod:yesyes" + ] + } +} \ No newline at end of file diff --git a/Forge/src/test/generated/resources/cgltest/data/anothermod/recipes/yesyes.json b/Forge/src/test/generated/resources/cgltest/data/anothermod/recipes/yesyes.json new file mode 100644 index 0000000..ffd74c0 --- /dev/null +++ b/Forge/src/test/generated/resources/cgltest/data/anothermod/recipes/yesyes.json @@ -0,0 +1,10 @@ +{ + "type": "minecraft:blasting", + "category": "blocks", + "cookingtime": 2000, + "experience": 12.0, + "ingredient": { + "item": "minecraft:acacia_boat" + }, + "result": "minecraft:allium" +} \ No newline at end of file diff --git a/Forge/src/test/generated/resources/cgltest/data/cgltest/recipes/my_stairs.json b/Forge/src/test/generated/resources/cgltest/data/cgltest/recipes/my_stairs.json new file mode 100644 index 0000000..5076090 --- /dev/null +++ b/Forge/src/test/generated/resources/cgltest/data/cgltest/recipes/my_stairs.json @@ -0,0 +1,18 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "equipment", + "group": "yes", + "key": { + "A": { + "item": "minecraft:acacia_log" + } + }, + "pattern": [ + "AAA" + ], + "result": { + "count": 2, + "item": "minecraft:acacia_stairs" + }, + "show_notification": true +} \ No newline at end of file diff --git a/Forge/src/test/generated/resources/cgltest/data/cgltest/recipes/sprucex12.json b/Forge/src/test/generated/resources/cgltest/data/cgltest/recipes/sprucex12.json new file mode 100644 index 0000000..4e0b32e --- /dev/null +++ b/Forge/src/test/generated/resources/cgltest/data/cgltest/recipes/sprucex12.json @@ -0,0 +1,13 @@ +{ + "type": "minecraft:crafting_shapeless", + "category": "misc", + "ingredients": [ + { + "item": "minecraft:azalea" + } + ], + "result": { + "count": 12, + "item": "minecraft:spruce_button" + } +} \ No newline at end of file diff --git a/Forge/src/test/generated/resources/cgltest/data/minecraft/recipes/acacia_boat.json b/Forge/src/test/generated/resources/cgltest/data/minecraft/recipes/acacia_boat.json new file mode 100644 index 0000000..34d1d33 --- /dev/null +++ b/Forge/src/test/generated/resources/cgltest/data/minecraft/recipes/acacia_boat.json @@ -0,0 +1,13 @@ +{ + "type": "minecraft:crafting_shapeless", + "category": "redstone", + "group": "no", + "ingredients": [ + { + "tag": "minecraft:coal_ores" + } + ], + "result": { + "item": "minecraft:acacia_boat" + } +} \ No newline at end of file diff --git a/Forge/src/test/groovy/cgltest/Datagen.groovy b/Forge/src/test/groovy/cgltest/Datagen.groovy new file mode 100644 index 0000000..d808f29 --- /dev/null +++ b/Forge/src/test/groovy/cgltest/Datagen.groovy @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2022 GroovyMC and contributors + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + +package cgltest + +import com.matyrobbrt.gml.bus.EventBusSubscriber +import com.matyrobbrt.gml.bus.type.ModBus +import groovy.transform.CompileStatic +import groovy.transform.PackageScope +import io.github.groovymc.cgl.api.datagen.recipe.GRecipeProvider +import io.github.groovymc.cgl.api.datagen.recipe.GShapelessRecipeBuilder +import net.minecraft.data.PackOutput +import net.minecraft.data.recipes.RecipeCategory +import net.minecraft.data.recipes.SimpleCookingRecipeBuilder +import net.minecraft.resources.ResourceLocation +import net.minecraft.tags.ItemTags +import net.minecraft.world.item.Items +import net.minecraftforge.data.event.GatherDataEvent +import net.minecraftforge.eventbus.api.SubscribeEvent + +@CompileStatic +@EventBusSubscriber(ModBus) +class Datagen extends GRecipeProvider { + @SubscribeEvent @PackageScope + static void onDatagen(final GatherDataEvent event) { + event.generator.addProvider(event.includeServer(), new Datagen(event.generator.packOutput)) + } + + Datagen(PackOutput packOutput) { + super(packOutput, 'cgltest') + } + + @Override + protected void buildRecipes() { + shaped { + result Items.ACACIA_STAIRS count 2 + category = RecipeCategory.COMBAT + group = 'yes' + + pattern 'AAA' + define 'A', Items.ACACIA_LOG + } save 'my_stairs' + + recipe(GShapelessRecipeBuilder) { + result Items.ACACIA_BOAT + category = RecipeCategory.REDSTONE + group = 'no' + + requires ItemTags.COAL_ORES + }.save() + + shapeless { + result Items.SPRUCE_BUTTON * 12 + requires Items.AZALEA + } save 'sprucex12' + + normal(SimpleCookingRecipeBuilder.blasting( + Items.ACACIA_BOAT.ingredient(), + RecipeCategory.DECORATIONS, + Items.ALLIUM, + 12f, + 2000 + )) { + unlockedBy('yes', has(Items.ALLIUM)) + } save new ResourceLocation('anothermod:yesyes') + } +} From 91722942345c240f60b3e802421a6e983c153669 Mon Sep 17 00:00:00 2001 From: Matyrobbrt Date: Thu, 6 Apr 2023 19:25:01 +0300 Subject: [PATCH 2/9] [noci] Update TC PR build type --- .teamcity/settings.kts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts index 89ba19c..7b0a993 100644 --- a/.teamcity/settings.kts +++ b/.teamcity/settings.kts @@ -85,6 +85,12 @@ object GroovyMC_CommonGroovyLibrary_PullRequests : BuildType({ root(DslContext.settingsRoot) } + triggers { + vcs { + branchFilter = "+:*" + } + } + features { swabra { filesCleanup = Swabra.FilesCleanup.BEFORE_BUILD From 568956a43ebeb9dd0a6e42544a1f5d1a927f55b5 Mon Sep 17 00:00:00 2001 From: Matyrobbrt Date: Thu, 6 Apr 2023 19:32:24 +0300 Subject: [PATCH 3/9] [noci] Don't run PR builds on the main repo, only on forks --- .teamcity/settings.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts index 7b0a993..1e83496 100644 --- a/.teamcity/settings.kts +++ b/.teamcity/settings.kts @@ -87,7 +87,7 @@ object GroovyMC_CommonGroovyLibrary_PullRequests : BuildType({ triggers { vcs { - branchFilter = "+:*" + branchFilter = "-refs/heads/*" } } From c660d85d760d16bbe2c3e11410b778099939098a Mon Sep 17 00:00:00 2001 From: Matyrobbrt Date: Thu, 6 Apr 2023 19:36:23 +0300 Subject: [PATCH 4/9] [noci] Revert last commit --- .teamcity/settings.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts index 1e83496..7b0a993 100644 --- a/.teamcity/settings.kts +++ b/.teamcity/settings.kts @@ -87,7 +87,7 @@ object GroovyMC_CommonGroovyLibrary_PullRequests : BuildType({ triggers { vcs { - branchFilter = "-refs/heads/*" + branchFilter = "+:*" } } From 255af8bc3a186c612cf1a3a1c389da2ae165001f Mon Sep 17 00:00:00 2001 From: Matyrobbrt Date: Thu, 13 Apr 2023 19:13:41 +0300 Subject: [PATCH 5/9] Add cooking recipes --- .../cgl/api/extension/ItemExtensions.groovy | 20 +++ .../recipe/GCookingRecipeBuilder.groovy | 147 ++++++++++++++++++ .../api/datagen/recipe/GRecipeProvider.groovy | 43 ++++- .../9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e | 4 +- .../data/minecraft/recipes/acacia_log.json | 10 ++ .../data/minecraft/recipes/iron_axe.json | 10 ++ Forge/src/test/groovy/cgltest/Datagen.groovy | 16 ++ 7 files changed, 247 insertions(+), 3 deletions(-) create mode 100644 Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GCookingRecipeBuilder.groovy create mode 100644 Forge/src/test/generated/resources/cgltest/data/minecraft/recipes/acacia_log.json create mode 100644 Forge/src/test/generated/resources/cgltest/data/minecraft/recipes/iron_axe.json diff --git a/Common/src/extension/groovy/io/github/groovymc/cgl/api/extension/ItemExtensions.groovy b/Common/src/extension/groovy/io/github/groovymc/cgl/api/extension/ItemExtensions.groovy index fad7589..2d906c9 100644 --- a/Common/src/extension/groovy/io/github/groovymc/cgl/api/extension/ItemExtensions.groovy +++ b/Common/src/extension/groovy/io/github/groovymc/cgl/api/extension/ItemExtensions.groovy @@ -6,12 +6,16 @@ package io.github.groovymc.cgl.api.extension import groovy.transform.CompileStatic +import net.minecraft.core.BlockPos +import net.minecraft.core.Position import net.minecraft.nbt.CompoundTag import net.minecraft.tags.TagKey import net.minecraft.world.item.Item import net.minecraft.world.item.ItemStack import net.minecraft.world.item.crafting.Ingredient import net.minecraft.world.level.ItemLike +import net.minecraft.world.phys.Vec3 +import org.codehaus.groovy.runtime.DefaultGroovyMethods @CompileStatic class ItemExtensions { @@ -49,4 +53,20 @@ class ItemExtensions { static Ingredient ingredient(TagKey tag) { return Ingredient.of(tag) } + + static T asType(ItemStack self, Class type) { + return switch (type) { + case Ingredient -> (T) ingredient(self.item) + case ItemLike -> (T) self.item + default -> (T) DefaultGroovyMethods.asType(self, type) + } + } + + static T asType(ItemLike self, Class type) { + return switch (type) { + case Ingredient -> (T) ingredient(self.asItem()) + case ItemStack -> (T) self.asItem().defaultInstance + default -> (T) DefaultGroovyMethods.asType(self, type) + } + } } diff --git a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GCookingRecipeBuilder.groovy b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GCookingRecipeBuilder.groovy new file mode 100644 index 0000000..1219cdd --- /dev/null +++ b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GCookingRecipeBuilder.groovy @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2022 GroovyMC and contributors + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + +package io.github.groovymc.cgl.api.datagen.recipe + +import com.google.gson.JsonObject +import groovy.contracts.Requires +import groovy.transform.CompileStatic +import net.minecraft.advancements.Advancement +import net.minecraft.advancements.AdvancementRewards +import net.minecraft.advancements.RequirementsStrategy +import net.minecraft.advancements.critereon.RecipeUnlockedTrigger +import net.minecraft.core.registries.BuiltInRegistries +import net.minecraft.data.recipes.FinishedRecipe +import net.minecraft.resources.ResourceLocation +import net.minecraft.world.item.BlockItem +import net.minecraft.world.item.Item +import net.minecraft.world.item.crafting.AbstractCookingRecipe +import net.minecraft.world.item.crafting.CookingBookCategory +import net.minecraft.world.item.crafting.Ingredient +import net.minecraft.world.item.crafting.RecipeSerializer +import net.minecraft.world.level.ItemLike +import org.jetbrains.annotations.Nullable + +import java.util.function.Consumer + +@CompileStatic +class GCookingRecipeBuilder implements SimpleRecipeBuilder { + private CookingBookCategory bookCategory + private Ingredient ingredient + private float experience = 20 + private int cookingTime = 200 + private RecipeSerializer serializer + + GCookingRecipeBuilder() { + this(RecipeSerializer.SMELTING_RECIPE) + } + + GCookingRecipeBuilder(RecipeSerializer serializer) { + this.serializer = serializer + } + + GCookingRecipeBuilder ingredient(Ingredient ingredient) { + return setIngredient(ingredient) + } + + GCookingRecipeBuilder setIngredient(Ingredient ingredient) { + this.ingredient = ingredient + return this + } + + GCookingRecipeBuilder setExperience(float exp) { + this.experience = exp + return this + } + + GCookingRecipeBuilder experience(float exp) { + return setExperience(exp) + } + + GCookingRecipeBuilder setCookingTime(int cookingTime) { + this.cookingTime = cookingTime + return this + } + + GCookingRecipeBuilder cookingTime(int cookingTime) { + return setCookingTime(cookingTime) + } + + @Requires({ ingredient && serializer && cookingTime > 0 }) + void save(Consumer finishedRecipeConsumer, ResourceLocation recipeId) { + this.advancement.parent(ROOT_RECIPE_ADVANCEMENT).addCriterion('has_the_recipe', RecipeUnlockedTrigger.unlocked(recipeId)).rewards(AdvancementRewards.Builder.recipe(recipeId)).requirements(RequirementsStrategy.OR); + finishedRecipeConsumer.accept(new Result(recipeId, this.group == null ? '' : this.group, this.bookCategory ?: determineRecipeCategory(this.serializer, this.result), this.ingredient, this.result, this.experience, this.cookingTime, this.advancement, recipeId.withPrefix("recipes/" + this.category.getFolderName() + "/"), this.serializer)); + } + + private static CookingBookCategory determineRecipeCategory(RecipeSerializer serializer, ItemLike result) { + if (serializer == RecipeSerializer.SMELTING_RECIPE) { + if (result.asItem().isEdible()) { + return CookingBookCategory.FOOD + } else { + return result.asItem() instanceof BlockItem ? CookingBookCategory.BLOCKS : CookingBookCategory.MISC + } + } else if (serializer == RecipeSerializer.BLASTING_RECIPE) { + return result.asItem() instanceof BlockItem ? CookingBookCategory.BLOCKS : CookingBookCategory.MISC + } else { + return CookingBookCategory.MISC + } + } + + static class Result implements FinishedRecipe { + private final ResourceLocation id + private final String group + private final CookingBookCategory category + private final Ingredient ingredient + private final Item result + private final float experience + private final int cookingTime + private final Advancement.Builder advancement + private final ResourceLocation advancementId + private final RecipeSerializer serializer + + Result(ResourceLocation resourceLocation, String string, CookingBookCategory cookingBookCategory, Ingredient ingredient, Item item, float exp, int time, Advancement.Builder builder, ResourceLocation resourceLocation2, RecipeSerializer recipeSerializer) { + this.id = resourceLocation + this.group = string + this.category = cookingBookCategory + this.ingredient = ingredient + this.result = item + this.experience = exp + this.cookingTime = time + this.advancement = builder + this.advancementId = resourceLocation2 + this.serializer = recipeSerializer + } + + void serializeRecipeData(JsonObject json) { + if (!this.group.isEmpty()) { + json.addProperty('group', this.group); + } + + json.addProperty('category', this.category.getSerializedName()) + json.add('ingredient', this.ingredient.toJson()) + json.addProperty('result', BuiltInRegistries.ITEM.getKey(this.result).toString()) + json.addProperty('experience', this.experience) + json.addProperty('cookingtime', this.cookingTime) + } + + RecipeSerializer getType() { + this.serializer + } + + ResourceLocation getId() { + this.id + } + + @Nullable + JsonObject serializeAdvancement() { + this.advancement.criteria.size() == 1 ? null : this.advancement.serializeToJson() + } + + @Nullable + ResourceLocation getAdvancementId() { + this.advancementId + } + } +} diff --git a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GRecipeProvider.groovy b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GRecipeProvider.groovy index ab43aa8..265e24d 100644 --- a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GRecipeProvider.groovy +++ b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GRecipeProvider.groovy @@ -9,11 +9,11 @@ import groovy.transform.CompileStatic import groovy.transform.stc.ClosureParams import groovy.transform.stc.FirstParam import groovy.transform.stc.SimpleType +import groovy.util.logging.Slf4j import net.minecraft.advancements.Advancement import net.minecraft.advancements.CriterionTriggerInstance import net.minecraft.core.registries.BuiltInRegistries import net.minecraft.data.PackOutput -import net.minecraft.data.recipes.CraftingRecipeBuilder import net.minecraft.data.recipes.FinishedRecipe import net.minecraft.data.recipes.RecipeBuilder import net.minecraft.data.recipes.RecipeCategory @@ -21,16 +21,19 @@ import net.minecraft.data.recipes.RecipeProvider import net.minecraft.resources.ResourceLocation import net.minecraft.world.item.Item import net.minecraft.world.item.ItemStack -import net.minecraft.world.item.crafting.CraftingBookCategory +import net.minecraft.world.item.crafting.RecipeSerializer import net.minecraft.world.level.ItemLike import org.jetbrains.annotations.Nullable import java.util.function.Consumer +@Slf4j @CompileStatic abstract class GRecipeProvider extends RecipeProvider { public Consumer writer protected final String defaultNamespace + + protected final List forgottenRecipes = [] GRecipeProvider(PackOutput packOutput, String defaultNamespace = 'minecraft') { super(packOutput) this.defaultNamespace = defaultNamespace @@ -41,10 +44,43 @@ abstract class GRecipeProvider extends RecipeProvider { this.writer = writer this.buildRecipes() this.writer = null + + if (!forgottenRecipes.isEmpty()) { + log.error("It seems like ${forgottenRecipes.size()} recipes created in provider ${this} have not been saved. You may save them automatically by calling GRecipeProvider#saveForgotten in buildRecipes().") + throw new RuntimeException("${forgottenRecipes.size()} recipes have not been saved!") + } } protected abstract void buildRecipes() + /** + * Saves all forgotten recipes of type {@link BaseRecipeBuilder}.
+ * Note that this means the recipes will be saved to a path representing the result's registry name. + */ + protected void saveForgotten() { + List.copyOf(this.forgottenRecipes).each { + if (it instanceof BaseRecipeBuilder) { + it.save() + } + } + } + + protected GCookingRecipeBuilder smelting(@DelegatesTo(value = GCookingRecipeBuilder, strategy = Closure.DELEGATE_FIRST) @ClosureParams(value = SimpleType, options = 'io.github.groovymc.cgl.api.datagen.GCookingRecipeBuilder') Closure clos) { + recipe(new GCookingRecipeBuilder(), clos) + } + + protected GCookingRecipeBuilder blasting(@DelegatesTo(value = GCookingRecipeBuilder, strategy = Closure.DELEGATE_FIRST) @ClosureParams(value = SimpleType, options = 'io.github.groovymc.cgl.api.datagen.GCookingRecipeBuilder') Closure clos) { + recipe(new GCookingRecipeBuilder(RecipeSerializer.BLASTING_RECIPE), clos) + } + + protected GCookingRecipeBuilder campfireCooking(@DelegatesTo(value = GCookingRecipeBuilder, strategy = Closure.DELEGATE_FIRST) @ClosureParams(value = SimpleType, options = 'io.github.groovymc.cgl.api.datagen.GCookingRecipeBuilder') Closure clos) { + recipe(new GCookingRecipeBuilder(RecipeSerializer.CAMPFIRE_COOKING_RECIPE), clos) + } + + protected GCookingRecipeBuilder smoking(@DelegatesTo(value = GCookingRecipeBuilder, strategy = Closure.DELEGATE_FIRST) @ClosureParams(value = SimpleType, options = 'io.github.groovymc.cgl.api.datagen.GCookingRecipeBuilder') Closure clos) { + recipe(new GCookingRecipeBuilder(RecipeSerializer.SMOKING_RECIPE), clos) + } + protected GShapedRecipeBuilder shaped(@DelegatesTo(value = GShapedRecipeBuilder, strategy = Closure.DELEGATE_FIRST) @ClosureParams(value = SimpleType, options = 'io.github.groovymc.cgl.api.datagen.GShapedRecipeBuilder') Closure clos) { recipe(new GShapedRecipeBuilder(), clos) } @@ -62,6 +98,7 @@ abstract class GRecipeProvider extends RecipeProvider { clos.resolveStrategy = Closure.DELEGATE_FIRST clos.delegate = recipe clos(recipe) + forgottenRecipes.add(recipe) return recipe } @@ -72,6 +109,7 @@ abstract class GRecipeProvider extends RecipeProvider { return new SaveableRecipe() { @Override void save(ResourceLocation location) { + forgottenRecipes.remove(this) recipe.save(getProvider().writer, location) } @@ -126,6 +164,7 @@ trait BaseRecipeBuilder extends SaveableRecipe implements RecipeBuilder { } void save(ResourceLocation location) { + provider.forgottenRecipes.remove(this) this.save(this.getProvider().writer, location) } } diff --git a/Forge/src/test/generated/resources/cgltest/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e b/Forge/src/test/generated/resources/cgltest/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e index 73fbc59..fe8259b 100644 --- a/Forge/src/test/generated/resources/cgltest/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e +++ b/Forge/src/test/generated/resources/cgltest/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e @@ -1,6 +1,8 @@ -// 1.19.4 2023-04-06T19:07:45.0927493 Recipes +// 1.19.4 2023-04-13T19:10:59.0862563 Recipes c77d782a99edd1c0996eb899f82184cc8d44f41a data/anothermod/advancements/recipes/decorations/yesyes.json 7087581372cb9ebf895e12bcc3eb4430609d6788 data/anothermod/recipes/yesyes.json 0c5b9caa42269c7d408273d712efad02abb504c0 data/cgltest/recipes/my_stairs.json 520af23a1b5c56b3529c20f7ee1083e55efaaa30 data/cgltest/recipes/sprucex12.json 792fac73fab43971d32004ce6fba4744ca787e95 data/minecraft/recipes/acacia_boat.json +018c2e8a055e1428adf76f0d21688abdc6bd3566 data/minecraft/recipes/acacia_log.json +eeb92eab7ddaa17ae0e0ffecf34ec5c121612837 data/minecraft/recipes/iron_axe.json diff --git a/Forge/src/test/generated/resources/cgltest/data/minecraft/recipes/acacia_log.json b/Forge/src/test/generated/resources/cgltest/data/minecraft/recipes/acacia_log.json new file mode 100644 index 0000000..89274d5 --- /dev/null +++ b/Forge/src/test/generated/resources/cgltest/data/minecraft/recipes/acacia_log.json @@ -0,0 +1,10 @@ +{ + "type": "minecraft:campfire_cooking", + "category": "misc", + "cookingtime": 200, + "experience": 20.0, + "ingredient": { + "item": "minecraft:azalea" + }, + "result": "minecraft:acacia_log" +} \ No newline at end of file diff --git a/Forge/src/test/generated/resources/cgltest/data/minecraft/recipes/iron_axe.json b/Forge/src/test/generated/resources/cgltest/data/minecraft/recipes/iron_axe.json new file mode 100644 index 0000000..9939304 --- /dev/null +++ b/Forge/src/test/generated/resources/cgltest/data/minecraft/recipes/iron_axe.json @@ -0,0 +1,10 @@ +{ + "type": "minecraft:smelting", + "category": "misc", + "cookingtime": 150, + "experience": 4.0, + "ingredient": { + "item": "minecraft:bamboo" + }, + "result": "minecraft:iron_axe" +} \ No newline at end of file diff --git a/Forge/src/test/groovy/cgltest/Datagen.groovy b/Forge/src/test/groovy/cgltest/Datagen.groovy index d808f29..94a409e 100644 --- a/Forge/src/test/groovy/cgltest/Datagen.groovy +++ b/Forge/src/test/groovy/cgltest/Datagen.groovy @@ -17,6 +17,7 @@ import net.minecraft.data.recipes.SimpleCookingRecipeBuilder import net.minecraft.resources.ResourceLocation import net.minecraft.tags.ItemTags import net.minecraft.world.item.Items +import net.minecraft.world.item.crafting.Ingredient import net.minecraftforge.data.event.GatherDataEvent import net.minecraftforge.eventbus.api.SubscribeEvent @@ -65,5 +66,20 @@ class Datagen extends GRecipeProvider { )) { unlockedBy('yes', has(Items.ALLIUM)) } save new ResourceLocation('anothermod:yesyes') + + campfireCooking { + result Items.ACACIA_LOG + ingredient Items.AZALEA as Ingredient + category RecipeCategory.BREWING + } + + smelting { + result Items.IRON_AXE + ingredient Items.BAMBOO.ingredient() + cookingTime 150 + experience 4f + } + + saveForgotten() } } From b096efe64548f6bc420de95b6a0c1c15a1916acc Mon Sep 17 00:00:00 2001 From: Matyrobbrt Date: Thu, 13 Apr 2023 19:24:17 +0300 Subject: [PATCH 6/9] SingleItemRecipeBuilder --- .../recipe/GCookingRecipeBuilder.groovy | 11 +- .../api/datagen/recipe/GRecipeProvider.groovy | 8 ++ .../recipe/GShapedRecipeBuilder.groovy | 2 +- .../recipe/GShapelessRecipeBuilder.groovy | 2 +- .../recipe/GSingleItemRecipeBuilder.groovy | 107 ++++++++++++++++++ .../9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e | 3 +- .../cgltest/recipes/test_stonecutting.json | 8 ++ Forge/src/test/groovy/cgltest/Datagen.groovy | 5 + 8 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GSingleItemRecipeBuilder.groovy create mode 100644 Forge/src/test/generated/resources/cgltest/data/cgltest/recipes/test_stonecutting.json diff --git a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GCookingRecipeBuilder.groovy b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GCookingRecipeBuilder.groovy index 1219cdd..1598fa9 100644 --- a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GCookingRecipeBuilder.groovy +++ b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GCookingRecipeBuilder.groovy @@ -42,6 +42,15 @@ class GCookingRecipeBuilder implements SimpleRecipeBuilder serializer) { + return setSerializer(serializer) + } + + GCookingRecipeBuilder setSerializer(RecipeSerializer serializer) { + this.serializer = serializer + return this + } + GCookingRecipeBuilder ingredient(Ingredient ingredient) { return setIngredient(ingredient) } @@ -72,7 +81,7 @@ class GCookingRecipeBuilder implements SimpleRecipeBuilder 0 }) void save(Consumer finishedRecipeConsumer, ResourceLocation recipeId) { this.advancement.parent(ROOT_RECIPE_ADVANCEMENT).addCriterion('has_the_recipe', RecipeUnlockedTrigger.unlocked(recipeId)).rewards(AdvancementRewards.Builder.recipe(recipeId)).requirements(RequirementsStrategy.OR); - finishedRecipeConsumer.accept(new Result(recipeId, this.group == null ? '' : this.group, this.bookCategory ?: determineRecipeCategory(this.serializer, this.result), this.ingredient, this.result, this.experience, this.cookingTime, this.advancement, recipeId.withPrefix("recipes/" + this.category.getFolderName() + "/"), this.serializer)); + finishedRecipeConsumer.accept(new Result(recipeId, this.group ?: '', this.bookCategory ?: determineRecipeCategory(this.serializer, this.result), this.ingredient, this.result, this.experience, this.cookingTime, this.advancement, recipeId.withPrefix("recipes/" + this.category.getFolderName() + "/"), this.serializer)); } private static CookingBookCategory determineRecipeCategory(RecipeSerializer serializer, ItemLike result) { diff --git a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GRecipeProvider.groovy b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GRecipeProvider.groovy index 265e24d..363790a 100644 --- a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GRecipeProvider.groovy +++ b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GRecipeProvider.groovy @@ -65,6 +65,14 @@ abstract class GRecipeProvider extends RecipeProvider { } } + protected GSingleItemRecipeBuilder stonecutting(@DelegatesTo(value = GSingleItemRecipeBuilder, strategy = Closure.DELEGATE_FIRST) @ClosureParams(value = SimpleType, options = 'io.github.groovymc.cgl.api.datagen.GSingleItemRecipeBuilder') Closure clos) { + recipe(new GSingleItemRecipeBuilder(RecipeSerializer.STONECUTTER), clos) + } + + protected GSingleItemRecipeBuilder singleItem(@DelegatesTo(value = GSingleItemRecipeBuilder, strategy = Closure.DELEGATE_FIRST) @ClosureParams(value = SimpleType, options = 'io.github.groovymc.cgl.api.datagen.GSingleItemRecipeBuilder') Closure clos) { + recipe(new GSingleItemRecipeBuilder(), clos) + } + protected GCookingRecipeBuilder smelting(@DelegatesTo(value = GCookingRecipeBuilder, strategy = Closure.DELEGATE_FIRST) @ClosureParams(value = SimpleType, options = 'io.github.groovymc.cgl.api.datagen.GCookingRecipeBuilder') Closure clos) { recipe(new GCookingRecipeBuilder(), clos) } diff --git a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GShapedRecipeBuilder.groovy b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GShapedRecipeBuilder.groovy index bc5dd20..b30d104 100644 --- a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GShapedRecipeBuilder.groovy +++ b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GShapedRecipeBuilder.groovy @@ -79,7 +79,7 @@ class GShapedRecipeBuilder extends CraftingRecipeBuilder implements SimpleRecipe void save(Consumer finishedRecipeConsumer, ResourceLocation recipeId) { this.ensureValid(recipeId) this.advancement.parent(ROOT_RECIPE_ADVANCEMENT).addCriterion("has_the_recipe", RecipeUnlockedTrigger.unlocked(recipeId)).rewards(AdvancementRewards.Builder.recipe(recipeId)).requirements(RequirementsStrategy.OR) - finishedRecipeConsumer.accept(new Result(recipeId, this.resultStack, this.group == null ? "" : this.group, determineBookCategory(this.category), this.rows, this.key, this.advancement, recipeId.withPrefix("recipes/" + this.category.getFolderName() + "/"), this.showNotification)) + finishedRecipeConsumer.accept(new Result(recipeId, this.resultStack, this.group ?: '', determineBookCategory(this.category), this.rows, this.key, this.advancement, recipeId.withPrefix("recipes/" + this.category.getFolderName() + "/"), this.showNotification)) } private void ensureValid(ResourceLocation id) { diff --git a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GShapelessRecipeBuilder.groovy b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GShapelessRecipeBuilder.groovy index 336a010..de37239 100644 --- a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GShapelessRecipeBuilder.groovy +++ b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GShapelessRecipeBuilder.groovy @@ -59,7 +59,7 @@ class GShapelessRecipeBuilder extends CraftingRecipeBuilder implements SimpleRec void save(Consumer finishedRecipeConsumer, ResourceLocation recipeId) { this.advancement.parent(ROOT_RECIPE_ADVANCEMENT).addCriterion("has_the_recipe", RecipeUnlockedTrigger.unlocked(recipeId)).rewards(net.minecraft.advancements.AdvancementRewards.Builder.recipe(recipeId)).requirements(RequirementsStrategy.OR); - finishedRecipeConsumer.accept(new Result(recipeId, this.resultStack, this.group == null ? "" : this.group, determineBookCategory(this.category), this.ingredients, this.advancement, recipeId.withPrefix("recipes/" + this.category.getFolderName() + "/"))); + finishedRecipeConsumer.accept(new Result(recipeId, this.resultStack, this.group ?: '', determineBookCategory(this.category), this.ingredients, this.advancement, recipeId.withPrefix("recipes/" + this.category.getFolderName() + "/"))); } static class Result extends CraftingRecipeBuilder.CraftingResult { diff --git a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GSingleItemRecipeBuilder.groovy b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GSingleItemRecipeBuilder.groovy new file mode 100644 index 0000000..b622d69 --- /dev/null +++ b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GSingleItemRecipeBuilder.groovy @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2022 GroovyMC and contributors + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + +package io.github.groovymc.cgl.api.datagen.recipe + +import com.google.gson.JsonObject +import groovy.contracts.Requires +import groovy.transform.CompileStatic +import net.minecraft.advancements.Advancement +import net.minecraft.advancements.AdvancementRewards +import net.minecraft.advancements.RequirementsStrategy +import net.minecraft.advancements.critereon.RecipeUnlockedTrigger +import net.minecraft.core.registries.BuiltInRegistries +import net.minecraft.data.recipes.FinishedRecipe +import net.minecraft.resources.ResourceLocation +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.crafting.Ingredient +import net.minecraft.world.item.crafting.RecipeSerializer +import org.jetbrains.annotations.Nullable + +import java.util.function.Consumer + +@CompileStatic +class GSingleItemRecipeBuilder implements SimpleRecipeBuilder { + private Ingredient ingredient + private RecipeSerializer type + + GSingleItemRecipeBuilder() {} + + GSingleItemRecipeBuilder(RecipeSerializer type) { + setType(type) + } + + GSingleItemRecipeBuilder ingredient(Ingredient ingredient) { + return setIngredient(ingredient) + } + + GSingleItemRecipeBuilder setIngredient(Ingredient ingredient) { + this.ingredient = ingredient + return this + } + + GSingleItemRecipeBuilder type(RecipeSerializer type) { + return setType(type) + } + + GSingleItemRecipeBuilder setType(RecipeSerializer type) { + this.type = type + return this + } + + @Requires({ this.ingredient && this.type }) + void save(Consumer finishedRecipeConsumer, ResourceLocation recipeId) { + this.advancement.parent(ROOT_RECIPE_ADVANCEMENT).addCriterion("has_the_recipe", RecipeUnlockedTrigger.unlocked(recipeId)).rewards(AdvancementRewards.Builder.recipe(recipeId)).requirements(RequirementsStrategy.OR) + finishedRecipeConsumer.accept(new Result(recipeId, this.type, this.group ?: '', this.ingredient, this.resultStack, this.advancement, recipeId.withPrefix("recipes/" + this.category.getFolderName() + "/"))) + } + + static class Result implements FinishedRecipe { + private final ResourceLocation id + private final String group + private final Ingredient ingredient + private final ItemStack result + private final Advancement.Builder advancement + private final ResourceLocation advancementId + private final RecipeSerializer type + + Result(ResourceLocation resourceLocation, RecipeSerializer recipeSerializer, String group, Ingredient ingredient, ItemStack result, Advancement.Builder builder, ResourceLocation advId) { + this.id = resourceLocation + this.type = recipeSerializer + this.group = group + this.ingredient = ingredient + this.result = result + this.advancement = builder + this.advancementId = advId + } + + void serializeRecipeData(JsonObject json) { + if (!this.group.isEmpty()) { + json.addProperty('group', this.group) + } + + json.add('ingredient', this.ingredient.toJson()) + json.addProperty('result', BuiltInRegistries.ITEM.getKey(this.result.item).toString()) + json.addProperty("count", this.result.count) + } + + ResourceLocation getId() { + this.id + } + + RecipeSerializer getType() { + this.type + } + + @Nullable + JsonObject serializeAdvancement() { + this.advancement.criteria.size() == 1 ? null : this.advancement.serializeToJson() + } + + @Nullable + ResourceLocation getAdvancementId() { + this.advancementId + } + } +} diff --git a/Forge/src/test/generated/resources/cgltest/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e b/Forge/src/test/generated/resources/cgltest/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e index fe8259b..2cfc385 100644 --- a/Forge/src/test/generated/resources/cgltest/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e +++ b/Forge/src/test/generated/resources/cgltest/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e @@ -1,8 +1,9 @@ -// 1.19.4 2023-04-13T19:10:59.0862563 Recipes +// 1.19.4 2023-04-13T19:23:58.4387268 Recipes c77d782a99edd1c0996eb899f82184cc8d44f41a data/anothermod/advancements/recipes/decorations/yesyes.json 7087581372cb9ebf895e12bcc3eb4430609d6788 data/anothermod/recipes/yesyes.json 0c5b9caa42269c7d408273d712efad02abb504c0 data/cgltest/recipes/my_stairs.json 520af23a1b5c56b3529c20f7ee1083e55efaaa30 data/cgltest/recipes/sprucex12.json +ac80faac08adba0a3780519b5ce69a0602b682fc data/cgltest/recipes/test_stonecutting.json 792fac73fab43971d32004ce6fba4744ca787e95 data/minecraft/recipes/acacia_boat.json 018c2e8a055e1428adf76f0d21688abdc6bd3566 data/minecraft/recipes/acacia_log.json eeb92eab7ddaa17ae0e0ffecf34ec5c121612837 data/minecraft/recipes/iron_axe.json diff --git a/Forge/src/test/generated/resources/cgltest/data/cgltest/recipes/test_stonecutting.json b/Forge/src/test/generated/resources/cgltest/data/cgltest/recipes/test_stonecutting.json new file mode 100644 index 0000000..a23ccd2 --- /dev/null +++ b/Forge/src/test/generated/resources/cgltest/data/cgltest/recipes/test_stonecutting.json @@ -0,0 +1,8 @@ +{ + "type": "minecraft:stonecutting", + "count": 1, + "ingredient": { + "item": "minecraft:pink_candle" + }, + "result": "minecraft:end_stone_brick_slab" +} \ No newline at end of file diff --git a/Forge/src/test/groovy/cgltest/Datagen.groovy b/Forge/src/test/groovy/cgltest/Datagen.groovy index 94a409e..fa53e24 100644 --- a/Forge/src/test/groovy/cgltest/Datagen.groovy +++ b/Forge/src/test/groovy/cgltest/Datagen.groovy @@ -80,6 +80,11 @@ class Datagen extends GRecipeProvider { experience 4f } + stonecutting { + result Items.END_STONE_BRICK_SLAB + ingredient Items.PINK_CANDLE.ingredient() + } save 'test_stonecutting' + saveForgotten() } } From fe15c1ca7e2eb223b08f2958fd6e80e3b78c381e Mon Sep 17 00:00:00 2001 From: Matyrobbrt Date: Thu, 13 Apr 2023 19:36:50 +0300 Subject: [PATCH 7/9] Some docs --- .../api/datagen/recipe/GRecipeProvider.groovy | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GRecipeProvider.groovy b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GRecipeProvider.groovy index 363790a..abfdcc1 100644 --- a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GRecipeProvider.groovy +++ b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GRecipeProvider.groovy @@ -27,6 +27,23 @@ import org.jetbrains.annotations.Nullable import java.util.function.Consumer +/** + * A {@link RecipeProvider} with a few additions that better support Groovy.
+ * All the G-recipe builders do not require an advancement criterion, unlike the vanilla recipe builders.
+ * They are designed to be used along side closure and parenthesis-less calls to make them easier to both read and write.
+ * An example of how a {@link GShapelessRecipeBuilder} is inteded to be used: + *
+ * {@code
+ * shapeless {
+ *      result Items.SPRUCE_BUTTON * 12
+ *      requires Items.AZALEA
+ *
+ *      category RecipeCategory.DECORATIONS
+ *      group 'spruce_stuff'
+ * } save 'sprucex12'
+ * }
+ * 
+ */ @Slf4j @CompileStatic abstract class GRecipeProvider extends RecipeProvider { @@ -34,6 +51,10 @@ abstract class GRecipeProvider extends RecipeProvider { protected final String defaultNamespace protected final List forgottenRecipes = [] + + /** + * @param defaultNamespace the default namespace to be used by {@link SaveableRecipe#save(java.lang.String)} + */ GRecipeProvider(PackOutput packOutput, String defaultNamespace = 'minecraft') { super(packOutput) this.defaultNamespace = defaultNamespace @@ -51,6 +72,11 @@ abstract class GRecipeProvider extends RecipeProvider { } } + /** + * Override this method to build your recipes.
+ * You may use the {@link #writer} as a {@code Consumer}, but it is not usually necessary as there are methods such as {@link #normal(net.minecraft.data.recipes.RecipeBuilder)} + * which allow you to more easily save recipes. + */ protected abstract void buildRecipes() /** @@ -138,10 +164,16 @@ abstract class GRecipeProvider extends RecipeProvider { trait SaveableRecipe { GRecipeProvider provider + /** + * Saves this recipe to the given {@code location}, using the provider's {@link GRecipeProvider#defaultNamespace default namespace}. + */ void save(String location) { this.save(location.contains(':') ? new ResourceLocation(location) : new ResourceLocation(provider.defaultNamespace, location)) } + /** + * Saves this recipe to the given {@code location}. + */ abstract void save(ResourceLocation location) } @@ -167,10 +199,16 @@ trait BaseRecipeBuilder extends SaveableRecipe implements RecipeBuilder { return this.@result } + /** + * Saves this recipe to a location representing the {@link #getResult() result's} registry name. + */ void save() { this.save(BuiltInRegistries.ITEM.getKey(getResult())) } + /** + * {@inheritDoc} + */ void save(ResourceLocation location) { provider.forgottenRecipes.remove(this) this.save(this.getProvider().writer, location) From 8d7763e7115bdbd8f29cc2b2192ac272301449af Mon Sep 17 00:00:00 2001 From: Matyrobbrt Date: Wed, 3 May 2023 17:34:25 +0300 Subject: [PATCH 8/9] Apply suggestions from code review --- .../cgl/api/extension/ItemExtensions.groovy | 4 +- .../datagen/recipe/BaseRecipeBuilder.groovy | 60 +++++++++++ .../recipe/GCookingRecipeBuilder.groovy | 18 ++-- .../api/datagen/recipe/GRecipeProvider.groovy | 102 +----------------- .../recipe/GShapedRecipeBuilder.groovy | 9 +- .../recipe/GSingleItemRecipeBuilder.groovy | 12 +-- .../api/datagen/recipe/SaveableRecipe.groovy | 26 +++++ .../datagen/recipe/SimpleRecipeBuilder.groovy | 53 +++++++++ 8 files changed, 166 insertions(+), 118 deletions(-) create mode 100644 Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/BaseRecipeBuilder.groovy create mode 100644 Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/SaveableRecipe.groovy create mode 100644 Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/SimpleRecipeBuilder.groovy diff --git a/Common/src/extension/groovy/io/github/groovymc/cgl/api/extension/ItemExtensions.groovy b/Common/src/extension/groovy/io/github/groovymc/cgl/api/extension/ItemExtensions.groovy index 2d906c9..ae9c865 100644 --- a/Common/src/extension/groovy/io/github/groovymc/cgl/api/extension/ItemExtensions.groovy +++ b/Common/src/extension/groovy/io/github/groovymc/cgl/api/extension/ItemExtensions.groovy @@ -7,6 +7,7 @@ package io.github.groovymc.cgl.api.extension import groovy.transform.CompileStatic import net.minecraft.core.BlockPos +import net.minecraft.core.Holder import net.minecraft.core.Position import net.minecraft.nbt.CompoundTag import net.minecraft.tags.TagKey @@ -57,7 +58,8 @@ class ItemExtensions { static T asType(ItemStack self, Class type) { return switch (type) { case Ingredient -> (T) ingredient(self.item) - case ItemLike -> (T) self.item + case ItemLike, Item -> (T) self.item + case Holder -> (T) self.itemHolder default -> (T) DefaultGroovyMethods.asType(self, type) } } diff --git a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/BaseRecipeBuilder.groovy b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/BaseRecipeBuilder.groovy new file mode 100644 index 0000000..0504e2a --- /dev/null +++ b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/BaseRecipeBuilder.groovy @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2022 GroovyMC and contributors + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + +package io.github.groovymc.cgl.api.datagen.recipe + +import groovy.transform.CompileStatic +import net.minecraft.core.registries.BuiltInRegistries +import net.minecraft.data.recipes.RecipeBuilder +import net.minecraft.resources.ResourceLocation +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack +import net.minecraft.world.level.ItemLike + +@CompileStatic +trait BaseRecipeBuilder extends SaveableRecipe implements RecipeBuilder { + ItemStack result + + ItemStack result(ItemLike result) { + this.result = result.asItem().getDefaultInstance() + return this.result + } + + void setResult(ItemLike result) { + this.result(result) + } + + ItemStack result(ItemStack result) { + this.result = result + return this.result + } + + void setResult(ItemStack result) { + this.result(result) + } + + Item getResult() { + return this.@result.item + } + + ItemStack getResultStack() { + return this.@result + } + + /** + * Saves this recipe to a location representing the {@link #getResult() result's} registry name. + */ + void save() { + this.save(BuiltInRegistries.ITEM.getKey(getResult())) + } + + /** + * {@inheritDoc} + */ + void save(ResourceLocation location) { + provider.forgottenRecipes.remove(this) + this.save(this.getProvider().writer, location) + } +} diff --git a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GCookingRecipeBuilder.groovy b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GCookingRecipeBuilder.groovy index 1598fa9..0c6740b 100644 --- a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GCookingRecipeBuilder.groovy +++ b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GCookingRecipeBuilder.groovy @@ -52,30 +52,30 @@ class GCookingRecipeBuilder implements SimpleRecipeBuilder 0 }) diff --git a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GRecipeProvider.groovy b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GRecipeProvider.groovy index abfdcc1..5a05d67 100644 --- a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GRecipeProvider.groovy +++ b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GRecipeProvider.groovy @@ -28,9 +28,9 @@ import org.jetbrains.annotations.Nullable import java.util.function.Consumer /** - * A {@link RecipeProvider} with a few additions that better support Groovy.
- * All the G-recipe builders do not require an advancement criterion, unlike the vanilla recipe builders.
- * They are designed to be used along side closure and parenthesis-less calls to make them easier to both read and write.
+ *

A {@link RecipeProvider} with a few additions that better support Groovy.

+ *

All the G-recipe builders do not require an advancement criterion, unlike the vanilla recipe builders.

+ *

They are designed to be used along side closure and parenthesis-less calls to make them easier to both read and write.

* An example of how a {@link GShapelessRecipeBuilder} is inteded to be used: *
  * {@code
@@ -158,100 +158,4 @@ abstract class GRecipeProvider extends RecipeProvider {
             }
         }
     }
-}
-
-@CompileStatic
-trait SaveableRecipe {
-    GRecipeProvider provider
-
-    /**
-     * Saves this recipe to the given {@code location}, using the provider's {@link GRecipeProvider#defaultNamespace  default namespace}.
-     */
-    void save(String location) {
-        this.save(location.contains(':') ? new ResourceLocation(location) : new ResourceLocation(provider.defaultNamespace, location))
-    }
-
-    /**
-     * Saves this recipe to the given {@code location}.
-     */
-    abstract void save(ResourceLocation location)
-}
-
-@CompileStatic
-trait BaseRecipeBuilder extends SaveableRecipe implements RecipeBuilder {
-    ItemStack result
-
-    ItemStack result(ItemLike result) {
-        this.result = result.asItem().getDefaultInstance()
-        return this.result
-    }
-
-    ItemStack result(ItemStack result) {
-        this.result = result
-        return this.result
-    }
-
-    Item getResult() {
-        return this.@result.item
-    }
-
-    ItemStack getResultStack() {
-        return this.@result
-    }
-
-    /**
-     * Saves this recipe to a location representing the {@link #getResult() result's} registry name.
-     */
-    void save() {
-        this.save(BuiltInRegistries.ITEM.getKey(getResult()))
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    void save(ResourceLocation location) {
-        provider.forgottenRecipes.remove(this)
-        this.save(this.getProvider().writer, location)
-    }
-}
-
-@CompileStatic
-trait SimpleRecipeBuilder extends BaseRecipeBuilder {
-    private RecipeCategory category = RecipeCategory.MISC
-    @Nullable
-    private String group
-    private final Advancement.Builder advancement = Advancement.Builder.advancement()
-
-    RecipeCategory getCategory() {
-        return this.@category
-    }
-    T setCategory(RecipeCategory category) {
-        this.@category = category
-        return (T) this
-    }
-
-    T category(RecipeCategory category) {
-        return setCategory(category)
-    }
-
-    String getGroup() {
-        return this.@group
-    }
-    T setGroup(String group) {
-        this.@group = group
-        return (T) this
-    }
-
-    T group(String group) {
-        return setGroup(group)
-    }
-
-    T unlockedBy(String criterionName, CriterionTriggerInstance criterionTrigger) {
-        this.@advancement.addCriterion(criterionName, criterionTrigger)
-        return (T) this
-    }
-
-    Advancement.Builder getAdvancement() {
-        return this.@advancement
-    }
 }
\ No newline at end of file
diff --git a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GShapedRecipeBuilder.groovy b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GShapedRecipeBuilder.groovy
index b30d104..774d1b0 100644
--- a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GShapedRecipeBuilder.groovy
+++ b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GShapedRecipeBuilder.groovy
@@ -55,6 +55,10 @@ class GShapedRecipeBuilder extends CraftingRecipeBuilder implements SimpleRecipe
         }
     }
 
+    GShapedRecipeBuilder pattern(String pattern) {
+        this.pattern(pattern.stripIndent().split('\n'))
+    }
+
     GShapedRecipeBuilder pattern(String... patterns) {
         patterns.each { pattern ->
             if (!this.rows.isEmpty() && pattern.length() !== this.rows[0].length()) {
@@ -67,13 +71,12 @@ class GShapedRecipeBuilder extends CraftingRecipeBuilder implements SimpleRecipe
     }
 
     GShapedRecipeBuilder showNotification(boolean showNotification) {
-        this.@showNotification = showNotification
+        this.setShowNotification(showNotification)
         return this
     }
 
-    GShapedRecipeBuilder setShowNotification(boolean showNotification) {
+    void setShowNotification(boolean showNotification) {
         this.@showNotification = showNotification
-        return this
     }
 
     void save(Consumer finishedRecipeConsumer, ResourceLocation recipeId) {
diff --git a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GSingleItemRecipeBuilder.groovy b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GSingleItemRecipeBuilder.groovy
index b622d69..e624c1a 100644
--- a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GSingleItemRecipeBuilder.groovy
+++ b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GSingleItemRecipeBuilder.groovy
@@ -34,21 +34,21 @@ class GSingleItemRecipeBuilder implements SimpleRecipeBuilder extends BaseRecipeBuilder {
+    private RecipeCategory category = RecipeCategory.MISC
+    @Nullable
+    private String group
+    private final Advancement.Builder advancement = Advancement.Builder.advancement()
+
+    RecipeCategory getCategory() {
+        return this.@category
+    }
+    void setCategory(RecipeCategory category) {
+        this.@category = category
+    }
+
+    T category(RecipeCategory category) {
+        setCategory(category)
+        return (T) this
+    }
+
+    String getGroup() {
+        return this.@group
+    }
+    void setGroup(String group) {
+        this.@group = group
+    }
+
+    T group(String group) {
+        setGroup(group)
+        return (T) this
+    }
+
+    T unlockedBy(String criterionName, CriterionTriggerInstance criterionTrigger) {
+        this.@advancement.addCriterion(criterionName, criterionTrigger)
+        return (T) this
+    }
+
+    Advancement.Builder getAdvancement() {
+        return this.@advancement
+    }
+}
\ No newline at end of file

From 3f9731728e58bfd83c9b49042a74289e109a4fa7 Mon Sep 17 00:00:00 2001
From: Matyrobbrt 
Date: Fri, 19 May 2023 20:51:52 +0300
Subject: [PATCH 9/9] TimeCategory support for cooking times

---
 .../recipe/GCookingRecipeBuilder.groovy       | 10 ++++++++
 .../9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e  |  4 ++--
 .../data/minecraft/recipes/iron_axe.json      |  2 +-
 Forge/src/test/groovy/cgltest/Datagen.groovy  | 23 +++++++++++++------
 4 files changed, 29 insertions(+), 10 deletions(-)

diff --git a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GCookingRecipeBuilder.groovy b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GCookingRecipeBuilder.groovy
index 0c6740b..8aa520a 100644
--- a/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GCookingRecipeBuilder.groovy
+++ b/Common/src/main/groovy/io/github/groovymc/cgl/api/datagen/recipe/GCookingRecipeBuilder.groovy
@@ -7,6 +7,7 @@ package io.github.groovymc.cgl.api.datagen.recipe
 
 import com.google.gson.JsonObject
 import groovy.contracts.Requires
+import groovy.time.TimeDuration
 import groovy.transform.CompileStatic
 import net.minecraft.advancements.Advancement
 import net.minecraft.advancements.AdvancementRewards
@@ -78,6 +79,15 @@ class GCookingRecipeBuilder implements SimpleRecipeBuilder 0 })
     void save(Consumer finishedRecipeConsumer, ResourceLocation recipeId) {
         this.advancement.parent(ROOT_RECIPE_ADVANCEMENT).addCriterion('has_the_recipe', RecipeUnlockedTrigger.unlocked(recipeId)).rewards(AdvancementRewards.Builder.recipe(recipeId)).requirements(RequirementsStrategy.OR);
diff --git a/Forge/src/test/generated/resources/cgltest/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e b/Forge/src/test/generated/resources/cgltest/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e
index 2cfc385..2abc6be 100644
--- a/Forge/src/test/generated/resources/cgltest/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e
+++ b/Forge/src/test/generated/resources/cgltest/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e
@@ -1,4 +1,4 @@
-// 1.19.4	2023-04-13T19:23:58.4387268	Recipes
+// 1.19.4	2023-05-19T20:51:34.6991639	Recipes
 c77d782a99edd1c0996eb899f82184cc8d44f41a data/anothermod/advancements/recipes/decorations/yesyes.json
 7087581372cb9ebf895e12bcc3eb4430609d6788 data/anothermod/recipes/yesyes.json
 0c5b9caa42269c7d408273d712efad02abb504c0 data/cgltest/recipes/my_stairs.json
@@ -6,4 +6,4 @@ c77d782a99edd1c0996eb899f82184cc8d44f41a data/anothermod/advancements/recipes/de
 ac80faac08adba0a3780519b5ce69a0602b682fc data/cgltest/recipes/test_stonecutting.json
 792fac73fab43971d32004ce6fba4744ca787e95 data/minecraft/recipes/acacia_boat.json
 018c2e8a055e1428adf76f0d21688abdc6bd3566 data/minecraft/recipes/acacia_log.json
-eeb92eab7ddaa17ae0e0ffecf34ec5c121612837 data/minecraft/recipes/iron_axe.json
+bb0034d4637a11f5503ca3e228616ffbeb123f22 data/minecraft/recipes/iron_axe.json
diff --git a/Forge/src/test/generated/resources/cgltest/data/minecraft/recipes/iron_axe.json b/Forge/src/test/generated/resources/cgltest/data/minecraft/recipes/iron_axe.json
index 9939304..ca449a9 100644
--- a/Forge/src/test/generated/resources/cgltest/data/minecraft/recipes/iron_axe.json
+++ b/Forge/src/test/generated/resources/cgltest/data/minecraft/recipes/iron_axe.json
@@ -1,7 +1,7 @@
 {
   "type": "minecraft:smelting",
   "category": "misc",
-  "cookingtime": 150,
+  "cookingtime": 260,
   "experience": 4.0,
   "ingredient": {
     "item": "minecraft:bamboo"
diff --git a/Forge/src/test/groovy/cgltest/Datagen.groovy b/Forge/src/test/groovy/cgltest/Datagen.groovy
index fa53e24..9ab99f6 100644
--- a/Forge/src/test/groovy/cgltest/Datagen.groovy
+++ b/Forge/src/test/groovy/cgltest/Datagen.groovy
@@ -7,6 +7,8 @@ package cgltest
 
 import com.matyrobbrt.gml.bus.EventBusSubscriber
 import com.matyrobbrt.gml.bus.type.ModBus
+import groovy.time.TimeCategory
+import groovy.transform.CompileDynamic
 import groovy.transform.CompileStatic
 import groovy.transform.PackageScope
 import io.github.groovymc.cgl.api.datagen.recipe.GRecipeProvider
@@ -73,18 +75,25 @@ class Datagen extends GRecipeProvider {
             category RecipeCategory.BREWING
         }
 
-        smelting {
-            result Items.IRON_AXE
-            ingredient Items.BAMBOO.ingredient()
-            cookingTime 150
-            experience 4f
-        }
-
         stonecutting {
             result Items.END_STONE_BRICK_SLAB
             ingredient Items.PINK_CANDLE.ingredient()
         } save 'test_stonecutting'
 
+        dynamic()
+
         saveForgotten()
     }
+
+    @CompileDynamic
+    private void dynamic() {
+        smelting {
+            result Items.IRON_AXE
+            ingredient Items.BAMBOO.ingredient()
+            use (TimeCategory) {
+                cookingTime 13.seconds
+            }
+            experience 4f
+        }
+    }
 }