From 43c081277b5b7a62c9a7f0e40ad4ac3dd6462678 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Mon, 12 Sep 2022 19:46:04 +0700 Subject: [PATCH] Bump JEI version, make use of createUnregisteredRecipeTransferHandler --- gradle.properties | 2 +- .../mc/otm/compat/jei/JEIPlugin.kt | 206 ++++++------------ .../kotlin/ru/dbotthepony/mc/otm/core/Ext.kt | 4 + .../mc/otm/network/MenuNetworkChannel.kt | 184 ---------------- 4 files changed, 73 insertions(+), 323 deletions(-) diff --git a/gradle.properties b/gradle.properties index a575365e8..275653609 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ mc_version=1.19.2 forge_gradle_version=5.1.27 forge_version=43.1.1 -jei_version=11.2.0.254 +jei_version=11.3.0.262 jupiter_version=5.8.2 the_one_probe_version=6.2.1 mekanism_version=10.3.2.homebaked diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/JEIPlugin.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/JEIPlugin.kt index f45c33808..c250b3862 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/JEIPlugin.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/jei/JEIPlugin.kt @@ -1,12 +1,7 @@ package ru.dbotthepony.mc.otm.compat.jei import it.unimi.dsi.fastutil.ints.Int2IntArrayMap -import it.unimi.dsi.fastutil.ints.Int2IntFunction -import it.unimi.dsi.fastutil.ints.IntArrayList import it.unimi.dsi.fastutil.ints.IntArraySet -import it.unimi.dsi.fastutil.objects.Object2IntArrayMap -import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap -import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import mezz.jei.api.IModPlugin import mezz.jei.api.JeiPlugin import mezz.jei.api.constants.RecipeTypes @@ -17,6 +12,7 @@ import mezz.jei.api.helpers.IJeiHelpers import mezz.jei.api.recipe.RecipeIngredientRole import mezz.jei.api.recipe.transfer.IRecipeTransferError import mezz.jei.api.recipe.transfer.IRecipeTransferHandler +import mezz.jei.api.recipe.transfer.IRecipeTransferInfo import mezz.jei.api.registration.IGuiHandlerRegistration import mezz.jei.api.registration.IRecipeCatalystRegistration import mezz.jei.api.registration.IRecipeCategoryRegistration @@ -27,7 +23,6 @@ import net.minecraft.resources.ResourceLocation import net.minecraft.world.entity.player.Player import net.minecraft.world.inventory.MenuType import net.minecraft.world.inventory.Slot -import net.minecraft.world.item.Item import net.minecraft.world.item.ItemStack import net.minecraft.world.item.crafting.CraftingRecipe import net.minecraft.world.item.crafting.RecipeType @@ -35,14 +30,12 @@ import ru.dbotthepony.mc.otm.OverdriveThatMatters import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.screen.MatteryScreen import ru.dbotthepony.mc.otm.core.TranslatableComponent +import ru.dbotthepony.mc.otm.core.toImmutableList import ru.dbotthepony.mc.otm.menu.ExoSuitInventoryMenu -import ru.dbotthepony.mc.otm.network.MenuNetworkChannel -import ru.dbotthepony.mc.otm.network.MoveMultipleItemsPacket import ru.dbotthepony.mc.otm.recipe.EnergyContainerRecipe import ru.dbotthepony.mc.otm.registry.MItems import ru.dbotthepony.mc.otm.registry.MRecipes import java.util.* -import java.util.function.BiFunction import java.util.stream.Collectors import kotlin.collections.ArrayList import kotlin.properties.Delegates @@ -89,6 +82,38 @@ class JEIPlugin : IModPlugin { val helper = registration.transferHelper registration.addRecipeTransferHandler(object : IRecipeTransferHandler { + private val transfer = helper.createUnregisteredRecipeTransferHandler(object : IRecipeTransferInfo { + override fun getContainerClass(): Class { + return ExoSuitInventoryMenu::class.java + } + + override fun getMenuType(): Optional> { + return Optional.empty() + } + + override fun getRecipeType(): mezz.jei.api.recipe.RecipeType { + return RecipeTypes.CRAFTING + } + + override fun canHandle(container: ExoSuitInventoryMenu, recipe: CraftingRecipe): Boolean { + return true + } + + override fun getRecipeSlots( + container: ExoSuitInventoryMenu, + recipe: CraftingRecipe + ): List { + return container.craftingSlots + } + + override fun getInventorySlots( + container: ExoSuitInventoryMenu, + recipe: CraftingRecipe + ): List { + return container.playerInventorySlots + } + }) + override fun getContainerClass(): Class { return ExoSuitInventoryMenu::class.java } @@ -122,8 +147,6 @@ class JEIPlugin : IModPlugin { it.put(4, i) } - // i wonder why Mezz didn't expose IConnectionToServer - // because without it there is a lot of code duplication! override fun transferRecipe( container: ExoSuitInventoryMenu, recipe: CraftingRecipe, @@ -144,143 +167,50 @@ class JEIPlugin : IModPlugin { return helper.createUserErrorWithTooltip(TranslatableComponent("jei.tooltip.error.recipe.transfer.too.large.player.inventory")) } } - } - val missingItems = inputs.stream() - .filter { !it.isEmpty } - .filter { it.itemStacks.noneMatch { recipeItem -> container.allAccessibleSlots.any { it.mayPickup(player) && it.item.item === recipeItem.item } } } - .collect(Collectors.toList()) + val filteredInputs = ArrayList() - if (missingItems.isNotEmpty()) { - return helper.createUserErrorForMissingSlots(TranslatableComponent("jei.tooltip.error.recipe.transfer.missing"), missingItems) - } - - val emptySlots = container.allAccessibleSlots.stream().filter { it.item.isEmpty }.toList() - val filledCraftingSlots = container.craftingSlots.stream().filter { !it.item.isEmpty }.count() - - if (emptySlots.size < filledCraftingSlots) { - return helper.createUserErrorWithTooltip(TranslatableComponent("jei.tooltip.error.recipe.transfer.inventory.full")) - } - - if (!doTransfer) { - return null - } - - val desiredItems = inputs.stream() - .filter { !it.isEmpty } - .flatMap { it.itemStacks } - .map { it.item } - .distinct() - .collect(Collectors.toSet()) - - val sources = Object2ObjectArrayMap>() - - for (slot in container.allAccessibleSlots) { - if (!slot.item.isEmpty && slot.mayPickup(player) && slot.item.item in desiredItems) { - sources.computeIfAbsent(slot.item.item, Object2ObjectFunction { ArrayList() }).add(slot) - } - } - - val bestSources = Array(inputs.size) { - val ingredient = inputs[it] - - if (ingredient.isEmpty) { - kotlin.collections.ArrayDeque() - } else { - var bestMatch: List? = null - var bestMatchSize = 0 - - for (item in ingredient.itemStacks) { - val slots = sources[item.item] - - if (slots != null) { - var thisSize = 0 - for (slot in slots) thisSize += slot.item.count - - if (bestMatchSize < thisSize) { - bestMatchSize = thisSize - bestMatch = slots - } - } - } - - val bestMatchNN = bestMatch ?: throw NullPointerException("Could not find best matching set of slots for recipe autofill (this exception should be never reachable)") - val trulyBest: List - - if (bestMatchNN.size <= 99) { - trulyBest = bestMatchNN - } else { - trulyBest = ArrayList(99).also { for (i in 0 .. 39) it.add(bestMatchNN[i]) } - } - - kotlin.collections.ArrayDeque(trulyBest.size).also { - it.addAll(trulyBest) - - it.sortWith { a, b -> - a.item.count.compareTo(b.item.count) - } - } - } - } - - val actions = ArrayList() - - val nextEmpty = emptySlots.iterator() - val emptiedSlots = IntArraySet() - - // clear crafting slots - for (slot in container.craftingSlots) { - if (!slot.item.isEmpty) { - val emptySlot = nextEmpty.next() - actions.add(MoveMultipleItemsPacket.Move(setOf(slot.index), emptySlot.index, slot.item.count)) - emptiedSlots.add(slot.index) - - for (sourceList in bestSources) { - if (sourceList.isNotEmpty() && sourceList.first().item.item === slot.item.item) { - sourceList.addFirst(emptySlot) - } - } - } - } - - val pieWishers = Object2IntArrayMap() - - for (slots in bestSources) { - if (slots.isNotEmpty()) { - pieWishers.compute(slots.first().item.item) { _, count -> (count ?: 0) + 1 } - } - } - - var transferCount = if (maxTransfer) 64 else 1 - - if (transferCount != 1) { for ((i, ingredient) in inputs.withIndex()) { - if (!ingredient.isEmpty) { - var maxTransferThis = 0 - for (slot in bestSources[i]) maxTransferThis += slot.item.count - transferCount = transferCount.coerceAtMost(maxTransferThis / pieWishers.getInt(bestSources[i].first().item.item)) + if (i in validSlots) { + filteredInputs.add(ingredient) } } - } - val remap = if (container.craftingSlots.size == 9) directMap else smallMap + val outputs = recipeSlots.getSlotViews(RecipeIngredientRole.OUTPUT).toImmutableList() + val catalysts = recipeSlots.getSlotViews(RecipeIngredientRole.CATALYST).toImmutableList() + val render = recipeSlots.getSlotViews(RecipeIngredientRole.RENDER_ONLY).toImmutableList() - for ((i, ingredient) in inputs.withIndex()) { - if (!ingredient.isEmpty) { - actions.add(MoveMultipleItemsPacket.Move( - IntArrayList().also { for (slot in bestSources[i]) if (slot.index !in emptiedSlots) it.add(slot.index) }, - container.craftingSlots[remap[i]].index, - transferCount)) + val combine = ArrayList(filteredInputs.size + outputs.size + render.size) + + combine.addAll(filteredInputs) + combine.addAll(outputs) + combine.addAll(render) + + val combined = combine.toImmutableList() + + val newView = object : IRecipeSlotsView { + override fun getSlotViews(): MutableList { + return combined + } + + override fun getSlotViews(role: RecipeIngredientRole): MutableList { + return when (role) { + RecipeIngredientRole.INPUT -> filteredInputs + RecipeIngredientRole.OUTPUT -> outputs + RecipeIngredientRole.CATALYST -> catalysts + RecipeIngredientRole.RENDER_ONLY -> render + } + } + + override fun findSlotByName(slotName: String): Optional { + return combined.stream().filter { it.slotName.orElse(null) == slotName }.findAny() + } } + + return this.transfer.transferRecipe(container, recipe, newView, player, maxTransfer, doTransfer) } - val packetA = MoveMultipleItemsPacket(actions, mapOf()) - val result = packetA.execute(player, container) - val packetB = MoveMultipleItemsPacket(actions, result) - - MenuNetworkChannel.sendToServer(packetB) - - return null + return this.transfer.transferRecipe(container, recipe, recipeSlots, player, maxTransfer, doTransfer) } }, RecipeTypes.CRAFTING) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt index 02ea06a9a..90eb8bfd4 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt @@ -331,3 +331,7 @@ fun FriendlyByteBuf.readItemType(): Item? { operator fun > StateHolder<*, *>.get(value: Property): T { return getValue(value) } + +fun List.toImmutableList(): ImmutableList { + return ImmutableList.copyOf(this) +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/network/MenuNetworkChannel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/network/MenuNetworkChannel.kt index 3827ac11a..bcb803679 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/network/MenuNetworkChannel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/network/MenuNetworkChannel.kt @@ -65,189 +65,6 @@ class SetCarriedPacket(val item: ItemStack) : MatteryPacket { } } -class MoveMultipleItemsPacket(val actions: Collection, val changedSlots: Map, val error: Throwable? = null) : MatteryPacket { - data class Move(val sourceSlots: Collection, val destinationSlot: Int, val count: Int) { - fun write(buff: FriendlyByteBuf) { - buff.writeInt(sourceSlots.size) - - for (slot in sourceSlots) { - buff.writeInt(slot) - } - - buff.writeInt(destinationSlot) - buff.writeInt(count) - } - - companion object { - fun read(buff: FriendlyByteBuf): Move { - val listSize = buff.readInt() - require(listSize <= 100) { "Too many source slots! ($listSize received)" } - - val seen = IntAVLTreeSet() - val list = IntArrayList() - - for (i in 0 until listSize) { - val index = buff.readInt() - - if (seen.add(index)) { - list.add(index) - } - } - - val destinationSlot = buff.readInt() - val count = buff.readInt() - - return Move(list, destinationSlot, count) - } - } - } - - override fun write(buff: FriendlyByteBuf) { - buff.writeInt(actions.size) - - for (action in actions) { - action.write(buff) - } - - buff.writeInt(changedSlots.size) - - for ((k, v) in changedSlots) { - buff.writeInt(k) - buff.writeItem(v) - } - } - - fun execute(ply: Player, container: AbstractContainerMenu): Map { - val changedSlots = Int2ObjectArrayMap() - - for ((sourceSlots, destinationSlotIndex, count) in actions) { - val destinationSlot = container.slots[destinationSlotIndex] - - if (!destinationSlot.item.isEmpty) { - // clear slot first - continue - } - - val realSourceSlots = sourceSlots.map { container.slots[it] } - - if (realSourceSlots.isEmpty() || realSourceSlots.all { it.item.isEmpty }) { - continue - } - - val type = realSourceSlots.first { !it.item.isEmpty }.item - - if (!realSourceSlots.all { it.item.isEmpty || it.item.item === type.item }) { - continue - } - - if (!destinationSlot.mayPlace(type)) { - continue - } - - var pickedUp = 0 - val maxPickup = destinationSlot.getMaxStackSize(type).coerceAtMost(count) - - for (slot in realSourceSlots) { - if (slot.mayPickup(ply) && !slot.item.isEmpty) { - if (!destinationSlot.item.isEmpty && !ItemStack.isSameItemSameTags(destinationSlot.item, slot.item)) { - continue - } - - val old = pickedUp - pickedUp = (pickedUp + slot.item.count).coerceAtMost(maxPickup) - val diff = pickedUp - old - - val copy = slot.item.copy().also { it.count = diff } - - slot.item.count -= diff - - if (slot.item.isEmpty) { - slot.set(ItemStack.EMPTY) - changedSlots[slot.index] = ItemStack.EMPTY - } else { - slot.setChanged() - changedSlots[slot.index] = slot.item.copy() - } - - if (destinationSlot.item.isEmpty) { - destinationSlot.set(copy) - } else { - destinationSlot.item.count += diff - destinationSlot.setChanged() - } - - if (pickedUp == maxPickup) { - break - } - } - } - - if (pickedUp != 0) { - changedSlots[destinationSlot.index] = destinationSlot.item.copy() - } - } - - return changedSlots - } - - override fun play(context: Supplier) { - context.packetHandled = true - - if (error != null) { - context.sender!!.connection.disconnect(TextComponent("Caught an exception processing ${MoveMultipleItemsPacket::class.qualifiedName}: $error")) - LOGGER.error("Processing multi move input from player ${context.sender} has thrown an exception!", error) - return - } - - context.enqueueWork { - try { - val container = context.sender!!.containerMenu - - if (container is ExoSuitInventoryMenu) { - execute(context.sender!!, container) - - for ((index, contents) in changedSlots) { - container.setRemoteSlot(index, contents) - } - } - } catch (err: Throwable) { - context.sender!!.connection.disconnect(TextComponent("Caught an exception processing ${MoveMultipleItemsPacket::class.qualifiedName}: $err")) - LOGGER.error("Processing multi move input from player ${context.sender} has thrown an exception!", err) - } - } - } - - companion object { - private val LOGGER = LogManager.getLogger() - - fun read(buff: FriendlyByteBuf): MoveMultipleItemsPacket { - try { - val actionCount = buff.readInt() - require(actionCount <= 40) { "Too many actions!" } - - val actions = ArrayList(actionCount) - - for (i in 0 until actionCount) { - actions.add(Move.read(buff)) - } - - val changedSlotCount = buff.readInt() - require(changedSlotCount <= 40) { "Too many changed slots!" } - - val changedSlots = Int2ObjectAVLTreeMap() - - for (i in 0 until changedSlotCount) { - changedSlots.put(buff.readInt(), buff.readItem()) - } - - return MoveMultipleItemsPacket(actions, changedSlots) - } catch (err: Throwable) { - return MoveMultipleItemsPacket(listOf(), mapOf(), err) - } - } - } -} - object MenuNetworkChannel : MatteryNetworkChannel( version = "1", name = "menu" @@ -272,7 +89,6 @@ object MenuNetworkChannel : MatteryNetworkChannel( add(BooleanPlayerInputPacket::class.java, BooleanPlayerInputPacket.Companion::read, NetworkDirection.PLAY_TO_SERVER) // menu specific - add(MoveMultipleItemsPacket::class.java, MoveMultipleItemsPacket.Companion::read, NetworkDirection.PLAY_TO_SERVER) // Item monitor add(ItemMonitorPlayerSettings::class.java, ItemMonitorPlayerSettings.Companion::read)