package net.shadowfacts.phycon.block.miner import alexiil.mc.lib.attributes.item.GroupedItemInvView import alexiil.mc.lib.attributes.item.ItemStackUtil import alexiil.mc.lib.attributes.item.filter.ItemFilter import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.item.ItemStack import net.minecraft.server.world.ServerWorld import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.world.World import net.shadowfacts.phycon.api.packet.Packet import net.shadowfacts.phycon.init.PhyBlockEntities import net.shadowfacts.phycon.block.DeviceBlockEntity import net.shadowfacts.phycon.block.terminal.TerminalBlockEntity import net.shadowfacts.phycon.component.NetworkStackDispatcher import net.shadowfacts.phycon.component.NetworkStackProvider import net.shadowfacts.phycon.component.handleItemStack import net.shadowfacts.phycon.packet.* import kotlin.math.min /** * @author shadowfacts */ class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER), NetworkStackProvider, NetworkStackDispatcher { private val facing: Direction get() = cachedState[MinerBlock.FACING] private val invProxy = MinerInvProxy(this) override val pendingInsertions = mutableListOf() override val dispatchStackTimeout = TerminalBlockEntity.INSERTION_TIMEOUT override fun handle(packet: Packet) { when (packet) { is RequestInventoryPacket -> handleRequestInventory(packet) is LocateStackPacket -> handleLocateStack(packet) is ExtractStackPacket -> handleExtractStack(packet) is CapacityPacket -> handleCapacity(packet) is ItemStackPacket -> handleItemStack(packet) } } private fun handleRequestInventory(packet: RequestInventoryPacket) { sendPacket(ReadInventoryPacket(invProxy, ipAddress, packet.source)) } private fun handleLocateStack(packet: LocateStackPacket) { val amount = invProxy.getAmount(packet.stack) if (amount > 0) { sendPacket(StackLocationPacket(packet.stack, amount, this, ipAddress, packet.source)) } } private fun handleExtractStack(packet: ExtractStackPacket) { // always recalculate immediately before breaking val drops = invProxy.getDrops(recalculate = true) if (invProxy.getAmount(packet.stack) > 0) { world!!.breakBlock(pos.offset(facing), false) // send the requested amount back to the requester var remaining = packet.amount for (droppedStack in drops) { if (remaining <= 0) { break } if (!ItemStackUtil.areEqualIgnoreAmounts(droppedStack, packet.stack)) { continue } val copy = droppedStack.copy() val toDecr = min(droppedStack.count, remaining) droppedStack.decrement(toDecr) remaining -= toDecr // todo: should this try to combine stacks and send as few packets as possible? copy.count = toDecr sendPacket(ItemStackPacket(copy, ipAddress, packet.source)) } // dump any remaining drops into the network for (droppedStack in drops) { if (droppedStack.isEmpty) continue dispatchItemStack(droppedStack) } } } override fun doHandleItemStack(packet: ItemStackPacket): ItemStack { // miner can't receive stacks, so remaining is the entire packet stack return packet.stack } override fun createPendingInsertion(stack: ItemStack) = PendingInsertion(stack, counter) class MinerInvProxy(val miner: MinerBlockEntity): GroupedItemInvView { private var cachedState: BlockState? = null private var cachedDrops: List? = null private val world: World get() = miner.world!! private val pos: BlockPos get() = miner.pos!! private val facing: Direction get() = miner.facing fun getDrops(recalculate: Boolean = false): List { val targetPos = pos.offset(facing) val realState = world.getBlockState(targetPos) // todo: does BlockState.equals actually work or is reference equality fine for BlockStates? if (cachedDrops == null || realState != cachedState || recalculate) { cachedState = realState val be = if (realState.block.hasBlockEntity()) world.getBlockEntity(targetPos) else null cachedDrops = Block.getDroppedStacks(realState, world as ServerWorld, targetPos, be) } return cachedDrops!! } override fun getStoredStacks(): Set { return getDrops().toSet() } override fun getTotalCapacity(): Int { return Int.MAX_VALUE } override fun getStatistics(filter: ItemFilter): GroupedItemInvView.ItemInvStatistic { var totalCount = 0 for (s in getDrops()) { if (filter.matches(s)) { totalCount += s.count } } return GroupedItemInvView.ItemInvStatistic(filter, totalCount, 0, Int.MAX_VALUE) } } class PendingInsertion(stack: ItemStack, timestamp: Long): NetworkStackDispatcher.PendingInsertion(stack, timestamp) { } }