package net.shadowfacts.phycon.block.extractor import alexiil.mc.lib.attributes.SearchOptions import alexiil.mc.lib.attributes.Simulation import alexiil.mc.lib.attributes.item.FixedItemInv import alexiil.mc.lib.attributes.item.ItemAttributes import alexiil.mc.lib.attributes.item.filter.ExactItemStackFilter import net.minecraft.block.BlockState import net.minecraft.item.ItemStack import net.minecraft.nbt.NbtCompound import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.shadowfacts.phycon.api.packet.Packet import net.shadowfacts.phycon.init.PhyBlockEntities import net.shadowfacts.phycon.block.DeviceBlockEntity import net.shadowfacts.phycon.block.FaceDeviceBlock import net.shadowfacts.phycon.component.ActivationController import net.shadowfacts.phycon.component.NetworkStackDispatcher import net.shadowfacts.phycon.component.finishTimedOutPendingInsertions import net.shadowfacts.phycon.component.handleItemStack import net.shadowfacts.phycon.packet.CapacityPacket import net.shadowfacts.phycon.packet.ItemStackPacket import net.shadowfacts.phycon.packet.RemoteActivationPacket import net.shadowfacts.phycon.util.ActivationMode import net.shadowfacts.phycon.util.ClientConfigurableDevice import kotlin.properties.Delegates /** * @author shadowfacts */ class ExtractorBlockEntity(pos: BlockPos, state: BlockState): DeviceBlockEntity(PhyBlockEntities.EXTRACTOR, pos, state), NetworkStackDispatcher, ActivationController.ActivatableDevice, ClientConfigurableDevice { companion object { val SLEEP_TIME = 40L } private val facing: Direction get() = cachedState[FaceDeviceBlock.FACING] private var inventory: FixedItemInv? = null override val pendingInsertions = mutableListOf() override val dispatchStackTimeout = 1L override val controller = ActivationController(SLEEP_TIME, this) fun updateInventory() { val offsetPos = pos.offset(facing) val option = SearchOptions.inDirection(facing) inventory = ItemAttributes.FIXED_INV.getFirstOrNull(world, offsetPos, option) } private fun getInventory(): FixedItemInv? { if (inventory == null) updateInventory() return inventory } override fun handle(packet: Packet) { when (packet) { is CapacityPacket -> handleCapacity(packet) is ItemStackPacket -> handleItemStack(packet) is RemoteActivationPacket -> controller.handleRemoteActivation(packet) } } override fun doHandleItemStack(packet: ItemStackPacket): ItemStack { // we can't insert things back into the inventory, so just let them spawn return packet.stack } override fun createPendingInsertion(stack: ItemStack) = PendingInsertion(stack, counter) override fun finishInsertion(insertion: PendingInsertion): ItemStack { val inventory = getInventory() ?: return insertion.stack // if the inventory has changed, the old slot index is meaningless if (inventory !== insertion.inventory) return insertion.stack val extracted = inventory.extractStack(insertion.inventorySlot, ExactItemStackFilter(insertion.stack), ItemStack.EMPTY, insertion.totalCapacity, Simulation.ACTION) if (extracted.isEmpty) return insertion.stack // if we extracted less than expected, make sure super.finishInsertion doesn't send more than we actually have insertion.stack = extracted return super.finishInsertion(insertion) } override fun tick() { super.tick() if (!world!!.isClient) { controller.tick() finishTimedOutPendingInsertions() } } override fun activate(): Boolean { val inventory = getInventory() ?: return false for (slot in 0 until inventory.slotCount) { val slotStack = inventory.getInvStack(slot) if (slotStack.isEmpty) continue val extractable = inventory.extractStack(slot, ExactItemStackFilter(slotStack), ItemStack.EMPTY, slotStack.count, Simulation.SIMULATE) if (extractable.isEmpty) continue dispatchItemStack(extractable) { insertion -> insertion.inventory = inventory insertion.inventorySlot = slot } return true } return false } override fun toCommonTag(tag: NbtCompound) { super.toCommonTag(tag) writeDeviceConfiguration(tag) } override fun fromCommonTag(tag: NbtCompound) { super.fromCommonTag(tag) loadDeviceConfiguration(tag) } override fun writeDeviceConfiguration(tag: NbtCompound) { tag.putString("ActivationMode", controller.activationMode.name) } override fun loadDeviceConfiguration(tag: NbtCompound) { controller.activationMode = ActivationMode.valueOf(tag.getString("ActivationMode")) } class PendingInsertion(stack: ItemStack, timestamp: Long): NetworkStackDispatcher.PendingInsertion(stack, timestamp) { lateinit var inventory: FixedItemInv var inventorySlot by Delegates.notNull() } }