169 lines
5.4 KiB
Kotlin
169 lines
5.4 KiB
Kotlin
|
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
|
||
|
import net.minecraft.block.BlockState
|
||
|
import net.minecraft.item.ItemStack
|
||
|
import net.minecraft.nbt.CompoundTag
|
||
|
import net.minecraft.util.math.Direction
|
||
|
import net.shadowfacts.phycon.api.packet.Packet
|
||
|
import net.shadowfacts.phycon.block.DeviceBlockEntity
|
||
|
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
|
||
|
import kotlin.math.min
|
||
|
|
||
|
/**
|
||
|
* @author shadowfacts
|
||
|
*/
|
||
|
class InserterBlockEntity: DeviceBlockEntity(PhyBlockEntities.INSERTER),
|
||
|
ItemStackPacketHandler,
|
||
|
ActivationController.ActivatableDevice {
|
||
|
|
||
|
companion object {
|
||
|
val SLEEP_TIME = 40L
|
||
|
val REQUEST_TIMEOUT = 40
|
||
|
}
|
||
|
|
||
|
private val facing: Direction
|
||
|
get() = cachedState[InserterBlock.FACING]
|
||
|
|
||
|
private var inventory: ItemInsertable? = null
|
||
|
private var currentRequest: PendingExtractRequest? = null
|
||
|
var stackToExtract = ItemStack.EMPTY
|
||
|
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
|
||
|
}
|
||
|
|
||
|
override fun toTag(tag: CompoundTag): CompoundTag {
|
||
|
tag.putString("ActivationMode", controller.activationMode.name)
|
||
|
tag.put("StackToExtract", stackToExtract.toTag(CompoundTag()))
|
||
|
tag.putInt("AmountToExtract", amountToExtract)
|
||
|
return super.toTag(tag)
|
||
|
}
|
||
|
|
||
|
override fun fromTag(state: BlockState, tag: CompoundTag) {
|
||
|
super.fromTag(state, tag)
|
||
|
controller.activationMode = ActivationMode.valueOf(tag.getString("ActivationMode"))
|
||
|
stackToExtract = ItemStack.fromTag(tag.getCompound("StackToExtract"))
|
||
|
amountToExtract = tag.getInt("AmountToExtract")
|
||
|
}
|
||
|
|
||
|
override fun toClientTag(tag: CompoundTag): CompoundTag {
|
||
|
tag.putString("ActivationMode", controller.activationMode.name)
|
||
|
tag.put("StackToExtract", stackToExtract.toTag(CompoundTag()))
|
||
|
tag.putInt("AmountToExtract", amountToExtract)
|
||
|
return super.toClientTag(tag)
|
||
|
}
|
||
|
|
||
|
override fun fromClientTag(tag: CompoundTag) {
|
||
|
super.fromClientTag(tag)
|
||
|
controller.activationMode = ActivationMode.valueOf(tag.getString("ActivationMode"))
|
||
|
stackToExtract = ItemStack.fromTag(tag.getCompound("StackToExtract"))
|
||
|
amountToExtract = tag.getInt("AmountToExtract")
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
}
|
||
|
}
|
||
|
}
|