package net.shadowfacts.phycon.network.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.api.util.IPAddress import net.shadowfacts.phycon.init.PhyBlockEntities import net.shadowfacts.phycon.network.DeviceBlockEntity import net.shadowfacts.phycon.network.block.terminal.TerminalBlockEntity import net.shadowfacts.phycon.network.component.NetworkStackDispatcher import net.shadowfacts.phycon.network.component.NetworkStackProvider import net.shadowfacts.phycon.network.component.handleItemStack import net.shadowfacts.phycon.network.component.spawnItemStack import net.shadowfacts.phycon.network.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) { } }