Bump JEI version, make use of createUnregisteredRecipeTransferHandler
This commit is contained in:
parent
583b467e69
commit
43c081277b
@ -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
|
||||
|
@ -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<ExoSuitInventoryMenu, CraftingRecipe> {
|
||||
private val transfer = helper.createUnregisteredRecipeTransferHandler(object : IRecipeTransferInfo<ExoSuitInventoryMenu, CraftingRecipe> {
|
||||
override fun getContainerClass(): Class<out ExoSuitInventoryMenu> {
|
||||
return ExoSuitInventoryMenu::class.java
|
||||
}
|
||||
|
||||
override fun getMenuType(): Optional<MenuType<ExoSuitInventoryMenu>> {
|
||||
return Optional.empty()
|
||||
}
|
||||
|
||||
override fun getRecipeType(): mezz.jei.api.recipe.RecipeType<CraftingRecipe> {
|
||||
return RecipeTypes.CRAFTING
|
||||
}
|
||||
|
||||
override fun canHandle(container: ExoSuitInventoryMenu, recipe: CraftingRecipe): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getRecipeSlots(
|
||||
container: ExoSuitInventoryMenu,
|
||||
recipe: CraftingRecipe
|
||||
): List<Slot> {
|
||||
return container.craftingSlots
|
||||
}
|
||||
|
||||
override fun getInventorySlots(
|
||||
container: ExoSuitInventoryMenu,
|
||||
recipe: CraftingRecipe
|
||||
): List<Slot> {
|
||||
return container.playerInventorySlots
|
||||
}
|
||||
})
|
||||
|
||||
override fun getContainerClass(): Class<out ExoSuitInventoryMenu> {
|
||||
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<IRecipeSlotView>()
|
||||
|
||||
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<Item, MutableList<Slot>>()
|
||||
|
||||
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<Slot>? = 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<Slot>
|
||||
|
||||
if (bestMatchNN.size <= 99) {
|
||||
trulyBest = bestMatchNN
|
||||
} else {
|
||||
trulyBest = ArrayList<Slot>(99).also { for (i in 0 .. 39) it.add(bestMatchNN[i]) }
|
||||
}
|
||||
|
||||
kotlin.collections.ArrayDeque<Slot>(trulyBest.size).also {
|
||||
it.addAll(trulyBest)
|
||||
|
||||
it.sortWith { a, b ->
|
||||
a.item.count.compareTo(b.item.count)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val actions = ArrayList<MoveMultipleItemsPacket.Move>()
|
||||
|
||||
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<Item>()
|
||||
|
||||
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<IRecipeSlotView>(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<IRecipeSlotView> {
|
||||
return combined
|
||||
}
|
||||
|
||||
override fun getSlotViews(role: RecipeIngredientRole): MutableList<IRecipeSlotView> {
|
||||
return when (role) {
|
||||
RecipeIngredientRole.INPUT -> filteredInputs
|
||||
RecipeIngredientRole.OUTPUT -> outputs
|
||||
RecipeIngredientRole.CATALYST -> catalysts
|
||||
RecipeIngredientRole.RENDER_ONLY -> render
|
||||
}
|
||||
}
|
||||
|
||||
override fun findSlotByName(slotName: String): Optional<IRecipeSlotView> {
|
||||
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)
|
||||
}
|
||||
|
@ -331,3 +331,7 @@ fun FriendlyByteBuf.readItemType(): Item? {
|
||||
operator fun <T : Comparable<T>> StateHolder<*, *>.get(value: Property<T>): T {
|
||||
return getValue(value)
|
||||
}
|
||||
|
||||
fun <T> List<T>.toImmutableList(): ImmutableList<T> {
|
||||
return ImmutableList.copyOf(this)
|
||||
}
|
||||
|
@ -65,189 +65,6 @@ class SetCarriedPacket(val item: ItemStack) : MatteryPacket {
|
||||
}
|
||||
}
|
||||
|
||||
class MoveMultipleItemsPacket(val actions: Collection<Move>, val changedSlots: Map<Int, ItemStack>, val error: Throwable? = null) : MatteryPacket {
|
||||
data class Move(val sourceSlots: Collection<Int>, 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<Int, ItemStack> {
|
||||
val changedSlots = Int2ObjectArrayMap<ItemStack>()
|
||||
|
||||
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<NetworkEvent.Context>) {
|
||||
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<Move>(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<ItemStack>()
|
||||
|
||||
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)
|
||||
|
Loading…
Reference in New Issue
Block a user