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 e3a00907d..027df49bb 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 @@ -4,7 +4,6 @@ package ru.dbotthepony.mc.otm.datagen.recipes import com.google.gson.JsonObject import net.minecraft.advancements.Advancement import net.minecraft.advancements.Criterion -import net.minecraft.advancements.CriterionTriggerInstance import net.minecraft.data.recipes.FinishedRecipe import net.minecraft.data.recipes.RecipeCategory import net.minecraft.data.recipes.RecipeOutput @@ -20,9 +19,7 @@ import ru.dbotthepony.mc.otm.core.registryName import ru.dbotthepony.mc.otm.core.set import ru.dbotthepony.mc.otm.core.toJsonStrict import ru.dbotthepony.mc.otm.datagen.modLocation -import ru.dbotthepony.mc.otm.recipe.EnergyContainerRecipe import ru.dbotthepony.mc.otm.recipe.UpgradeRecipe -import java.util.function.Consumer private interface RecipeCell { val value: Ingredient @@ -191,7 +188,7 @@ class MatteryRecipe(val result: ItemLike, val count: Int = 1, val category: Reci object : FinishedRecipe by it { override fun serializeRecipeData(pJson: JsonObject) { pJson["parent"] = it.serializeRecipe() - pJson["copyPaths"] = UpgradeRecipe.COPY_PATHS_CODEC.toJsonStrict(copyPaths) + pJson["copyPaths"] = UpgradeRecipe.OPERATION_CODEC.toJsonStrict(copyPaths) pJson["source"] = upgradeSource!!.toString() } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/energy/IMatteryEnergyStorage.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/energy/IMatteryEnergyStorage.kt index ce8a73ea1..2ab6a1cd0 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/energy/IMatteryEnergyStorage.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/energy/IMatteryEnergyStorage.kt @@ -96,7 +96,7 @@ interface IMatteryEnergyStorage : IEnergyStorage { val canSetBatteryLevel: Boolean get() = true /** - * How much energy (estimated) is stored in this energy storage. Why estimated? Because some objects can be bottomless. + * How much energy (considered to be estimated, and not ground truth) is stored in this energy storage * * Implementations are free to throw [UnsupportedOperationException] if setting battery level is not supported * due to technical complications (in this case, [canSetBatteryLevel] MUST be false) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/EnergyContainerRecipe.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/EnergyContainerRecipe.kt deleted file mode 100644 index caf8f34d8..000000000 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/EnergyContainerRecipe.kt +++ /dev/null @@ -1,83 +0,0 @@ -package ru.dbotthepony.mc.otm.recipe - -import com.mojang.serialization.Codec -import com.mojang.serialization.MapCodec -import net.minecraft.core.HolderLookup -import net.minecraft.core.RegistryAccess -import net.minecraft.core.component.DataComponents -import net.minecraft.network.FriendlyByteBuf -import net.minecraft.network.RegistryFriendlyByteBuf -import net.minecraft.network.codec.StreamCodec -import net.minecraft.world.item.ItemStack -import net.minecraft.world.item.crafting.CraftingBookCategory -import net.minecraft.world.item.crafting.CraftingInput -import net.minecraft.world.item.crafting.RecipeSerializer -import net.minecraft.world.item.crafting.ShapedRecipe -import net.minecraft.world.item.crafting.ShapedRecipePattern -import net.minecraft.world.item.enchantment.ItemEnchantments -import net.minecraft.world.level.Level -import ru.dbotthepony.mc.otm.capability.matteryEnergy -import ru.dbotthepony.mc.otm.container.util.iterator -import ru.dbotthepony.mc.otm.core.filterNotNull - -class EnergyContainerRecipe(group: String, category: CraftingBookCategory, pattern: ShapedRecipePattern, result: ItemStack, showNotification: Boolean = true) : ShapedRecipe(group, category, pattern, result, showNotification) { - constructor(parent: ShapedRecipe) : this(parent.group, parent.category(), parent.pattern, parent.result, parent.showNotification()) - - override fun assemble(container: CraftingInput, registryAccess: HolderLookup.Provider): ItemStack { - val itemStack = super.assemble(container, registryAccess) - - val battery = container.items().stream() - .filter { !it.isEmpty } - .map { it.matteryEnergy } - .filterNotNull() - .findAny().orElse(null) - - if (battery != null) { - itemStack.matteryEnergy?.batteryLevel = battery.batteryLevel - } - - if (itemStack.isEnchantable) { - for (it in container.items()) { - val enchantments = it[DataComponents.ENCHANTMENTS] - - if (enchantments != null) { - itemStack[DataComponents.ENCHANTMENTS] = ItemEnchantments.Mutable(itemStack.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY)).let { - for ((key, level) in enchantments.entrySet()) { - it.upgrade(key, level) - } - - it.toImmutable() - } - } - } - } - - return itemStack - } - - override fun matches(container: CraftingInput, level: Level): Boolean { - return super.matches(container, level) && container.items().none { it.isDamaged } - } - - override fun getSerializer(): RecipeSerializer { - return Companion - } - - companion object : RecipeSerializer { - private val codec by lazy { - RecipeSerializer.SHAPED_RECIPE.codec().xmap(::EnergyContainerRecipe, { it }) - } - - private val streamCodec by lazy { - RecipeSerializer.SHAPED_RECIPE.streamCodec().map(::EnergyContainerRecipe, { it }) - } - - override fun codec(): MapCodec { - return codec - } - - override fun streamCodec(): StreamCodec { - return streamCodec - } - } -} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/UpgradeRecipe.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/UpgradeRecipe.kt index c00371944..2c825aa55 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/UpgradeRecipe.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/UpgradeRecipe.kt @@ -1,109 +1,59 @@ package ru.dbotthepony.mc.otm.recipe -import com.google.common.collect.ImmutableList -import com.mojang.serialization.Codec import com.mojang.serialization.MapCodec import com.mojang.serialization.codecs.RecordCodecBuilder -import net.minecraft.core.NonNullList -import net.minecraft.core.RegistryAccess -import net.minecraft.nbt.CompoundTag +import net.minecraft.core.HolderLookup +import net.minecraft.core.component.DataComponentType +import net.minecraft.core.component.DataComponents +import net.minecraft.network.RegistryFriendlyByteBuf +import net.minecraft.network.codec.ByteBufCodecs +import net.minecraft.network.codec.StreamCodec import net.minecraft.resources.ResourceLocation import net.minecraft.util.StringRepresentable -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.CraftingRecipe -import net.minecraft.world.item.crafting.Ingredient +import net.minecraft.world.item.crafting.CraftingInput import net.minecraft.world.item.crafting.RecipeSerializer -import net.minecraft.world.item.crafting.RecipeType import net.minecraft.world.item.crafting.ShapedRecipe +import net.minecraft.world.item.crafting.ShapedRecipePattern +import net.minecraft.world.item.enchantment.ItemEnchantments import net.minecraft.world.level.Level -import net.minecraftforge.common.crafting.IShapedRecipe -import ru.dbotthepony.mc.otm.container.util.stream -import ru.dbotthepony.mc.otm.core.nbt.set +import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.core.registryName -import ru.dbotthepony.mc.otm.core.tagNotNull -import ru.dbotthepony.mc.otm.data.SingletonCodec -import java.util.stream.Stream class UpgradeRecipe( - val parent: ShapedRecipe, - copyPaths: Stream, - val source: ResourceLocation, -) : CraftingRecipe, IShapedRecipe by parent { - constructor(parent: ShapedRecipe, copyPaths: Collection, source: ResourceLocation) : this(parent, copyPaths.stream(), source) - - override fun matches(p_44002_: CraftingContainer, p_44003_: Level): Boolean { - return parent.matches(p_44002_, p_44003_) - } - - override fun canCraftInDimensions(p_43999_: Int, p_44000_: Int): Boolean { - return parent.canCraftInDimensions(p_43999_, p_44000_) - } - - override fun getResultItem(p_267052_: RegistryAccess): ItemStack { - return parent.getResultItem(p_267052_) - } - - override fun getRemainingItems(p_44004_: CraftingContainer): NonNullList { - return parent.getRemainingItems(p_44004_) - } - - override fun getIngredients(): NonNullList { - return parent.ingredients - } - - override fun isSpecial(): Boolean { - return parent.isSpecial - } - - override fun showNotification(): Boolean { - return parent.showNotification() - } - - override fun getGroup(): String { - return parent.group - } - - override fun getToastSymbol(): ItemStack { - return parent.toastSymbol - } - - override fun isIncomplete(): Boolean { - return parent.isIncomplete - } - - override fun getType(): RecipeType<*> { - return parent.type - } - - override fun category(): CraftingBookCategory { - return parent.category() - } + group: String, + category: CraftingBookCategory, + pattern: ShapedRecipePattern, + result: ItemStack, + val operations: List>, + showNotification: Boolean = true, +) : ShapedRecipe(group, category, pattern, result, showNotification) { + constructor(parent: ShapedRecipe, operations: List>) : this(parent.group, parent.category(), parent.pattern, parent.result, operations, parent.showNotification()) enum class OpType : StringRepresentable { - DIRECT { - override val codec: MapCodec = RecordCodecBuilder.mapCodec { + COPY_COMPONENT { + override val codec: MapCodec> = RecordCodecBuilder.mapCodec { it.group( - Codec.STRING.fieldOf("path").forGetter(Direct::path) - ).apply(it, ::Direct) + DataComponentType.CODEC.fieldOf("component").forGetter(CopyComponent<*>::component) + ).apply(it) { CopyComponent(it) } } }, - INDIRECT { - override val codec: MapCodec = RecordCodecBuilder.mapCodec { - it.group( - Codec.STRING.fieldOf("source").forGetter(Indirect::pathSource), - Codec.STRING.fieldOf("destination").forGetter(Indirect::pathDestination), - ).apply(it, ::Indirect) - } + COPY_ALL_COMPONENTS { + override val codec: MapCodec = MapCodec.unit(CopyAllComponents) }, - ALL { - override val codec: MapCodec by lazy { MapCodec.unit(All) } + COPY_ENCHANTMENTS { + override val codec: MapCodec = MapCodec.unit(CopyEnchantments) + }, + COPY_ENERGY_CHARGE { + override val codec: MapCodec = MapCodec.unit(CopyEnergyCharge) }, ; + private val serName = name.lowercase() + override fun getSerializedName(): String { - return name.lowercase() + return serName } abstract val codec: MapCodec @@ -111,134 +61,120 @@ class UpgradeRecipe( sealed class Op { abstract val type: OpType - abstract fun apply(source: CompoundTag, destination: CompoundTag) + abstract fun apply(source: ItemStack, container: CraftingInput, registry: HolderLookup.Provider, destination: ItemStack) + + open fun matches(container: CraftingInput, level: Level): Boolean { + return true + } } - data class Direct(val path: String) : Op() { - private val split = path.split('.') + data class CopyComponent(val component: DataComponentType) : Op() { override val type: OpType - get() = OpType.DIRECT + get() = OpType.COPY_COMPONENT - override fun apply(source: CompoundTag, destination: CompoundTag) { - var a = source - var b = destination + override fun apply(source: ItemStack, container: CraftingInput, registry: HolderLookup.Provider, destination: ItemStack) { + val existing = source[component] - 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() + if (existing != null) { + destination[component] = existing } } } - data class Indirect(val pathSource: String, val pathDestination: String) : Op() { - private val splitSource = pathSource.split('.') - private val splitDestination = pathDestination.split('.') - + object CopyAllComponents : Op() { override val type: OpType - get() = OpType.INDIRECT + get() = OpType.COPY_ALL_COMPONENTS - override fun apply(source: CompoundTag, destination: CompoundTag) { - var a = source - var b = destination + override fun apply(source: ItemStack, container: CraftingInput, registry: HolderLookup.Provider, destination: ItemStack) { + destination.applyComponentsAndValidate(source.componentsPatch) + } + } - for (i in 0 until splitSource.size - 1) { - val value = splitSource[i] + object CopyEnchantments : Op() { + override val type: OpType + get() = OpType.COPY_ENCHANTMENTS - if (value !in a) { - a[value] = CompoundTag() + override fun apply(source: ItemStack, container: CraftingInput, registry: HolderLookup.Provider, destination: ItemStack) { + val enchantments = source[DataComponents.ENCHANTMENTS] + + if (enchantments != null) { + destination[DataComponents.ENCHANTMENTS] = ItemEnchantments.Mutable(destination.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY)).let { + for ((key, level) in enchantments.entrySet()) { + it.upgrade(key, level) + } + + it.toImmutable() } - - 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() } } } - object All : Op() { + object CopyEnergyCharge : Op() { override val type: OpType - get() = OpType.ALL + get() = OpType.COPY_ENERGY_CHARGE - override fun apply(source: CompoundTag, destination: CompoundTag) { - source.allKeys.forEach { - destination[it] = source[it]!! - } + override fun apply(source: ItemStack, container: CraftingInput, registry: HolderLookup.Provider, destination: ItemStack) { + val batterySource = source.getCapability(MatteryCapability.ITEM_ENERGY) ?: return + val batteryDestination = destination.getCapability(MatteryCapability.ITEM_ENERGY) ?: return + batteryDestination.batteryLevel += batterySource.batteryLevel } } - val copyPaths: ImmutableList = copyPaths.collect(ImmutableList.toImmutableList()) - - override fun assemble(pInv: CraftingContainer, registryAccess: RegistryAccess): ItemStack { - val result = parent.assemble(pInv, registryAccess) + override fun assemble(input: CraftingInput, registry: HolderLookup.Provider): ItemStack { + val result = super.assemble(input, registry) 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) + for ((item, op) in operations) { + input.items() + .filter { it.item.registryName == item } + .forEach { op.apply(it, input, registry, result) } } return result } - override fun getSerializer(): RecipeSerializer { - return CODEC + override fun matches(p_345040_: CraftingInput, p_44167_: Level): Boolean { + return super.matches(p_345040_, p_44167_) && operations.all { it.second.matches(p_345040_, p_44167_) } } - companion object { - val COPY_PATHS_CODEC: Codec> = StringRepresentable - .fromEnum(OpType::values) - .dispatch({ it.type }, { it.codec }) - .listOf() + override fun getSerializer(): RecipeSerializer { + return Companion + } - val CODEC = Codec2RecipeSerializer { p -> - RecordCodecBuilder.create { - it.group( - p.wrap(ShapedRecipe.Serializer.SHAPED_RECIPE).fieldOf("parent").forGetter(UpgradeRecipe::parent), - COPY_PATHS_CODEC.fieldOf("copyPaths").forGetter(UpgradeRecipe::copyPaths), - ResourceLocation.CODEC.fieldOf("source").forGetter(UpgradeRecipe::source) - ).apply(it, ::UpgradeRecipe) - } + companion object : RecipeSerializer { + private val operationCodec: MapCodec = StringRepresentable + .fromEnum(OpType::values) + .dispatchMap({ it.type }, { it.codec }) + + private val operationEntryCodec = RecordCodecBuilder.create> { + it.group( + ResourceLocation.CODEC.fieldOf("item").forGetter(Pair::first), + operationCodec.forGetter(Pair::second), + ).apply(it) { a, b -> Pair(a, b) } + }.listOf() + + private val codec = RecordCodecBuilder.mapCodec { + it.group( + Serializer.CODEC.forGetter { it }, + operationEntryCodec.fieldOf("operations").forGetter(UpgradeRecipe::operations) + ).apply(it, ::UpgradeRecipe) + } + + private val streamCodec = StreamCodec.composite( + Serializer.STREAM_CODEC, { it }, + ByteBufCodecs.fromCodecWithRegistries(operationEntryCodec), { it.operations }, // quite lazy solution + ::UpgradeRecipe + ) + + override fun codec(): MapCodec { + return codec + } + + override fun streamCodec(): StreamCodec { + return streamCodec } } }