Compare commits

...

2 Commits

Author SHA1 Message Date
Shadowfacts a88eda357c
Improve Miner handling of excess items 2021-02-17 23:03:21 -05:00
Shadowfacts 7034ce11ef
Fix ConcurrentModificationExceptions 2021-02-17 23:03:15 -05:00
4 changed files with 41 additions and 17 deletions

View File

@ -14,7 +14,11 @@ import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.api.util.IPAddress 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.block.terminal.TerminalBlockEntity
import net.shadowfacts.phycon.network.component.NetworkStackDispatcher
import net.shadowfacts.phycon.network.component.NetworkStackProvider 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 net.shadowfacts.phycon.network.packet.*
import kotlin.math.min import kotlin.math.min
@ -22,18 +26,24 @@ import kotlin.math.min
* @author shadowfacts * @author shadowfacts
*/ */
class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER), class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
NetworkStackProvider { NetworkStackProvider,
NetworkStackDispatcher<MinerBlockEntity.PendingInsertion> {
private val facing: Direction private val facing: Direction
get() = cachedState[MinerBlock.FACING] get() = cachedState[MinerBlock.FACING]
private val invProxy = MinerInvProxy(this) private val invProxy = MinerInvProxy(this)
override val pendingInsertions = mutableListOf<PendingInsertion>()
override val dispatchStackTimeout = TerminalBlockEntity.INSERTION_TIMEOUT
override fun handle(packet: Packet) { override fun handle(packet: Packet) {
when (packet) { when (packet) {
is RequestInventoryPacket -> handleRequestInventory(packet) is RequestInventoryPacket -> handleRequestInventory(packet)
is LocateStackPacket -> handleLocateStack(packet) is LocateStackPacket -> handleLocateStack(packet)
is ExtractStackPacket -> handleExtractStack(packet) is ExtractStackPacket -> handleExtractStack(packet)
is CapacityPacket -> handleCapacity(packet)
is ItemStackPacket -> handleItemStack(packet)
} }
} }
@ -50,7 +60,7 @@ class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
private fun handleExtractStack(packet: ExtractStackPacket) { private fun handleExtractStack(packet: ExtractStackPacket) {
// always recalculate immediately before breaking // always recalculate immediately before breaking
val drops = invProxy.getDrops(true) val drops = invProxy.getDrops(recalculate = true)
if (invProxy.getAmount(packet.stack) > 0) { if (invProxy.getAmount(packet.stack) > 0) {
world!!.breakBlock(pos.offset(facing), false) world!!.breakBlock(pos.offset(facing), false)
@ -77,11 +87,18 @@ class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
// dump any remaining drops into the network // dump any remaining drops into the network
for (droppedStack in drops) { for (droppedStack in drops) {
if (droppedStack.isEmpty) continue if (droppedStack.isEmpty) continue
sendPacket(ItemStackPacket(droppedStack, ipAddress, IPAddress.BROADCAST)) 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 { class MinerInvProxy(val miner: MinerBlockEntity): GroupedItemInvView {
private var cachedState: BlockState? = null private var cachedState: BlockState? = null
private var cachedDrops: List<ItemStack>? = null private var cachedDrops: List<ItemStack>? = null
@ -96,7 +113,7 @@ class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
fun getDrops(recalculate: Boolean = false): List<ItemStack> { fun getDrops(recalculate: Boolean = false): List<ItemStack> {
val targetPos = pos.offset(facing) val targetPos = pos.offset(facing)
val realState = world.getBlockState(targetPos) val realState = world.getBlockState(targetPos)
// todo: does BlockState.equals actually work? // todo: does BlockState.equals actually work or is reference equality fine for BlockStates?
if (cachedDrops == null || realState != cachedState || recalculate) { if (cachedDrops == null || realState != cachedState || recalculate) {
cachedState = realState cachedState = realState
val be = if (realState.block.hasBlockEntity()) world.getBlockEntity(targetPos) else null val be = if (realState.block.hasBlockEntity()) world.getBlockEntity(targetPos) else null
@ -124,4 +141,7 @@ class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
} }
} }
class PendingInsertion(stack: ItemStack, timestamp: Long): NetworkStackDispatcher.PendingInsertion<PendingInsertion>(stack, timestamp) {
}
} }

View File

@ -161,12 +161,11 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
private fun finishPendingRequests() { private fun finishPendingRequests() {
if (world!!.isClient) return if (world!!.isClient) return
if (pendingRequests.isEmpty()) return
for (request in pendingRequests) { val finishable = pendingRequests.filter { it.isFinishable(counter) }
if (request.isFinishable(counter)) { // stackLocateRequestCompleted removes the object from pendingRequests
stackLocateRequestCompleted(request) finishable.forEach(::stackLocateRequestCompleted)
}
}
} }
fun addObserver() { fun addObserver() {

View File

@ -14,13 +14,11 @@ interface ItemStackPacketHandler: PacketSink, PacketSource {
fun doHandleItemStack(packet: ItemStackPacket): ItemStack fun doHandleItemStack(packet: ItemStackPacket): ItemStack
} }
fun <BE> BE.handleItemStack(packet: ItemStackPacket) where BE: BlockEntity, BE: ItemStackPacketHandler { fun <Self> Self.handleItemStack(packet: ItemStackPacket) where Self: BlockEntity, Self: ItemStackPacketHandler {
// todo: is 5 a good number? // todo: is 5 a good number?
// the max bounce count should always be odd, so the item is spawned in-world at the // the max bounce count should always be odd, so the item is spawned in-world at the
if (packet.bounceCount == 5) { if (packet.bounceCount == 5) {
// todo: calculate entity spawn point by finding non-obstructed location spawnItemStack(packet.stack)
val entity = ItemEntity(world!!, pos.x.toDouble(), pos.y + 1.0, pos.z.toDouble(), packet.stack)
world!!.spawnEntity(entity)
return return
} }
val remainder = doHandleItemStack(packet) val remainder = doHandleItemStack(packet)
@ -28,4 +26,10 @@ fun <BE> BE.handleItemStack(packet: ItemStackPacket) where BE: BlockEntity, BE:
if (!remainder.isEmpty) { if (!remainder.isEmpty) {
sendPacket(ItemStackPacket(remainder, packet.bounceCount + 1, ipAddress, packet.source)) sendPacket(ItemStackPacket(remainder, packet.bounceCount + 1, ipAddress, packet.source))
} }
}
fun <Self> Self.spawnItemStack(stack: ItemStack) where Self: BlockEntity, Self: ItemStackPacketHandler {
// todo: calculate entity spawn point by finding non-obstructed location
val entity = ItemEntity(world!!, pos.x.toDouble(), pos.y + 1.0, pos.z.toDouble(), stack)
world!!.spawnEntity(entity)
} }

View File

@ -72,9 +72,10 @@ interface NetworkStackDispatcher<Insertion: NetworkStackDispatcher.PendingInsert
fun <Self, Insertion: NetworkStackDispatcher.PendingInsertion<Insertion>> Self.finishTimedOutPendingInsertions() where Self: BlockEntity, Self: NetworkStackDispatcher<Insertion> { fun <Self, Insertion: NetworkStackDispatcher.PendingInsertion<Insertion>> Self.finishTimedOutPendingInsertions() where Self: BlockEntity, Self: NetworkStackDispatcher<Insertion> {
if (world!!.isClient) return if (world!!.isClient) return
if (pendingInsertions.isEmpty()) return
for (insertion in pendingInsertions) { val finishable = pendingInsertions.filter { it.isFinishable(this) }
if (!insertion.isFinishable(this)) continue // finishInsertion removes the object from pendingInsertions
finishInsertion(insertion) finishable.forEach(::finishInsertion)
} // todo: if a timed-out insertion can't be finished, we should probably retry after some time (exponential backoff?)
} }