Terminal: Rudimentary stack extraction
This commit is contained in:
parent
5d62e60bb2
commit
f4f4c7ff03
|
@ -7,8 +7,7 @@ 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
|
||||||
import net.shadowfacts.phycon.network.DeviceBlockEntity
|
import net.shadowfacts.phycon.network.DeviceBlockEntity
|
||||||
import net.shadowfacts.phycon.network.packet.ReadInventoryPacket
|
import net.shadowfacts.phycon.network.packet.*
|
||||||
import net.shadowfacts.phycon.network.packet.RequestInventoryPacket
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
|
@ -27,18 +26,38 @@ class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE) {
|
||||||
inventory = ItemAttributes.GROUPED_INV.getFirstOrNull(world, offsetPos, option)
|
inventory = ItemAttributes.GROUPED_INV.getFirstOrNull(world, offsetPos, option)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getInventory(): GroupedItemInv? {
|
||||||
|
// if we don't have an inventory, try to get one
|
||||||
|
// this happens when readAll is called before a neighbor state changes, such as immediately after world load
|
||||||
|
if (inventory == null) updateInventory()
|
||||||
|
return inventory
|
||||||
|
}
|
||||||
|
|
||||||
override fun handlePacket(packet: Packet) {
|
override fun handlePacket(packet: Packet) {
|
||||||
when (packet) {
|
when (packet) {
|
||||||
is RequestInventoryPacket -> handleRequestInventory(packet)
|
is RequestInventoryPacket -> handleRequestInventory(packet)
|
||||||
|
is LocateStackPacket -> handleLocateStack(packet)
|
||||||
|
is ExtractStackPacket -> handleExtractStack(packet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleRequestInventory(packet: RequestInventoryPacket) {
|
private fun handleRequestInventory(packet: RequestInventoryPacket) {
|
||||||
// if we don't have an inventory, try to get one
|
getInventory()?.also { inv ->
|
||||||
// this happens when readAll is called before a neighbor state changes, such as immediately after world load
|
enqueueToSingle(ReadInventoryPacket(inv, macAddress, packet.source))
|
||||||
if (inventory == null) updateInventory()
|
}
|
||||||
inventory?.also {
|
}
|
||||||
enqueueToSingle(ReadInventoryPacket(it, macAddress, packet.source))
|
|
||||||
|
private fun handleLocateStack(packet: LocateStackPacket) {
|
||||||
|
getInventory()?.also { inv ->
|
||||||
|
val amount = inv.getAmount(packet.stack)
|
||||||
|
enqueueToSingle(StackLocationPacket(packet.stack, amount, this, macAddress, packet.source))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleExtractStack(packet: ExtractStackPacket) {
|
||||||
|
getInventory()?.also { inv ->
|
||||||
|
val extracted = inv.extract(packet.stack, packet.amount)
|
||||||
|
enqueueToSingle(ItemStackPacket(extracted, macAddress, packet.source))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,10 @@ 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 net.fabricmc.fabric.api.block.entity.BlockEntityClientSerializable
|
import net.fabricmc.fabric.api.block.entity.BlockEntityClientSerializable
|
||||||
import net.fabricmc.fabric.api.container.ContainerProviderRegistry
|
import net.fabricmc.fabric.api.container.ContainerProviderRegistry
|
||||||
|
import net.minecraft.entity.ItemEntity
|
||||||
import net.minecraft.entity.player.PlayerEntity
|
import net.minecraft.entity.player.PlayerEntity
|
||||||
import net.minecraft.inventory.BasicInventory
|
import net.minecraft.inventory.BasicInventory
|
||||||
import net.minecraft.inventory.Inventory
|
import net.minecraft.inventory.Inventory
|
||||||
|
@ -15,20 +17,27 @@ import net.shadowfacts.phycon.api.packet.Packet
|
||||||
import net.shadowfacts.phycon.api.util.MACAddress
|
import net.shadowfacts.phycon.api.util.MACAddress
|
||||||
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.packet.DeviceRemovedPacket
|
import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlockEntity
|
||||||
import net.shadowfacts.phycon.network.packet.ReadInventoryPacket
|
import net.shadowfacts.phycon.network.packet.*
|
||||||
import net.shadowfacts.phycon.network.packet.RequestInventoryPacket
|
|
||||||
import net.shadowfacts.phycon.util.fromTag
|
import net.shadowfacts.phycon.util.fromTag
|
||||||
import net.shadowfacts.phycon.util.toTag
|
import net.shadowfacts.phycon.util.toTag
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
*/
|
*/
|
||||||
class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), InventoryListener, BlockEntityClientSerializable {
|
class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), InventoryListener, BlockEntityClientSerializable {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val LOCATE_REQUEST_TIMEOUT = 40 // ticks
|
||||||
|
}
|
||||||
|
|
||||||
private val inventoryCache = mutableMapOf<MACAddress, GroupedItemInv>()
|
private val inventoryCache = mutableMapOf<MACAddress, GroupedItemInv>()
|
||||||
val internalBuffer = BasicInventory(18)
|
val internalBuffer = BasicInventory(18)
|
||||||
|
|
||||||
|
private val pendingRequests = LinkedList<StackLocateRequest>()
|
||||||
|
|
||||||
private var observers = 0
|
private var observers = 0
|
||||||
val cachedNetItems = ItemStackCollections.intMap()
|
val cachedNetItems = ItemStackCollections.intMap()
|
||||||
var cachedSortedNetItems = listOf<ItemStack>()
|
var cachedSortedNetItems = listOf<ItemStack>()
|
||||||
|
@ -42,15 +51,47 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
|
||||||
when (packet) {
|
when (packet) {
|
||||||
is ReadInventoryPacket -> handleReadInventory(packet)
|
is ReadInventoryPacket -> handleReadInventory(packet)
|
||||||
is DeviceRemovedPacket -> handleDeviceRemoved(packet)
|
is DeviceRemovedPacket -> handleDeviceRemoved(packet)
|
||||||
|
is StackLocationPacket -> handleStackLocation(packet)
|
||||||
|
is ItemStackPacket -> handleItemStack(packet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleReadInventory(packet: ReadInventoryPacket) {
|
private fun handleReadInventory(packet: ReadInventoryPacket) {
|
||||||
inventoryCache[packet.source] = packet.inventory
|
inventoryCache[packet.source] = packet.inventory
|
||||||
|
updateNetItems()
|
||||||
|
sync()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleDeviceRemoved(packet: DeviceRemovedPacket) {
|
private fun handleDeviceRemoved(packet: DeviceRemovedPacket) {
|
||||||
inventoryCache.remove(packet.source)
|
inventoryCache.remove(packet.source)
|
||||||
|
updateNetItems()
|
||||||
|
sync()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleStackLocation(packet: StackLocationPacket) {
|
||||||
|
val request = pendingRequests.firstOrNull {
|
||||||
|
ItemStackUtil.areEqualIgnoreAmounts(it.stack, packet.stack)
|
||||||
|
}
|
||||||
|
if (request != null) {
|
||||||
|
request.results.add(packet.amount to packet.sourceInterface)
|
||||||
|
if (request.totalResultAmount >= request.amount || counter - request.timestamp >= LOCATE_REQUEST_TIMEOUT || request.results.size >= inventoryCache.size) {
|
||||||
|
pendingRequests.remove(request)
|
||||||
|
stackLocateRequestCompleted(request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleItemStack(packet: ItemStackPacket) {
|
||||||
|
// todo: handle merging stacks into the buffer better?
|
||||||
|
for (i in 0 until internalBuffer.invSize) {
|
||||||
|
if (internalBuffer.getInvStack(i).isEmpty) {
|
||||||
|
internalBuffer.setInvStack(i, packet.stack)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateNetItems() {
|
private fun updateNetItems() {
|
||||||
|
@ -61,6 +102,12 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
|
||||||
cachedNetItems.mergeInt(stack, amount) { a, b -> a + b }
|
cachedNetItems.mergeInt(stack, amount) { a, b -> a + b }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// todo: is the map necessary or is just the sorted list enough?
|
||||||
|
cachedSortedNetItems = cachedNetItems.object2IntEntrySet().sortedByDescending { it.intValue }.map {
|
||||||
|
val stack = it.key.copy()
|
||||||
|
stack.count = it.intValue
|
||||||
|
stack
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addObserver() {
|
fun addObserver() {
|
||||||
|
@ -75,7 +122,6 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
|
||||||
super.tick()
|
super.tick()
|
||||||
|
|
||||||
if (observers > 0 && (++counter % 20) == 0) {
|
if (observers > 0 && (++counter % 20) == 0) {
|
||||||
counter = 0
|
|
||||||
if (world!!.isClient) {
|
if (world!!.isClient) {
|
||||||
println(cachedNetItems)
|
println(cachedNetItems)
|
||||||
} else {
|
} else {
|
||||||
|
@ -87,6 +133,9 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
|
||||||
|
|
||||||
fun onActivate(player: PlayerEntity) {
|
fun onActivate(player: PlayerEntity) {
|
||||||
if (!world!!.isClient) {
|
if (!world!!.isClient) {
|
||||||
|
updateNetItems()
|
||||||
|
sync()
|
||||||
|
|
||||||
inventoryCache.clear()
|
inventoryCache.clear()
|
||||||
enqueueToSingle(RequestInventoryPacket(macAddress))
|
enqueueToSingle(RequestInventoryPacket(macAddress))
|
||||||
ContainerProviderRegistry.INSTANCE.openContainer(TerminalContainer.ID, player) { buf ->
|
ContainerProviderRegistry.INSTANCE.openContainer(TerminalContainer.ID, player) { buf ->
|
||||||
|
@ -96,8 +145,25 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
|
||||||
addObserver()
|
addObserver()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun requestItem(stack: ItemStack, amount: Int = stack.count) {
|
||||||
|
pendingRequests.add(StackLocateRequest(stack, amount, counter))
|
||||||
|
enqueueToSingle(LocateStackPacket(stack, macAddress))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stackLocateRequestCompleted(request: StackLocateRequest) {
|
||||||
|
// todo: also sort results by interface priority
|
||||||
|
val sortedResults = request.results.sortedByDescending { it.first }.toMutableList()
|
||||||
|
var amountRequested = 0
|
||||||
|
while (amountRequested < request.amount && sortedResults.isNotEmpty()) {
|
||||||
|
val (sourceAmount, sourceInterface) = sortedResults.removeAt(0)
|
||||||
|
val amountToRequest = min(sourceAmount, request.amount - amountRequested)
|
||||||
|
amountRequested += amountToRequest
|
||||||
|
enqueueToSingle(ExtractStackPacket(request.stack, amountToRequest, macAddress, sourceInterface.macAddress))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onInvChange(inv: Inventory) {
|
override fun onInvChange(inv: Inventory) {
|
||||||
if (inv == internalBuffer) {
|
if (inv == internalBuffer && !world!!.isClient) {
|
||||||
markDirty()
|
markDirty()
|
||||||
sync()
|
sync()
|
||||||
}
|
}
|
||||||
|
@ -126,7 +192,14 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun fromClientTag(tag: CompoundTag) {
|
override fun fromClientTag(tag: CompoundTag) {
|
||||||
internalBuffer.fromTag(tag.getList("InternalBuffer", 10))
|
tag.getList("InternalBuffer", 10)?.also { list ->
|
||||||
|
if (list.isNotEmpty()) {
|
||||||
|
internalBuffer.fromTag(list)
|
||||||
|
} else {
|
||||||
|
// todo: should this clear or just do nothing?
|
||||||
|
internalBuffer.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
val list = tag.getList("CachedNetItems", 10)
|
val list = tag.getList("CachedNetItems", 10)
|
||||||
cachedNetItems.clear()
|
cachedNetItems.clear()
|
||||||
for (entryTag in list) {
|
for (entryTag in list) {
|
||||||
|
@ -142,3 +215,13 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL), Invento
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class StackLocateRequest(
|
||||||
|
val stack: ItemStack,
|
||||||
|
val amount: Int,
|
||||||
|
val timestamp: Int,
|
||||||
|
var results: MutableSet<Pair<Int, InterfaceBlockEntity>> = mutableSetOf()
|
||||||
|
) {
|
||||||
|
val totalResultAmount: Int
|
||||||
|
get() = results.fold(0) { acc, (amount, _) -> acc + amount }
|
||||||
|
}
|
||||||
|
|
|
@ -2,10 +2,13 @@ package net.shadowfacts.phycon.network.block.terminal
|
||||||
|
|
||||||
import net.minecraft.container.Container
|
import net.minecraft.container.Container
|
||||||
import net.minecraft.container.Slot
|
import net.minecraft.container.Slot
|
||||||
|
import net.minecraft.container.SlotActionType
|
||||||
import net.minecraft.entity.player.PlayerEntity
|
import net.minecraft.entity.player.PlayerEntity
|
||||||
import net.minecraft.entity.player.PlayerInventory
|
import net.minecraft.entity.player.PlayerInventory
|
||||||
|
import net.minecraft.item.ItemStack
|
||||||
import net.minecraft.util.Identifier
|
import net.minecraft.util.Identifier
|
||||||
import net.shadowfacts.phycon.PhysicalConnectivity
|
import net.shadowfacts.phycon.PhysicalConnectivity
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
|
@ -52,4 +55,19 @@ class TerminalContainer(syncId: Int, playerInv: PlayerInventory, val terminal: T
|
||||||
|
|
||||||
terminal.removeObserver()
|
terminal.removeObserver()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onSlotClick(slotId: Int, clickData: Int, actionType: SlotActionType, player: PlayerEntity): ItemStack {
|
||||||
|
if (actionType == SlotActionType.QUICK_MOVE) {
|
||||||
|
if (slotId < 54) {
|
||||||
|
// the slot clicked was one of the network stacks
|
||||||
|
val slot = slotList[slotId]
|
||||||
|
val stack = slotList[slotId].stack
|
||||||
|
if (!stack.isEmpty && !player.world.isClient) {
|
||||||
|
terminal.requestItem(stack, max(stack.count, stack.maxCount))
|
||||||
|
}
|
||||||
|
return ItemStack.EMPTY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.onSlotClick(slotId, clickData, actionType, player)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 ExtractStackPacket(val stack: ItemStack, val amount: Int, 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 ItemStackPacket(val stack: ItemStack, 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 LocateStackPacket(val stack: ItemStack, source: MACAddress, destination: MACAddress = MACAddress.BROADCAST): BasePacket(source, destination) {
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
// todo: better name with LocateStackPacket
|
||||||
|
class StackLocationPacket(
|
||||||
|
val stack: ItemStack,
|
||||||
|
val amount: Int,
|
||||||
|
val sourceInterface: InterfaceBlockEntity,
|
||||||
|
source: MACAddress,
|
||||||
|
destination: MACAddress
|
||||||
|
): BasePacket(source, destination) {
|
||||||
|
}
|
Loading…
Reference in New Issue