PhysicalConnectivity/src/main/kotlin/net/shadowfacts/phycon/block/inserter/InserterBlockEntity.kt

172 lines
5.4 KiB
Kotlin
Raw Normal View History

2021-02-28 22:56:25 +00:00
package net.shadowfacts.phycon.block.inserter
import alexiil.mc.lib.attributes.SearchOptions
import alexiil.mc.lib.attributes.Simulation
import alexiil.mc.lib.attributes.item.ItemAttributes
import alexiil.mc.lib.attributes.item.ItemInsertable
import alexiil.mc.lib.attributes.item.ItemStackUtil
2021-12-22 23:59:51 +00:00
import net.minecraft.block.BlockState
2021-02-28 22:56:25 +00:00
import net.minecraft.item.ItemStack
2021-12-22 23:59:51 +00:00
import net.minecraft.nbt.NbtCompound
import net.minecraft.util.math.BlockPos
2021-02-28 22:56:25 +00:00
import net.minecraft.util.math.Direction
import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.block.DeviceBlockEntity
import net.shadowfacts.phycon.block.FaceDeviceBlock
2021-02-28 22:56:25 +00:00
import net.shadowfacts.phycon.component.ActivationController
import net.shadowfacts.phycon.component.ItemStackPacketHandler
import net.shadowfacts.phycon.component.NetworkStackProvider
import net.shadowfacts.phycon.component.handleItemStack
import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.packet.*
import net.shadowfacts.phycon.util.ActivationMode
2021-03-03 03:32:30 +00:00
import net.shadowfacts.phycon.util.ClientConfigurableDevice
import net.shadowfacts.phycon.util.GhostInv
2021-02-28 22:56:25 +00:00
import kotlin.math.min
/**
* @author shadowfacts
*/
2021-12-22 23:59:51 +00:00
class InserterBlockEntity(pos: BlockPos, state: BlockState): DeviceBlockEntity(PhyBlockEntities.INSERTER, pos, state),
2021-02-28 22:56:25 +00:00
ItemStackPacketHandler,
2021-03-03 03:32:30 +00:00
ActivationController.ActivatableDevice,
ClientConfigurableDevice,
GhostInv {
2021-02-28 22:56:25 +00:00
companion object {
val SLEEP_TIME = 40L
val REQUEST_TIMEOUT = 40
}
private val facing: Direction
get() = cachedState[FaceDeviceBlock.FACING]
2021-02-28 22:56:25 +00:00
private var inventory: ItemInsertable? = null
private var currentRequest: PendingExtractRequest? = null
2021-03-01 03:01:45 +00:00
var stackToExtract: ItemStack = ItemStack.EMPTY
override var ghostSlotStack: ItemStack
get() = stackToExtract
set(value) { stackToExtract = value }
2021-02-28 22:56:25 +00:00
var amountToExtract = 1
override val controller = ActivationController(SLEEP_TIME, this)
fun updateInventory() {
val offsetPos = pos.offset(facing)
val option = SearchOptions.inDirection(facing)
inventory = ItemAttributes.INSERTABLE.getFirstOrNull(world, offsetPos, option)
}
private fun getInventory(): ItemInsertable? {
if (inventory == null) updateInventory()
return inventory
}
override fun handle(packet: Packet) {
when (packet) {
is RemoteActivationPacket -> controller.handleRemoteActivation(packet)
is StackLocationPacket -> handleStackLocation(packet)
is ItemStackPacket -> handleItemStack(packet)
}
}
override fun doHandleItemStack(packet: ItemStackPacket): ItemStack {
val inventory = getInventory()
return if (inventory != null) {
inventory.attemptInsertion(packet.stack, Simulation.ACTION)
} else {
// no inventory, entire stack remains
packet.stack
}
}
private fun handleStackLocation(packet: StackLocationPacket) {
val request = currentRequest
if (request != null && ItemStackUtil.areEqualIgnoreAmounts(request.stack, packet.stack)) {
request.results.add(packet.amount to packet.stackProvider)
if (request.isFinishable(counter)) {
finishRequest()
}
}
}
override fun tick() {
super.tick()
if (!world!!.isClient) {
controller.tick()
val request = currentRequest
if (request != null) {
if (request.isFinishable(counter)) {
finishRequest()
} else if (counter - request.timestamp >= REQUEST_TIMEOUT && request.totalAmount == 0) {
currentRequest = null
}
}
}
}
override fun activate(): Boolean {
if (currentRequest != null || stackToExtract.isEmpty) {
return false
}
// todo: configure me
currentRequest = PendingExtractRequest(stackToExtract, counter)
sendPacket(LocateStackPacket(stackToExtract, ipAddress))
return true
}
private fun finishRequest() {
val request = currentRequest ?: return
// todo: dedup with TerminalBlockEntity.stackLocateRequestCompleted
val actualAmount = min(min(request.stack.maxCount, request.totalAmount), amountToExtract)
val sortedResults = request.results.sortedByDescending { it.first }.toMutableList()
var amountRequested = 0
while (amountRequested < actualAmount && sortedResults.isNotEmpty()) {
val (sourceAmount, source) = sortedResults.removeAt(0)
val amountToRequest = min(sourceAmount, actualAmount - amountRequested)
amountRequested += amountToRequest
sendPacket(ExtractStackPacket(request.stack, amountToRequest, ipAddress, source.ipAddress))
}
currentRequest = null
}
2021-12-22 23:59:51 +00:00
override fun toCommonTag(tag: NbtCompound) {
super.toCommonTag(tag)
2021-03-03 03:32:30 +00:00
writeDeviceConfiguration(tag)
2021-12-22 23:59:51 +00:00
tag.put("StackToExtract", stackToExtract.writeNbt(NbtCompound()))
2021-02-28 22:56:25 +00:00
}
2021-12-22 23:59:51 +00:00
override fun fromCommonTag(tag: NbtCompound) {
super.fromCommonTag(tag)
2021-03-03 03:32:30 +00:00
loadDeviceConfiguration(tag)
2021-12-22 23:59:51 +00:00
stackToExtract = ItemStack.fromNbt(tag.getCompound("StackToExtract"))
2021-03-03 03:32:30 +00:00
}
2021-12-22 23:59:51 +00:00
override fun writeDeviceConfiguration(tag: NbtCompound) {
2021-03-03 03:32:30 +00:00
tag.putString("ActivationMode", controller.activationMode.name)
tag.putInt("AmountToExtract", amountToExtract)
}
2021-12-22 23:59:51 +00:00
override fun loadDeviceConfiguration(tag: NbtCompound) {
2021-03-03 03:32:30 +00:00
controller.activationMode = ActivationMode.valueOf(tag.getString("ActivationMode"))
2021-02-28 22:56:25 +00:00
amountToExtract = tag.getInt("AmountToExtract")
}
2021-03-03 03:32:30 +00:00
2021-02-28 22:56:25 +00:00
class PendingExtractRequest(
val stack: ItemStack,
val timestamp: Long,
var results: MutableSet<Pair<Int, NetworkStackProvider>> = mutableSetOf()
) {
val totalAmount: Int
get() = results.fold(0) { acc, (amount, _) -> acc + amount }
fun isFinishable(currentTimestamp: Long): Boolean {
return totalAmount >= stack.maxCount || (currentTimestamp - timestamp >= REQUEST_TIMEOUT && totalAmount > 0)
}
}
}