From 66140d33a2081bd3ffc66a7a29072b5259409ca3 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Fri, 24 Mar 2023 23:28:06 +0700 Subject: [PATCH] Item repairer recipe --- .../datagen/recipes/CraftingTableRecipes.kt | 13 ++ .../mc/otm/datagen/recipes/MatteryRecipe.kt | 60 ++++- .../dbotthepony/mc/otm/registry/MRecipes.java | 5 + .../mc/otm/core/collect/JsonArrayCollector.kt | 31 +++ .../mc/otm/recipe/EnergyContainerRecipe.kt | 32 +-- .../mc/otm/recipe/UpgradeRecipe.kt | 205 ++++++++++++++++++ 6 files changed, 324 insertions(+), 22 deletions(-) create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/JsonArrayCollector.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/recipe/UpgradeRecipe.kt diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/CraftingTableRecipes.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/CraftingTableRecipes.kt index 5b471d904..a33668476 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/CraftingTableRecipes.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/CraftingTableRecipes.kt @@ -8,12 +8,14 @@ import net.minecraft.world.item.ItemStack import net.minecraft.world.item.Items import net.minecraft.world.item.crafting.Ingredient import net.minecraftforge.common.Tags +import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity import ru.dbotthepony.mc.otm.registry.MBlocks import ru.dbotthepony.mc.otm.registry.MItemTags import ru.dbotthepony.mc.otm.registry.MItems import ru.dbotthepony.mc.otm.registry.MRegistry import ru.dbotthepony.mc.otm.core.registryName import ru.dbotthepony.mc.otm.datagen.modLocation +import ru.dbotthepony.mc.otm.recipe.UpgradeRecipe import java.util.function.Consumer fun addCraftingTableRecipes(consumer: Consumer) { @@ -295,4 +297,15 @@ fun addCraftingTableRecipes(consumer: Consumer) { .rowB(Tags.Items.RODS_WOODEN) .rowB(Tags.Items.RODS_WOODEN) .build(consumer) + + MatteryRecipe(MItems.ITEM_REPAIRER, category = machinesCategory) + .setUpgradeSource(MItems.MATTER_REPLICATOR) + .addUpgradeOps( + UpgradeRecipe.Indirect("BlockEntityTag.${MatteryBlockEntity.ENERGY_KEY}", "BlockEntityTag.energy"), + UpgradeRecipe.Indirect("BlockEntityTag.${MatteryBlockEntity.MATTER_STORAGE_KEY}", "BlockEntityTag.matter"), + ) + .row(MItemTags.ADVANCED_CIRCUIT, Tags.Items.GEMS_EMERALD, MItemTags.ADVANCED_CIRCUIT) + .row(MItems.ELECTRIC_PARTS, MItems.MATTER_REPLICATOR, MItems.ELECTRIC_PARTS) + .row(MItems.ELECTROMAGNET, MItems.ELECTROMAGNET, MItems.ELECTROMAGNET) + .build(consumer) } diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/MatteryRecipe.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/MatteryRecipe.kt index 8d0c7f1f3..22ad6f598 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/MatteryRecipe.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/MatteryRecipe.kt @@ -1,13 +1,11 @@ package ru.dbotthepony.mc.otm.datagen.recipes -import com.google.common.collect.ImmutableList +import com.google.gson.JsonObject import net.minecraft.advancements.CriterionTriggerInstance import net.minecraft.data.recipes.FinishedRecipe -import net.minecraft.data.recipes.RecipeBuilder import net.minecraft.data.recipes.RecipeCategory import net.minecraft.data.recipes.ShapedRecipeBuilder -import net.minecraft.data.recipes.ShapelessRecipeBuilder import net.minecraft.resources.ResourceLocation import net.minecraft.tags.TagKey import net.minecraft.world.item.Item @@ -15,8 +13,11 @@ import net.minecraft.world.item.crafting.Ingredient import net.minecraft.world.item.crafting.RecipeSerializer import net.minecraft.world.level.ItemLike import ru.dbotthepony.mc.otm.OverdriveThatMatters +import ru.dbotthepony.mc.otm.core.collect.JsonArrayCollector import ru.dbotthepony.mc.otm.core.registryName +import ru.dbotthepony.mc.otm.core.set import ru.dbotthepony.mc.otm.recipe.EnergyContainerRecipe +import ru.dbotthepony.mc.otm.recipe.UpgradeRecipe import java.util.function.Consumer private interface RecipeCell { @@ -116,7 +117,7 @@ class MatteryRecipe(val result: ItemLike, val count: Int = 1, val category: Reci } } - fun build(consumer: Consumer, name: String? = null) { + private fun buildRegular(): ShapedRecipeBuilder { if (index == 0) { throw NoSuchElementException("No recipe rows were defined") } @@ -140,15 +141,62 @@ class MatteryRecipe(val result: ItemLike, val count: Int = 1, val category: Reci builder.unlockedBy(a, b) } + return builder + } + + private var source: ResourceLocation? = null + private val copyPaths = ArrayList() + + fun setUpgradeSource(source: ResourceLocation): MatteryRecipe { + this.source = source + return this + } + + fun setUpgradeSource(source: ItemLike): MatteryRecipe { + this.source = source.asItem().registryName!! + return this + } + + fun addUpgradeOps(vararg operations: UpgradeRecipe.Op): MatteryRecipe { + for (op in operations) copyPaths.add(op) + return this + } + + private fun filter(consumer: Consumer): Consumer { + if (source != null) { + check(copyPaths.isNotEmpty()) { "Defined upgrade recipe without nbt migration operations" } + + return Consumer { + consumer.accept(object : FinishedRecipe by it { + override fun serializeRecipeData(pJson: JsonObject) { + it.serializeRecipeData(pJson) + + pJson["copyPaths"] = copyPaths.stream().map { it.serialize() }.collect(JsonArrayCollector) + pJson["source"] = source!!.toString() + } + + override fun getType(): RecipeSerializer<*> { + return UpgradeRecipe.Companion + } + }) + } + } + + return consumer + } + + fun build(consumer: Consumer, name: String? = null) { + val builder = buildRegular() + if (name != null) { - builder.save(consumer, ResourceLocation(OverdriveThatMatters.MOD_ID, + builder.save(filter(consumer), ResourceLocation(OverdriveThatMatters.MOD_ID, if (result.asItem().registryName!!.namespace == OverdriveThatMatters.MOD_ID) "${result.asItem().registryName!!.path}_$name" else "${result.asItem().registryName!!.namespace}_${result.asItem().registryName!!.path}_$name" )) } else { - builder.save(consumer) + builder.save(filter(consumer)) } } diff --git a/src/main/java/ru/dbotthepony/mc/otm/registry/MRecipes.java b/src/main/java/ru/dbotthepony/mc/otm/registry/MRecipes.java index 1f4d002cb..f7a72fec6 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/registry/MRecipes.java +++ b/src/main/java/ru/dbotthepony/mc/otm/registry/MRecipes.java @@ -11,6 +11,7 @@ import ru.dbotthepony.mc.otm.OverdriveThatMatters; import ru.dbotthepony.mc.otm.recipe.EnergyContainerRecipe; import ru.dbotthepony.mc.otm.recipe.PlatePressRecipe; import ru.dbotthepony.mc.otm.recipe.PlatePressRecipeFactory; +import ru.dbotthepony.mc.otm.recipe.UpgradeRecipe; public class MRecipes { public static class MatteryRecipeType> implements RecipeType { @@ -28,6 +29,7 @@ public class MRecipes { public static final MatteryRecipeType PLATE_PRESS = new MatteryRecipeType<>(OverdriveThatMatters.loc(MNames.PLATE_PRESS)); public static final MatteryRecipeType ENERGY_CONTAINER = new MatteryRecipeType<>(OverdriveThatMatters.loc("energy_container")); + public static final MatteryRecipeType UPGRADE = new MatteryRecipeType<>(OverdriveThatMatters.loc("upgrade")); private static final DeferredRegister> serializerRegistry = DeferredRegister.create(ForgeRegistries.RECIPE_SERIALIZERS, OverdriveThatMatters.MOD_ID); private static final DeferredRegister> typeRegistry = DeferredRegister.create(ForgeRegistries.RECIPE_TYPES, OverdriveThatMatters.MOD_ID); @@ -35,8 +37,11 @@ public class MRecipes { static { serializerRegistry.register(MNames.PLATE_PRESS, () -> PlatePressRecipeFactory.INSTANCE); serializerRegistry.register(ENERGY_CONTAINER.name.getPath(), () -> EnergyContainerRecipe.Companion); + serializerRegistry.register(UPGRADE.name.getPath(), () -> UpgradeRecipe.Companion); + typeRegistry.register(MNames.PLATE_PRESS, () -> PLATE_PRESS); typeRegistry.register(ENERGY_CONTAINER.name.getPath(), () -> ENERGY_CONTAINER); + typeRegistry.register(UPGRADE.name.getPath(), () -> UPGRADE); } public static void register(IEventBus bus) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/JsonArrayCollector.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/JsonArrayCollector.kt new file mode 100644 index 000000000..2922a2270 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/JsonArrayCollector.kt @@ -0,0 +1,31 @@ +package ru.dbotthepony.mc.otm.core.collect + +import com.google.gson.JsonArray +import com.google.gson.JsonElement +import java.util.function.BiConsumer +import java.util.function.BinaryOperator +import java.util.function.Function +import java.util.function.Supplier +import java.util.stream.Collector + +object JsonArrayCollector : Collector { + override fun supplier(): Supplier { + return Supplier { JsonArray() } + } + + override fun accumulator(): BiConsumer { + return BiConsumer { t, u -> t.add(u) } + } + + override fun combiner(): BinaryOperator { + return BinaryOperator { t, u -> t.addAll(u); t } + } + + override fun finisher(): Function { + return Function.identity() + } + + override fun characteristics(): Set { + return setOf(Collector.Characteristics.IDENTITY_FINISH) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/EnergyContainerRecipe.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/EnergyContainerRecipe.kt index 8c911b316..253ac43b1 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/EnergyContainerRecipe.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/EnergyContainerRecipe.kt @@ -17,14 +17,14 @@ import ru.dbotthepony.mc.otm.container.stream import ru.dbotthepony.mc.otm.core.filterNotNull class EnergyContainerRecipe( - p_44153_: ResourceLocation, - p_44154_: String, + id: ResourceLocation, + group: String, category: CraftingBookCategory, - p_44155_: Int, - p_44156_: Int, - p_44157_: NonNullList, - p_44158_: ItemStack, -) : ShapedRecipe(p_44153_, p_44154_, category, p_44155_, p_44156_, p_44157_, p_44158_) { + width: Int, + height: Int, + ingredients: NonNullList, + result: ItemStack, +) : ShapedRecipe(id, group, category, width, height, ingredients, result) { constructor(parent: ShapedRecipe) : this(parent.id, parent.group, parent.category(), parent.width, parent.height, parent.ingredients, parent.resultItem) override fun assemble(container: CraftingContainer): ItemStack { @@ -53,25 +53,25 @@ class EnergyContainerRecipe( return itemStack } - override fun matches(p_44002_: CraftingContainer, p_44003_: Level): Boolean { - return super.matches(p_44002_, p_44003_) && !p_44002_.stream().anyMatch { it.isDamaged } + override fun matches(container: CraftingContainer, level: Level): Boolean { + return super.matches(container, level) && !container.stream().anyMatch { it.isDamaged } } - override fun getSerializer(): RecipeSerializer<*> { + override fun getSerializer(): RecipeSerializer { return Companion } companion object : RecipeSerializer { - override fun fromJson(p_44103_: ResourceLocation, p_44104_: JsonObject): EnergyContainerRecipe { - return EnergyContainerRecipe(Serializer.SHAPED_RECIPE.fromJson(p_44103_, p_44104_)) + override fun fromJson(id: ResourceLocation, data: JsonObject): EnergyContainerRecipe { + return EnergyContainerRecipe(Serializer.SHAPED_RECIPE.fromJson(id, data)) } - override fun fromNetwork(p_44105_: ResourceLocation, p_44106_: FriendlyByteBuf): EnergyContainerRecipe? { - return Serializer.SHAPED_RECIPE.fromNetwork(p_44105_, p_44106_)?.let(::EnergyContainerRecipe) + override fun fromNetwork(id: ResourceLocation, data: FriendlyByteBuf): EnergyContainerRecipe? { + return Serializer.SHAPED_RECIPE.fromNetwork(id, data)?.let(::EnergyContainerRecipe) } - override fun toNetwork(p_44101_: FriendlyByteBuf, p_44102_: EnergyContainerRecipe) { - Serializer.SHAPED_RECIPE.toNetwork(p_44101_, p_44102_) + override fun toNetwork(buff: FriendlyByteBuf, value: EnergyContainerRecipe) { + Serializer.SHAPED_RECIPE.toNetwork(buff, value) } } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/UpgradeRecipe.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/UpgradeRecipe.kt new file mode 100644 index 000000000..6131ac60b --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/UpgradeRecipe.kt @@ -0,0 +1,205 @@ +package ru.dbotthepony.mc.otm.recipe + +import com.google.common.collect.ImmutableList +import com.google.gson.JsonObject +import com.google.gson.JsonPrimitive +import net.minecraft.core.NonNullList +import net.minecraft.nbt.CompoundTag +import net.minecraft.network.FriendlyByteBuf +import net.minecraft.resources.ResourceLocation +import net.minecraft.util.GsonHelper +import net.minecraft.world.inventory.CraftingContainer +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.item.crafting.ShapedRecipe +import ru.dbotthepony.mc.otm.container.stream +import ru.dbotthepony.mc.otm.core.nbt.set +import ru.dbotthepony.mc.otm.core.registryName +import ru.dbotthepony.mc.otm.core.set +import ru.dbotthepony.mc.otm.core.tagNotNull +import ru.dbotthepony.mc.otm.core.util.readJson +import ru.dbotthepony.mc.otm.core.util.writeJson +import ru.dbotthepony.mc.otm.data.stream +import java.util.stream.Stream + +class UpgradeRecipe( + id: ResourceLocation, + group: String, + category: CraftingBookCategory, + width: Int, + height: Int, + ingredients: NonNullList, + result: ItemStack, + copyPaths: Stream, + val source: ResourceLocation, +) : ShapedRecipe(id, group, category, width, height, ingredients, result) { + constructor(parent: ShapedRecipe, copyPaths: Stream, source: ResourceLocation) : this(parent.id, parent.group, parent.category(), parent.width, parent.height, parent.ingredients, parent.resultItem, copyPaths, source) + + enum class OpType { + DIRECT { + override fun deserialize(obj: JsonObject): Op { + return Direct(GsonHelper.getAsString(obj, "path")) + } + }, INDIRECT { + override fun deserialize(obj: JsonObject): Op { + return Indirect(GsonHelper.getAsString(obj, "source"), GsonHelper.getAsString(obj, "destination")) + } + }; + + abstract fun deserialize(obj: JsonObject): Op + } + + interface Op { + val type: OpType + fun apply(source: CompoundTag, destination: CompoundTag) + fun serialize(json: JsonObject) + + fun serialize(): JsonObject { + return JsonObject().also { + it["type"] = JsonPrimitive(type.name) + serialize(it) + } + } + } + + data class Direct(val path: String) : Op { + private val split = path.split('.') + override val type: OpType + get() = OpType.DIRECT + + override fun serialize(json: JsonObject) { + json["path"] = JsonPrimitive("path") + } + + override fun apply(source: CompoundTag, destination: CompoundTag) { + var a = source + var b = destination + + for (i in 0 until split.size - 1) { + val value = split[i] + + if (value !in a) { + a[value] = CompoundTag() + } + + if (value !in b) { + b[value] = CompoundTag() + } + + a = a[value] as CompoundTag + b = b[value] as CompoundTag + } + + val value = split.last() + + if (value in a) { + b[value] = a[value]!!.copy() + } + } + } + + data class Indirect(val pathSource: String, val pathDestination: String) : Op { + private val splitSource = pathSource.split('.') + private val splitDestination = pathDestination.split('.') + + override val type: OpType + get() = OpType.INDIRECT + + override fun serialize(json: JsonObject) { + json["source"] = JsonPrimitive(pathSource) + json["destination"] = JsonPrimitive(pathDestination) + } + + override fun apply(source: CompoundTag, destination: CompoundTag) { + var a = source + var b = destination + + for (i in 0 until splitSource.size - 1) { + val value = splitSource[i] + + if (value !in a) { + a[value] = CompoundTag() + } + + a = a[value] as CompoundTag + } + + for (i in 0 until splitDestination.size - 1) { + val value = splitDestination[i] + + if (value !in b) { + b[value] = CompoundTag() + } + + b = b[value] as CompoundTag + } + + val valueA = splitSource.last() + val valueB = splitDestination.last() + + if (valueA in a) { + b[valueB] = a[valueA]!!.copy() + } + } + } + + val copyPaths: ImmutableList = copyPaths.collect(ImmutableList.toImmutableList()) + + override fun assemble(pInv: CraftingContainer): ItemStack { + val result = super.assemble(pInv) + + if (result.isEmpty) { + return result + } + + val sources = pInv.stream().filter { !it.isEmpty && it.item.registryName == source }.toList() + + if (sources.size != 1) { + return ItemStack.EMPTY + } + + val source = sources.first() + + for (op in copyPaths) { + op.apply(source.tagNotNull, result.tagNotNull) + } + + return result + } + + override fun getSerializer(): RecipeSerializer { + return Companion + } + + companion object : RecipeSerializer { + fun deserializeOp(json: JsonObject): Op { + return OpType.valueOf(GsonHelper.getAsString(json, "type")).deserialize(json) + } + + override fun fromJson(id: ResourceLocation, data: JsonObject): UpgradeRecipe { + return UpgradeRecipe( + Serializer.SHAPED_RECIPE.fromJson(id, data), + GsonHelper.getAsJsonArray(data, "copyPaths").stream().map { deserializeOp(it as JsonObject) }, + ResourceLocation(GsonHelper.getAsString(data, "source")) + ) + } + + override fun fromNetwork(id: ResourceLocation, buff: FriendlyByteBuf): UpgradeRecipe? { + val recipe = Serializer.SHAPED_RECIPE.fromNetwork(id, buff) ?: return null + + return UpgradeRecipe( + recipe, + buff.readCollection({ ArrayList(it) }, { deserializeOp(it.readJson() as JsonObject) }).stream(), + buff.readResourceLocation() + ) + } + + override fun toNetwork(buff: FriendlyByteBuf, value: UpgradeRecipe) { + Serializer.SHAPED_RECIPE.toNetwork(buff, value) + buff.writeCollection(value.copyPaths) { it, v -> it.writeJson(v.serialize()) } + buff.writeResourceLocation(value.source) + } + } +}