Convert extractor to use NetworkStackDispatcher

This commit is contained in:
Shadowfacts 2021-02-24 22:09:24 -05:00
parent d621e9e089
commit 9252a35dae
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
2 changed files with 51 additions and 22 deletions

View File

@ -1,26 +1,31 @@
package net.shadowfacts.phycon.network.block.extractor package net.shadowfacts.phycon.network.block.extractor
import alexiil.mc.lib.attributes.SearchOptions import alexiil.mc.lib.attributes.SearchOptions
import alexiil.mc.lib.attributes.item.GroupedItemInv 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.ItemAttributes
import alexiil.mc.lib.attributes.item.filter.ExactItemStackFilter
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.item.ItemStack
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.util.math.Direction import net.minecraft.util.math.Direction
import net.shadowfacts.phycon.api.packet.Packet import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.api.util.IPAddress
import net.shadowfacts.phycon.init.PhyBlockEntities import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.network.DeviceBlockEntity import net.shadowfacts.phycon.network.DeviceBlockEntity
import net.shadowfacts.phycon.network.component.ActivationController import net.shadowfacts.phycon.network.component.ActivationController
import net.shadowfacts.phycon.network.component.NetworkStackDispatcher
import net.shadowfacts.phycon.network.component.handleItemStack
import net.shadowfacts.phycon.network.packet.CapacityPacket import net.shadowfacts.phycon.network.packet.CapacityPacket
import net.shadowfacts.phycon.network.packet.CheckCapacityPacket
import net.shadowfacts.phycon.network.packet.ItemStackPacket import net.shadowfacts.phycon.network.packet.ItemStackPacket
import net.shadowfacts.phycon.network.packet.RemoteActivationPacket import net.shadowfacts.phycon.network.packet.RemoteActivationPacket
import net.shadowfacts.phycon.util.ActivationMode import net.shadowfacts.phycon.util.ActivationMode
import kotlin.properties.Delegates
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR), class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR),
NetworkStackDispatcher<ExtractorBlockEntity.PendingInsertion>,
ActivationController.ActivatableDevice { ActivationController.ActivatableDevice {
companion object { companion object {
@ -30,17 +35,18 @@ class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR),
private val facing: Direction private val facing: Direction
get() = cachedState[ExtractorBlock.FACING] get() = cachedState[ExtractorBlock.FACING]
private var inventory: GroupedItemInv? = null private var inventory: FixedItemInv? = null
private var shouldExtract = false override val pendingInsertions = mutableListOf<PendingInsertion>()
override val dispatchStackTimeout = 40L
override val controller = ActivationController(SLEEP_TIME, this) override val controller = ActivationController(SLEEP_TIME, this)
fun updateInventory() { fun updateInventory() {
val offsetPos = pos.offset(facing) val offsetPos = pos.offset(facing)
val option = SearchOptions.inDirection(facing) val option = SearchOptions.inDirection(facing)
inventory = ItemAttributes.GROUPED_INV.getFirstOrNull(world, offsetPos, option) inventory = ItemAttributes.FIXED_INV.getFirstOrNull(world, offsetPos, option)
} }
private fun getInventory(): GroupedItemInv? { private fun getInventory(): FixedItemInv? {
if (inventory == null) updateInventory() if (inventory == null) updateInventory()
return inventory return inventory
} }
@ -48,18 +54,27 @@ class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR),
override fun handle(packet: Packet) { override fun handle(packet: Packet) {
when (packet) { when (packet) {
is CapacityPacket -> handleCapacity(packet) is CapacityPacket -> handleCapacity(packet)
is ItemStackPacket -> handleItemStack(packet)
is RemoteActivationPacket -> controller.handleRemoteActivation(packet) is RemoteActivationPacket -> controller.handleRemoteActivation(packet)
} }
} }
private fun handleCapacity(packet: CapacityPacket) { override fun doHandleItemStack(packet: ItemStackPacket): ItemStack {
if (shouldExtract && packet.capacity > 0) { // we can't insert things back into the inventory, so just let them spawn
getInventory()?.also { inv -> return packet.stack
shouldExtract = false
val extracted = inv.extract(packet.stack, packet.capacity)
sendPacket(ItemStackPacket(extracted, ipAddress, packet.stackReceiver.ipAddress))
}
} }
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() { override fun tick() {
@ -72,11 +87,17 @@ class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR),
override fun activate(): Boolean { override fun activate(): Boolean {
val inventory = getInventory() ?: return false val inventory = getInventory() ?: return false
val stack = inventory.storedStacks.firstOrNull() ?: return false for (slot in 0 until inventory.slotCount) {
shouldExtract = true val slotStack = inventory.getInvStack(slot)
sendPacket(CheckCapacityPacket(stack, ipAddress, IPAddress.BROADCAST)) if (slotStack.isEmpty) continue
dispatchItemStack(slotStack) { insertion ->
insertion.inventory = inventory
insertion.inventorySlot = slot
}
return true return true
} }
return false
}
override fun toTag(tag: CompoundTag): CompoundTag { override fun toTag(tag: CompoundTag): CompoundTag {
tag.putString("ActivationMode", controller.activationMode.name) tag.putString("ActivationMode", controller.activationMode.name)
@ -97,4 +118,9 @@ class ExtractorBlockEntity: DeviceBlockEntity(PhyBlockEntities.EXTRACTOR),
super.fromClientTag(tag) super.fromClientTag(tag)
controller.activationMode = ActivationMode.valueOf(tag.getString("ActivationMode")) controller.activationMode = ActivationMode.valueOf(tag.getString("ActivationMode"))
} }
class PendingInsertion(stack: ItemStack, timestamp: Long): NetworkStackDispatcher.PendingInsertion<PendingInsertion>(stack, timestamp) {
lateinit var inventory: FixedItemInv
var inventorySlot by Delegates.notNull<Int>()
}
} }

View File

@ -33,7 +33,8 @@ interface NetworkStackDispatcher<Insertion: NetworkStackDispatcher.PendingInsert
if (insertion != null) { if (insertion != null) {
insertion.results.add(packet.capacity to packet.stackReceiver) insertion.results.add(packet.capacity to packet.stackReceiver)
if (insertion.isFinishable(this)) { if (insertion.isFinishable(this)) {
finishInsertion(insertion) val remaining = finishInsertion(insertion)
// todo: do something with remaining
} }
} }
} }
@ -43,7 +44,8 @@ interface NetworkStackDispatcher<Insertion: NetworkStackDispatcher.PendingInsert
// todo: also sort results by interface priority // todo: also sort results by interface priority
val sortedResults = insertion.results.sortedBy { it.first }.toMutableList() val sortedResults = insertion.results.sortedBy { it.first }.toMutableList()
val remaining = insertion.stack // copy the insertion stack so subclasses that override this method can still see the originally dispatched stack after the super call
val remaining = insertion.stack.copy()
while (!remaining.isEmpty && sortedResults.isNotEmpty()) { while (!remaining.isEmpty && sortedResults.isNotEmpty()) {
val (capacity, receivingInterface) = sortedResults.removeFirst() val (capacity, receivingInterface) = sortedResults.removeFirst()
if (capacity <= 0) continue if (capacity <= 0) continue
@ -56,7 +58,7 @@ interface NetworkStackDispatcher<Insertion: NetworkStackDispatcher.PendingInsert
} }
open class PendingInsertion<Self: PendingInsertion<Self>>( open class PendingInsertion<Self: PendingInsertion<Self>>(
val stack: ItemStack, var stack: ItemStack,
val timestamp: Long val timestamp: Long
) { ) {
val results = mutableSetOf<Pair<Int, NetworkStackReceiver>>() val results = mutableSetOf<Pair<Int, NetworkStackReceiver>>()
@ -77,5 +79,6 @@ fun <Self, Insertion: NetworkStackDispatcher.PendingInsertion<Insertion>> Self.f
val finishable = pendingInsertions.filter { it.isFinishable(this) } val finishable = pendingInsertions.filter { it.isFinishable(this) }
// finishInsertion removes the object from pendingInsertions // finishInsertion removes the object from pendingInsertions
finishable.forEach(::finishInsertion) finishable.forEach(::finishInsertion)
// todo: do something with remaining?
// todo: if a timed-out insertion can't be finished, we should probably retry after some time (exponential backoff?) // todo: if a timed-out insertion can't be finished, we should probably retry after some time (exponential backoff?)
} }