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() {
|
override fun onInitializeClient() {
|
||||||
BlockRenderLayerMap.INSTANCE.putBlock(PhyBlocks.CABLE, RenderLayer.getTranslucent())
|
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 {
|
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
|
package net.shadowfacts.phycon.network.block.netinterface
|
||||||
|
|
||||||
import alexiil.mc.lib.attributes.SearchOptions
|
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.GroupedItemInv
|
||||||
import alexiil.mc.lib.attributes.item.ItemAttributes
|
import alexiil.mc.lib.attributes.item.ItemAttributes
|
||||||
|
import net.minecraft.entity.ItemEntity
|
||||||
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.init.PhyBlockEntities
|
import net.shadowfacts.phycon.init.PhyBlockEntities
|
||||||
|
@ -38,6 +40,8 @@ class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE) {
|
||||||
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 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.GroupedItemInv
|
||||||
import alexiil.mc.lib.attributes.item.ItemStackCollections
|
import alexiil.mc.lib.attributes.item.ItemStackCollections
|
||||||
import alexiil.mc.lib.attributes.item.ItemStackUtil
|
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.block.entity.BlockEntityClientSerializable
|
||||||
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory
|
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory
|
||||||
import net.minecraft.block.BlockState
|
import net.minecraft.block.BlockState
|
||||||
|
@ -37,12 +38,14 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val LOCATE_REQUEST_TIMEOUT = 40 // ticks
|
val LOCATE_REQUEST_TIMEOUT = 40 // ticks
|
||||||
|
val INSERTION_TIMEOUT = 40
|
||||||
}
|
}
|
||||||
|
|
||||||
private val inventoryCache = mutableMapOf<MACAddress, GroupedItemInv>()
|
private val inventoryCache = mutableMapOf<MACAddress, GroupedItemInv>()
|
||||||
val internalBuffer = TerminalBufferInventory(18)
|
val internalBuffer = TerminalBufferInventory(18)
|
||||||
|
|
||||||
private val pendingRequests = LinkedList<StackLocateRequest>()
|
private val pendingRequests = LinkedList<StackLocateRequest>()
|
||||||
|
private val pendingInsertions = Int2ObjectArrayMap<PendingStackInsertion>()
|
||||||
|
|
||||||
private var observers = 0
|
private var observers = 0
|
||||||
val cachedNetItems = ItemStackCollections.intMap()
|
val cachedNetItems = ItemStackCollections.intMap()
|
||||||
|
@ -59,6 +62,7 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
|
||||||
is DeviceRemovedPacket -> handleDeviceRemoved(packet)
|
is DeviceRemovedPacket -> handleDeviceRemoved(packet)
|
||||||
is StackLocationPacket -> handleStackLocation(packet)
|
is StackLocationPacket -> handleStackLocation(packet)
|
||||||
is ItemStackPacket -> handleItemStack(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)
|
val entity = ItemEntity(world!!, pos.x.toDouble(), pos.y + 1.0, pos.z.toDouble(), remaining)
|
||||||
world!!.spawnEntity(entity)
|
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() {
|
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() {
|
fun addObserver() {
|
||||||
observers++
|
observers++
|
||||||
}
|
}
|
||||||
|
@ -126,6 +160,7 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
|
||||||
println(cachedNetItems)
|
println(cachedNetItems)
|
||||||
} else {
|
} else {
|
||||||
updateNetItems()
|
updateNetItems()
|
||||||
|
beginInsertions()
|
||||||
sync()
|
sync()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,7 +173,7 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
|
||||||
|
|
||||||
inventoryCache.clear()
|
inventoryCache.clear()
|
||||||
sendToSingle(RequestInventoryPacket(macAddress))
|
sendToSingle(RequestInventoryPacket(macAddress))
|
||||||
val factory = object: ExtendedScreenHandlerFactory {
|
val factory = object: ExtendedScreenHandlerFactory {
|
||||||
override fun createMenu(syncId: Int, playerInv: PlayerInventory, player: PlayerEntity): ScreenHandler? {
|
override fun createMenu(syncId: Int, playerInv: PlayerInventory, player: PlayerEntity): ScreenHandler? {
|
||||||
return TerminalScreenHandler(syncId, playerInv, this@TerminalBlockEntity)
|
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) {
|
override fun onInventoryChanged(inv: Inventory) {
|
||||||
if (inv == internalBuffer && world != null && !world!!.isClient) {
|
if (inv == internalBuffer && world != null && !world!!.isClient) {
|
||||||
markDirty()
|
markDirty()
|
||||||
|
@ -234,3 +287,13 @@ data class StackLocateRequest(
|
||||||
val totalResultAmount: Int
|
val totalResultAmount: Int
|
||||||
get() = results.fold(0) { acc, (amount, _) -> acc + amount }
|
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
|
TO_NETWORK, FROM_NETWORK, UNASSIGNED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo: modes should be saved to NBT
|
||||||
private val modes = Array(size) { Mode.UNASSIGNED }
|
private val modes = Array(size) { Mode.UNASSIGNED }
|
||||||
|
|
||||||
fun insertFromNetwork(stack: ItemStack): ItemStack {
|
fun insertFromNetwork(stack: ItemStack): ItemStack {
|
||||||
|
@ -56,4 +57,12 @@ class TerminalBufferInventory(size: Int): SimpleInventory(size) {
|
||||||
super.setStack(slot, stack)
|
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
|
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)
|
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