package net.shadowfacts.phycon.block.terminal import alexiil.mc.lib.attributes.item.ItemStackCollections import alexiil.mc.lib.attributes.item.ItemStackUtil import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory import net.minecraft.block.BlockState import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory.SimpleInventory import net.minecraft.item.ItemStack import net.minecraft.nbt.NbtCompound import net.minecraft.network.PacketByteBuf import net.minecraft.screen.ScreenHandler import net.minecraft.server.network.ServerPlayerEntity import net.minecraft.text.TranslatableText import net.minecraft.util.math.BlockPos import net.shadowfacts.phycon.init.PhyBlockEntities import net.shadowfacts.phycon.packet.ItemStackPacket import net.shadowfacts.phycon.packet.LocateStackPacket import net.shadowfacts.phycon.packet.RequestInventoryPacket import net.shadowfacts.phycon.util.fromTag import net.shadowfacts.phycon.util.toTag import java.util.LinkedList import kotlin.math.min /** * @author shadowfacts */ class CraftingTerminalBlockEntity(pos: BlockPos, state: BlockState): AbstractTerminalBlockEntity(PhyBlockEntities.CRAFTING_TERMINAL, pos, state) { val craftingInv = SimpleInventory(9) private val completedCraftingStackRequests = LinkedList() override fun onActivate(player: PlayerEntity) { if (!world!!.isClient) { updateAndSync() inventoryCache.clear() sendPacket(RequestInventoryPacket(ipAddress)) val factory = object: ExtendedScreenHandlerFactory { override fun createMenu(syncId: Int, playerInv: PlayerInventory, player: PlayerEntity): ScreenHandler? { return CraftingTerminalScreenHandler(syncId, playerInv, this@CraftingTerminalBlockEntity) } override fun getDisplayName() = TranslatableText("block.phycon.crafting_terminal") override fun writeScreenOpeningData(player: ServerPlayerEntity, buf: PacketByteBuf) { buf.writeBlockPos(this@CraftingTerminalBlockEntity.pos) } } player.openHandledScreen(factory) } } fun requestItemsForCrafting(maxAmount: Int) { val amounts = ItemStackCollections.map() 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) } for ((stack, amountPerSlot) in amounts) { val total = amountPerSlot.sum() val request = CraftingStackLocateRequest(stack, total, counter, amountPerSlot) pendingRequests.add(request) sendPacket(LocateStackPacket(stack, ipAddress)) } } override fun stackLocateRequestCompleted(request: StackLocateRequest) { if (request is CraftingStackLocateRequest) { completedCraftingStackRequests.add(request) } super.stackLocateRequestCompleted(request) } override fun doHandleItemStack(packet: ItemStackPacket): ItemStack { val craftingReq = completedCraftingStackRequests.find { ItemStackUtil.areEqualIgnoreAmounts(it.stack, packet.stack) } if (craftingReq != null) { var remaining = packet.stack.copy() for (i in 0 until craftingInv.size()) { val currentStack = craftingInv.getStack(i) if (currentStack.count >= currentStack.maxCount) continue if (!ItemStackUtil.areEqualIgnoreAmounts(currentStack, 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 if (remaining.isEmpty) { break } } if (craftingReq.amountPerSlot.sum() == 0 || craftingReq.received >= craftingReq.totalResultAmount) { completedCraftingStackRequests.remove(craftingReq) } if (!remaining.isEmpty) { remaining = internalBuffer.insert(remaining, TerminalBufferInventory.Mode.FROM_NETWORK) } updateAndSync() return remaining } else { return super.doHandleItemStack(packet) } } override fun toCommonTag(tag: NbtCompound) { super.toCommonTag(tag) tag.put("CraftingInv", craftingInv.toTag()) } override fun fromCommonTag(tag: NbtCompound) { super.fromCommonTag(tag) craftingInv.fromTag(tag.getList("CraftingInv", 10)) } class CraftingStackLocateRequest( stack: ItemStack, amount: Int, timestamp: Long, val amountPerSlot: IntArray, ): StackLocateRequest(stack, amount, timestamp) { var received: Int = 0 } }