Terminal: Add basic insertion into inventories
This commit is contained in:
parent
b1c37595ab
commit
dedfcae79b
|
@ -15,6 +15,6 @@ object PhysicalConnectivityClient: ClientModInitializer {
|
|||
override fun onInitializeClient() {
|
||||
BlockRenderLayerMap.INSTANCE.putBlock(PhyBlocks.CABLE, RenderLayer.getTranslucent())
|
||||
|
||||
ScreenRegistry.register(PhyScreens.TERMINAL_SCREEN_HANDLER, ::TerminalScreen)
|
||||
ScreenRegistry.register(PhyScreens.TERMINAL_SCREEN_HANDLER, ::TerminalScreen)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,6 @@ import net.shadowfacts.phycon.network.block.terminal.TerminalScreenHandler
|
|||
|
||||
object PhyScreens {
|
||||
|
||||
val TERMINAL_SCREEN_HANDLER = ScreenHandlerRegistry.registerExtended(Identifier(PhysicalConnectivity.MODID, "terminal"), ::TerminalScreenHandler)
|
||||
val TERMINAL_SCREEN_HANDLER = ScreenHandlerRegistry.registerExtended(Identifier(PhysicalConnectivity.MODID, "terminal"), ::TerminalScreenHandler)
|
||||
|
||||
}
|
|
@ -1,8 +1,10 @@
|
|||
package net.shadowfacts.phycon.network.block.netinterface
|
||||
|
||||
import alexiil.mc.lib.attributes.SearchOptions
|
||||
import alexiil.mc.lib.attributes.Simulation
|
||||
import alexiil.mc.lib.attributes.item.GroupedItemInv
|
||||
import alexiil.mc.lib.attributes.item.ItemAttributes
|
||||
import net.minecraft.entity.ItemEntity
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.shadowfacts.phycon.api.packet.Packet
|
||||
import net.shadowfacts.phycon.init.PhyBlockEntities
|
||||
|
@ -38,6 +40,8 @@ class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE) {
|
|||
is RequestInventoryPacket -> handleRequestInventory(packet)
|
||||
is LocateStackPacket -> handleLocateStack(packet)
|
||||
is ExtractStackPacket -> handleExtractStack(packet)
|
||||
is CheckCapacityPacket -> handleCheckCapacity(packet)
|
||||
is ItemStackPacket -> handleItemStack(packet)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,4 +65,30 @@ class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE) {
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleCheckCapacity(packet: CheckCapacityPacket) {
|
||||
getInventory()?.also { inv ->
|
||||
val remaining = inv.attemptInsertion(packet.stack, Simulation.SIMULATE)
|
||||
val couldAccept = packet.stack.count - remaining.count
|
||||
sendToSingle(CapacityPacket(packet.stack, couldAccept, this, macAddress, packet.source))
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleItemStack(packet: ItemStackPacket) {
|
||||
val inventory = getInventory()
|
||||
if (inventory != null) {
|
||||
val remaining = inventory.insert(packet.stack)
|
||||
if (!remaining.isEmpty) {
|
||||
// todo: should this send whatever was left back to the terminal instead of dropping it?
|
||||
// todo: calculate entity spawn point by finding non-obstructed location
|
||||
val entity = ItemEntity(world!!, pos.x.toDouble(), pos.y + 1.0, pos.z.toDouble(), remaining)
|
||||
world!!.spawnEntity(entity)
|
||||
}
|
||||
} else {
|
||||
// todo: should the stack back to the terminal instead of dropping it?
|
||||
// todo: calculate entity spawn point by finding non-obstructed location
|
||||
val entity = ItemEntity(world!!, pos.x.toDouble(), pos.y + 1.0, pos.z.toDouble(), packet.stack)
|
||||
world!!.spawnEntity(entity)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package net.shadowfacts.phycon.network.block.terminal
|
|||
import alexiil.mc.lib.attributes.item.GroupedItemInv
|
||||
import alexiil.mc.lib.attributes.item.ItemStackCollections
|
||||
import alexiil.mc.lib.attributes.item.ItemStackUtil
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap
|
||||
import net.fabricmc.fabric.api.block.entity.BlockEntityClientSerializable
|
||||
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory
|
||||
import net.minecraft.block.BlockState
|
||||
|
@ -37,12 +38,14 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
|
|||
|
||||
companion object {
|
||||
val LOCATE_REQUEST_TIMEOUT = 40 // ticks
|
||||
val INSERTION_TIMEOUT = 40
|
||||
}
|
||||
|
||||
private val inventoryCache = mutableMapOf<MACAddress, GroupedItemInv>()
|
||||
val internalBuffer = TerminalBufferInventory(18)
|
||||
|
||||
private val pendingRequests = LinkedList<StackLocateRequest>()
|
||||
private val pendingInsertions = Int2ObjectArrayMap<PendingStackInsertion>()
|
||||
|
||||
private var observers = 0
|
||||
val cachedNetItems = ItemStackCollections.intMap()
|
||||
|
@ -59,6 +62,7 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
|
|||
is DeviceRemovedPacket -> handleDeviceRemoved(packet)
|
||||
is StackLocationPacket -> handleStackLocation(packet)
|
||||
is ItemStackPacket -> handleItemStack(packet)
|
||||
is CapacityPacket -> handleCapacity(packet)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,6 +98,24 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
|
|||
val entity = ItemEntity(world!!, pos.x.toDouble(), pos.y + 1.0, pos.z.toDouble(), remaining)
|
||||
world!!.spawnEntity(entity)
|
||||
}
|
||||
|
||||
// this happens outside the normal update loop because by receiving the item stack packet
|
||||
// we "know" how much the count in the source inventory has changed
|
||||
updateNetItems()
|
||||
sync()
|
||||
}
|
||||
|
||||
private fun handleCapacity(packet: CapacityPacket) {
|
||||
val insertion = pendingInsertions.values.firstOrNull {
|
||||
ItemStackUtil.areEqualIgnoreAmounts(packet.stack, it.stack)
|
||||
}
|
||||
if (insertion != null) {
|
||||
insertion.results.add(packet.capacity to packet.receivingInterface)
|
||||
if (insertion.totalCapacity >= insertion.stack.count || counter - insertion.timestamp >= INSERTION_TIMEOUT || insertion.results.size >= inventoryCache.size) {
|
||||
pendingInsertions.remove(insertion.bufferSlot)
|
||||
completeInsertion(insertion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateNetItems() {
|
||||
|
@ -112,6 +134,18 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
|
|||
}
|
||||
}
|
||||
|
||||
private fun beginInsertions() {
|
||||
if (world!!.isClient) return
|
||||
|
||||
for (slot in 0 until internalBuffer.size()) {
|
||||
if (internalBuffer.getMode(slot) != TerminalBufferInventory.Mode.TO_NETWORK) continue
|
||||
if (slot in pendingInsertions) continue
|
||||
val stack = internalBuffer.getStack(slot)
|
||||
pendingInsertions[slot] = PendingStackInsertion(slot, stack, counter)
|
||||
sendToSingle(CheckCapacityPacket(stack, macAddress, MACAddress.BROADCAST))
|
||||
}
|
||||
}
|
||||
|
||||
fun addObserver() {
|
||||
observers++
|
||||
}
|
||||
|
@ -126,6 +160,7 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
|
|||
println(cachedNetItems)
|
||||
} else {
|
||||
updateNetItems()
|
||||
beginInsertions()
|
||||
sync()
|
||||
}
|
||||
}
|
||||
|
@ -138,7 +173,7 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
|
|||
|
||||
inventoryCache.clear()
|
||||
sendToSingle(RequestInventoryPacket(macAddress))
|
||||
val factory = object: ExtendedScreenHandlerFactory {
|
||||
val factory = object: ExtendedScreenHandlerFactory {
|
||||
override fun createMenu(syncId: Int, playerInv: PlayerInventory, player: PlayerEntity): ScreenHandler? {
|
||||
return TerminalScreenHandler(syncId, playerInv, this@TerminalBlockEntity)
|
||||
}
|
||||
|
@ -171,6 +206,24 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
|
|||
}
|
||||
}
|
||||
|
||||
private fun completeInsertion(insertion: PendingStackInsertion) {
|
||||
// todo: also sort results by interface priority
|
||||
val sortedResults = insertion.results.sortedBy { it.first }.toMutableList()
|
||||
val remaining = insertion.stack
|
||||
while (!remaining.isEmpty && sortedResults.isNotEmpty()) {
|
||||
val (capacity, receivingInterface) = sortedResults.removeAt(0)
|
||||
if (capacity <= 0) continue
|
||||
sendToSingle(ItemStackPacket(remaining.copy(), macAddress, receivingInterface.macAddress))
|
||||
// todo: the interface should confirm how much was actually inserted, in case of race condition
|
||||
remaining.count -= capacity
|
||||
}
|
||||
internalBuffer.setStack(insertion.bufferSlot, remaining)
|
||||
|
||||
// as with extracting, we "know" the new amounts and so can update instantly without actually sending out packets
|
||||
updateNetItems()
|
||||
sync()
|
||||
}
|
||||
|
||||
override fun onInventoryChanged(inv: Inventory) {
|
||||
if (inv == internalBuffer && world != null && !world!!.isClient) {
|
||||
markDirty()
|
||||
|
@ -234,3 +287,13 @@ data class StackLocateRequest(
|
|||
val totalResultAmount: Int
|
||||
get() = results.fold(0) { acc, (amount, _) -> acc + amount }
|
||||
}
|
||||
|
||||
data class PendingStackInsertion(
|
||||
val bufferSlot: Int,
|
||||
val stack: ItemStack,
|
||||
val timestamp: Int,
|
||||
val results: MutableSet<Pair<Int, InterfaceBlockEntity>> = mutableSetOf(),
|
||||
) {
|
||||
val totalCapacity: Int
|
||||
get() = results.fold(0) { acc, (amount, _) -> acc + amount }
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ class TerminalBufferInventory(size: Int): SimpleInventory(size) {
|
|||
TO_NETWORK, FROM_NETWORK, UNASSIGNED
|
||||
}
|
||||
|
||||
// todo: modes should be saved to NBT
|
||||
private val modes = Array(size) { Mode.UNASSIGNED }
|
||||
|
||||
fun insertFromNetwork(stack: ItemStack): ItemStack {
|
||||
|
@ -56,4 +57,12 @@ class TerminalBufferInventory(size: Int): SimpleInventory(size) {
|
|||
super.setStack(slot, stack)
|
||||
}
|
||||
|
||||
fun getMode(slot: Int): Mode {
|
||||
return modes[slot]
|
||||
}
|
||||
|
||||
fun markSlot(slot: Int, mode: Mode) {
|
||||
modes[slot] = mode
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -83,6 +83,13 @@ class TerminalScreenHandler(syncId: Int, playerInv: PlayerInventory, val termina
|
|||
}
|
||||
}
|
||||
return ItemStack.EMPTY
|
||||
} else if (slotId in 54 until 72) {
|
||||
// internal buffer
|
||||
if (actionType == SlotActionType.PICKUP && !player.inventory.cursorStack.isEmpty) {
|
||||
// placing cursor stack into buffer
|
||||
val bufferSlot = slotId - 54 // subtract 54 to convert the handler slot ID to a valid buffer index
|
||||
terminal.internalBuffer.markSlot(bufferSlot, TerminalBufferInventory.Mode.TO_NETWORK)
|
||||
}
|
||||
}
|
||||
return super.onSlotClick(slotId, clickData, actionType, player)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package net.shadowfacts.phycon.network.packet
|
||||
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.shadowfacts.phycon.api.util.MACAddress
|
||||
import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlockEntity
|
||||
|
||||
/**
|
||||
* @author shadowfacts
|
||||
*/
|
||||
class CapacityPacket(val stack: ItemStack, val capacity: Int, val receivingInterface: InterfaceBlockEntity, source: MACAddress, destination: MACAddress): BasePacket(source, destination) {
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package net.shadowfacts.phycon.network.packet
|
||||
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.shadowfacts.phycon.api.util.MACAddress
|
||||
|
||||
/**
|
||||
* @author shadowfacts
|
||||
*/
|
||||
class CheckCapacityPacket(val stack: ItemStack, source: MACAddress, destination: MACAddress): BasePacket(source, destination) {
|
||||
}
|
Loading…
Reference in New Issue