81 lines
2.7 KiB
Kotlin
81 lines
2.7 KiB
Kotlin
package net.shadowfacts.phycon.network.component
|
|
|
|
import alexiil.mc.lib.attributes.item.ItemStackUtil
|
|
import net.minecraft.block.entity.BlockEntity
|
|
import net.minecraft.item.ItemStack
|
|
import net.shadowfacts.phycon.api.util.IPAddress
|
|
import net.shadowfacts.phycon.network.packet.CapacityPacket
|
|
import net.shadowfacts.phycon.network.packet.CheckCapacityPacket
|
|
import net.shadowfacts.phycon.network.packet.ItemStackPacket
|
|
|
|
/**
|
|
* @author shadowfacts
|
|
*/
|
|
interface NetworkStackDispatcher<Insertion: NetworkStackDispatcher.PendingInsertion<Insertion>>: ItemStackPacketHandler {
|
|
|
|
val counter: Long
|
|
val dispatchStackTimeout: Long
|
|
val pendingInsertions: MutableList<Insertion>
|
|
|
|
fun createPendingInsertion(stack: ItemStack): Insertion
|
|
|
|
fun dispatchItemStack(stack: ItemStack, modifyInsertion: ((Insertion) -> Unit)? = null) {
|
|
val insertion = createPendingInsertion(stack)
|
|
modifyInsertion?.invoke(insertion)
|
|
pendingInsertions.add(insertion)
|
|
sendPacket(CheckCapacityPacket(insertion.stack, ipAddress, IPAddress.BROADCAST))
|
|
}
|
|
|
|
fun handleCapacity(packet: CapacityPacket) {
|
|
val insertion = pendingInsertions.firstOrNull {
|
|
ItemStackUtil.areEqualIgnoreAmounts(packet.stack, it.stack)
|
|
}
|
|
if (insertion != null) {
|
|
insertion.results.add(packet.capacity to packet.stackReceiver)
|
|
if (insertion.isFinishable(this)) {
|
|
finishInsertion(insertion)
|
|
}
|
|
}
|
|
}
|
|
|
|
fun finishInsertion(insertion: Insertion): ItemStack {
|
|
pendingInsertions.remove(insertion)
|
|
|
|
// 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.removeFirst()
|
|
if (capacity <= 0) continue
|
|
sendPacket(ItemStackPacket(remaining.copy(), ipAddress, receivingInterface.ipAddress))
|
|
// todo: the destination should confirm how much was actually inserted, in case of race condition
|
|
remaining.count -= capacity
|
|
}
|
|
|
|
return remaining
|
|
}
|
|
|
|
open class PendingInsertion<Self: PendingInsertion<Self>>(
|
|
val stack: ItemStack,
|
|
val timestamp: Long
|
|
) {
|
|
val results = mutableSetOf<Pair<Int, NetworkStackReceiver>>()
|
|
|
|
val totalCapacity: Int
|
|
get() = results.fold(0) { acc, (amount, _) -> acc + amount }
|
|
|
|
fun isFinishable(owner: NetworkStackDispatcher<Self>): Boolean {
|
|
return totalCapacity >= stack.count || owner.counter - timestamp >= owner.dispatchStackTimeout
|
|
}
|
|
}
|
|
}
|
|
|
|
fun <Self, Insertion: NetworkStackDispatcher.PendingInsertion<Insertion>> Self.finishTimedOutPendingInsertions() where Self: BlockEntity, Self: NetworkStackDispatcher<Insertion> {
|
|
if (world!!.isClient) return
|
|
|
|
for (insertion in pendingInsertions) {
|
|
if (!insertion.isFinishable(this)) continue
|
|
finishInsertion(insertion)
|
|
}
|
|
}
|