diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/terminal/AbstractTerminalBlockEntity.kt b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/AbstractTerminalBlockEntity.kt index 1a6993b..f629c5f 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/block/terminal/AbstractTerminalBlockEntity.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/AbstractTerminalBlockEntity.kt @@ -1,6 +1,6 @@ package net.shadowfacts.phycon.block.terminal -import alexiil.mc.lib.attributes.item.ItemStackCollections +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant import net.fabricmc.fabric.api.transfer.v1.storage.Storage import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction @@ -52,8 +52,7 @@ abstract class AbstractTerminalBlockEntity(type: BlockEntityType<*>, pos: BlockP override val pendingInsertions = mutableListOf() override val dispatchStackTimeout = INSERTION_TIMEOUT - // TODO: need a ItemVariant -> Int/Long map - val cachedNetItems = ItemStackCollections.intMap() + val cachedNetItems = Object2IntOpenHashMap() private var requestInventoryTimestamp: Long? = null // todo: multiple players could have the terminal open simultaneously @@ -142,7 +141,7 @@ abstract class AbstractTerminalBlockEntity(type: BlockEntityType<*>, pos: BlockP val transaction = Transaction.openOuter() for (view in inventory.iterator(transaction)) { val amount = view.amount.toInt() - cachedNetItems.mergeInt(view.resource.toStack(), amount, IntBinaryOperator { a, b -> a + b }) + cachedNetItems.mergeInt(view.resource, amount, IntBinaryOperator { a, b -> a + b }) } transaction.close() } diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/terminal/AbstractTerminalScreenHandler.kt b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/AbstractTerminalScreenHandler.kt index b611980..1742519 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/block/terminal/AbstractTerminalScreenHandler.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/AbstractTerminalScreenHandler.kt @@ -1,24 +1,20 @@ package net.shadowfacts.phycon.block.terminal +import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant import net.minecraft.screen.slot.Slot import net.minecraft.screen.slot.SlotActionType import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.item.ItemStack -import net.minecraft.network.PacketByteBuf import net.minecraft.screen.ScreenHandler import net.minecraft.screen.ScreenHandlerType import net.minecraft.server.network.ServerPlayerEntity -import net.minecraft.util.Identifier import net.minecraft.util.registry.Registry import net.shadowfacts.phycon.DefaultPlugin -import net.shadowfacts.phycon.PhysicalConnectivity -import net.shadowfacts.phycon.init.PhyBlocks -import net.shadowfacts.phycon.init.PhyScreens import net.shadowfacts.phycon.networking.S2CTerminalUpdateDisplayedItems import net.shadowfacts.phycon.util.SortMode import net.shadowfacts.phycon.util.TerminalSettings -import net.shadowfacts.phycon.util.copyWithCount +import net.shadowfacts.phycon.util.name import java.lang.ref.WeakReference import kotlin.math.ceil import kotlin.math.max @@ -49,7 +45,7 @@ abstract class AbstractTerminalScreenHandler( field = value if (terminal.world!!.isClient) { itemsForDisplay = value.map { - it.stack.copyWithCount(it.amount) + it.variant.toStack(it.amount) } } } @@ -106,6 +102,8 @@ abstract class AbstractTerminalScreenHandler( return@filter true } } + // TODO: this is happening on the logical server, won't work with localization + // should filtering happen on the client? it.key.name.string.contains(searchQuery, true) } @@ -273,5 +271,5 @@ abstract class AbstractTerminalScreenHandler( fun isBufferSlot(id: Int) = id in bufferSlotsStart until playerSlotsStart fun isPlayerSlot(id: Int) = id >= playerSlotsStart - data class Entry(val stack: ItemStack, val amount: Int) + data class Entry(val variant: ItemVariant, val amount: Int) } diff --git a/src/main/kotlin/net/shadowfacts/phycon/block/terminal/CraftingTerminalBlockEntity.kt b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/CraftingTerminalBlockEntity.kt index 2b4ea54..06892c2 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/block/terminal/CraftingTerminalBlockEntity.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/block/terminal/CraftingTerminalBlockEntity.kt @@ -1,7 +1,9 @@ package net.shadowfacts.phycon.block.terminal -import alexiil.mc.lib.attributes.item.ItemStackCollections +import it.unimi.dsi.fastutil.ints.IntBinaryOperator +import it.unimi.dsi.fastutil.objects.Object2IntArrayMap import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory +import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant import net.minecraft.block.BlockState import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory @@ -20,7 +22,6 @@ import net.shadowfacts.phycon.util.equalsIgnoringAmount import net.shadowfacts.phycon.util.fromTag import net.shadowfacts.phycon.util.toTag import java.util.LinkedList -import kotlin.math.min /** * @author shadowfacts @@ -52,20 +53,23 @@ class CraftingTerminalBlockEntity(pos: BlockPos, state: BlockState) : } fun requestItemsForCrafting(maxAmount: Int) { - val amounts = ItemStackCollections.map() + // use an array map because we have at most 9 items + // values are bitfields of which slots contain the item + val stackToSlotsMap = Object2IntArrayMap() + val or = IntBinaryOperator { a, b -> a or b } for (i in 0 until craftingInv.size()) { val craftingInvStack = craftingInv.getStack(i) if (craftingInvStack.isEmpty) continue if (craftingInvStack.count >= craftingInvStack.maxCount) continue - if (craftingInvStack !in amounts) amounts[craftingInvStack] = IntArray(9) { 0 } - amounts[craftingInvStack]!![i] = min(maxAmount, craftingInvStack.maxCount - craftingInvStack.count) + stackToSlotsMap.mergeInt(ItemVariant.of(craftingInvStack), 1 shl i, or) } - for ((stack, amountPerSlot) in amounts) { - val total = amountPerSlot.sum() - val request = CraftingStackLocateRequest(stack, total, counter, amountPerSlot) + for ((variant, slots) in stackToSlotsMap) { + val total = slots.countOneBits() + val stack = variant.toStack() + val request = CraftingStackLocateRequest(stack, total, counter, slots) pendingRequests.add(request) sendPacket(LocateStackPacket(stack, ipAddress)) } @@ -90,19 +94,19 @@ class CraftingTerminalBlockEntity(pos: BlockPos, state: BlockState) : if (currentStack.count >= currentStack.maxCount) continue if (!currentStack.equalsIgnoringAmount(remaining)) continue - val toInsert = - minOf(remaining.count, currentStack.maxCount - currentStack.count, craftingReq.amountPerSlot[i]) - currentStack.count += toInsert - remaining.count -= toInsert - craftingReq.amountPerSlot[i] -= toInsert - craftingReq.received += toInsert + currentStack.count += 1 + remaining.count -= 1 + + craftingReq.slots = craftingReq.slots and (1 shl i).inv() + craftingReq.received += 1 if (remaining.isEmpty) { break } } - if (craftingReq.amountPerSlot.sum() == 0 || craftingReq.received >= craftingReq.totalResultAmount) { + // if slots == 0, there are no more slots needing items for this request + if (craftingReq.slots == 0 || craftingReq.received >= craftingReq.totalResultAmount) { completedCraftingStackRequests.remove(craftingReq) } @@ -132,7 +136,10 @@ class CraftingTerminalBlockEntity(pos: BlockPos, state: BlockState) : stack: ItemStack, amount: Int, timestamp: Long, - val amountPerSlot: IntArray, + /** + * Values are bitfields of which slots in the crafting inventory this request is for. + */ + var slots: Int, ) : StackLocateRequest(stack, amount, timestamp) { var received: Int = 0 } diff --git a/src/main/kotlin/net/shadowfacts/phycon/networking/S2CTerminalUpdateDisplayedItems.kt b/src/main/kotlin/net/shadowfacts/phycon/networking/S2CTerminalUpdateDisplayedItems.kt index 66ee69b..3a91775 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/networking/S2CTerminalUpdateDisplayedItems.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/networking/S2CTerminalUpdateDisplayedItems.kt @@ -10,9 +10,7 @@ import net.minecraft.network.PacketByteBuf import net.shadowfacts.phycon.PhysicalConnectivityClient import net.shadowfacts.phycon.block.terminal.AbstractTerminalBlockEntity import net.shadowfacts.phycon.block.terminal.AbstractTerminalScreenHandler -import net.shadowfacts.phycon.util.TerminalSettings -import net.shadowfacts.phycon.util.readItemStackWithoutCount -import net.shadowfacts.phycon.util.writeItemStackWithoutCount +import net.shadowfacts.phycon.util.* /** * @author shadowfacts @@ -35,7 +33,7 @@ object S2CTerminalUpdateDisplayedItems : ClientReceiver { buf.writeVarInt(entries.size) for (e in entries) { - buf.writeItemStackWithoutCount(e.stack) + buf.writeItemVariant(e.variant) buf.writeVarInt(e.amount) } @@ -58,7 +56,7 @@ object S2CTerminalUpdateDisplayedItems : ClientReceiver { val entryCount = buf.readVarInt() val entries = ArrayList(entryCount) for (i in 0 until entryCount) { - entries.add(AbstractTerminalScreenHandler.Entry(buf.readItemStackWithoutCount(), buf.readVarInt())) + entries.add(AbstractTerminalScreenHandler.Entry(buf.readItemVariant(), buf.readVarInt())) } val query = buf.readString() PhysicalConnectivityClient.terminalSettings.fromTag(buf.readNbt()!!) diff --git a/src/main/kotlin/net/shadowfacts/phycon/util/ItemStack.kt b/src/main/kotlin/net/shadowfacts/phycon/util/ItemStack.kt deleted file mode 100644 index c25c0da..0000000 --- a/src/main/kotlin/net/shadowfacts/phycon/util/ItemStack.kt +++ /dev/null @@ -1,10 +0,0 @@ -package net.shadowfacts.phycon.util - -import net.minecraft.item.ItemStack - -/** - * @author shadowfacts - */ -fun ItemStack.copyWithCount(count: Int): ItemStack { - return copy().also { it.count = count } -} diff --git a/src/main/kotlin/net/shadowfacts/phycon/util/ItemStackUtils.kt b/src/main/kotlin/net/shadowfacts/phycon/util/ItemStackUtils.kt index d12d83f..444a71b 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/util/ItemStackUtils.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/util/ItemStackUtils.kt @@ -6,6 +6,10 @@ import net.minecraft.item.ItemStack * @author shadowfacts */ +fun ItemStack.copyWithCount(count: Int): ItemStack { + return copy().also { it.count = count } +} + fun ItemStack.equalsIgnoringAmount(other: ItemStack): Boolean { if (this.isEmpty) return other.isEmpty if (other.isEmpty) return false diff --git a/src/main/kotlin/net/shadowfacts/phycon/util/ItemVariantUtils.kt b/src/main/kotlin/net/shadowfacts/phycon/util/ItemVariantUtils.kt new file mode 100644 index 0000000..478dca5 --- /dev/null +++ b/src/main/kotlin/net/shadowfacts/phycon/util/ItemVariantUtils.kt @@ -0,0 +1,18 @@ +package net.shadowfacts.phycon.util + +import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant +import net.minecraft.item.ItemStack +import net.minecraft.text.Text + +/** + * @author shadowfacts + */ + +val ItemVariant.name: Text + get() { + // don't use toStack because that copies the NBT + // we assume that items don't mutate NBT in getName + val stack = ItemStack(this.item, 1) + stack.nbt = this.nbt + return item.getName(stack) + } diff --git a/src/main/kotlin/net/shadowfacts/phycon/util/PacketByteBuf.kt b/src/main/kotlin/net/shadowfacts/phycon/util/PacketByteBuf.kt index 3b78fdf..5a9048a 100644 --- a/src/main/kotlin/net/shadowfacts/phycon/util/PacketByteBuf.kt +++ b/src/main/kotlin/net/shadowfacts/phycon/util/PacketByteBuf.kt @@ -1,5 +1,6 @@ package net.shadowfacts.phycon.util +import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.nbt.NbtCompound @@ -35,3 +36,11 @@ fun PacketByteBuf.readItemStackWithoutCount(): ItemStack { stack } } + +fun PacketByteBuf.writeItemVariant(variant: ItemVariant) { + variant.toPacket(this) +} + +fun PacketByteBuf.readItemVariant(): ItemVariant { + return ItemVariant.fromPacket(this) +}