Add network stack provider priorities

This commit is contained in:
Shadowfacts 2021-03-03 22:00:21 -05:00
parent 2d3ac4538d
commit 170e50755a
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
9 changed files with 139 additions and 18 deletions

View File

@ -10,7 +10,7 @@ class NumberField(
var number: Int?
get() {
return if (text.isEmpty()) {
return if (isTextTemporarilyAllowed(text)) {
null
} else {
try {
@ -31,7 +31,7 @@ class NumberField(
}
override fun validate(proposedText: String): Boolean {
return proposedText.isEmpty() || try {
return isTextTemporarilyAllowed(proposedText) || try {
val value = Integer.parseInt(proposedText)
validator?.invoke(value) ?: true
} catch (e: NumberFormatException) {
@ -39,4 +39,8 @@ class NumberField(
}
}
private fun isTextTemporarilyAllowed(s: String): Boolean {
return s.isEmpty() || s == "-"
}
}

View File

@ -5,7 +5,6 @@ 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

View File

@ -44,6 +44,7 @@ class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
override val pendingInsertions = mutableListOf<PendingInsertion>()
override val dispatchStackTimeout = TerminalBlockEntity.INSERTION_TIMEOUT
override val controller = ActivationController(40L, this)
override var providerPriority = 0
var minerMode = MinerMode.ON_DEMAND
@ -151,6 +152,10 @@ class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
return minerMode == MinerMode.AUTOMATIC
}
override fun canConfigureProviderPriority(): Boolean {
return minerMode == MinerMode.ON_DEMAND
}
override fun toCommonTag(tag: CompoundTag) {
super.toCommonTag(tag)
writeDeviceConfiguration(tag)
@ -164,11 +169,13 @@ class MinerBlockEntity: DeviceBlockEntity(PhyBlockEntities.MINER),
override fun writeDeviceConfiguration(tag: CompoundTag) {
tag.putString("MinerMode", minerMode.name)
tag.putString("ActivationMode", controller.activationMode.name)
tag.putInt("ProviderPriority", providerPriority)
}
override fun loadDeviceConfiguration(tag: CompoundTag) {
minerMode = MinerMode.valueOf(tag.getString("MinerMode"))
controller.activationMode = ActivationMode.valueOf(tag.getString("ActivationMode"))
providerPriority = tag.getInt("ProviderPriority")
}
enum class MinerMode {

View File

@ -4,7 +4,9 @@ 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.ItemAttributes
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.init.PhyBlockEntities
@ -15,6 +17,7 @@ import net.shadowfacts.phycon.component.NetworkStackProvider
import net.shadowfacts.phycon.component.NetworkStackReceiver
import net.shadowfacts.phycon.component.handleItemStack
import net.shadowfacts.phycon.packet.*
import net.shadowfacts.phycon.util.ClientConfigurableDevice
import kotlin.math.min
/**
@ -23,11 +26,14 @@ import kotlin.math.min
class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE),
ItemStackPacketHandler,
NetworkStackProvider,
NetworkStackReceiver {
NetworkStackReceiver,
ClientConfigurableDevice {
private val facing: Direction
get() = cachedState[FaceDeviceBlock.FACING]
override var providerPriority = 0
// todo: should this be a weak ref?
private var inventory: GroupedItemInv? = null
@ -101,4 +107,22 @@ class InterfaceBlockEntity: DeviceBlockEntity(PhyBlockEntities.INTERFACE),
}
}
override fun toCommonTag(tag: CompoundTag) {
super.toCommonTag(tag)
writeDeviceConfiguration(tag)
}
override fun fromCommonTag(tag: CompoundTag) {
super.fromCommonTag(tag)
loadDeviceConfiguration(tag)
}
override fun writeDeviceConfiguration(tag: CompoundTag) {
tag.putInt("ProviderPriority", providerPriority)
}
override fun loadDeviceConfiguration(tag: CompoundTag) {
providerPriority = tag.getInt("ProviderPriority")
}
}

View File

@ -5,7 +5,6 @@ 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.screenhandler.v1.ExtendedScreenHandlerFactory
import net.minecraft.block.BlockState
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.inventory.Inventory
@ -42,7 +41,9 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
NetworkStackDispatcher<TerminalBlockEntity.PendingInsertion> {
companion object {
val LOCATE_REQUEST_TIMEOUT: Long = 40 // ticks
// the locate request timeout is only 1 tick because that's long enough to hear from every device on the network
// in a degraded state (when there's latency in the network), not handling interface priorities correctly is acceptable
val LOCATE_REQUEST_TIMEOUT: Long = 1 // ticks
val INSERTION_TIMEOUT: Long = 40
}
@ -163,14 +164,15 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
override fun tick() {
super.tick()
if (counter % 20 == 0L) {
if (!world!!.isClient) {
finishPendingRequests()
beginInsertions()
finishTimedOutPendingInsertions()
}
if (!world!!.isClient) {
finishPendingRequests()
finishTimedOutPendingInsertions()
}
if (observers > 0 && !world!!.isClient) {
if (counter % 20 == 0L && !world!!.isClient) {
beginInsertions()
if (observers > 0) {
updateAndSync()
}
}
@ -209,8 +211,15 @@ class TerminalBlockEntity: DeviceBlockEntity(PhyBlockEntities.TERMINAL),
private fun stackLocateRequestCompleted(request: StackLocateRequest) {
pendingRequests.remove(request)
// todo: also sort results by interface priority
val sortedResults = request.results.sortedByDescending { it.first }.toMutableList()
val sortedResults = request.results.toMutableList()
sortedResults.sortWith { a, b ->
// sort results first by provider priority, and then by the count that it can provide
if (a.second.providerPriority == b.second.providerPriority) {
b.first - a.first
} else {
b.second.providerPriority - a.second.providerPriority
}
}
var amountRequested = 0
while (amountRequested < request.amount && sortedResults.isNotEmpty()) {
val (sourceAmount, sourceInterface) = sortedResults.removeAt(0)
@ -268,6 +277,8 @@ data class StackLocateRequest(
get() = results.fold(0) { acc, (amount, _) -> acc + amount }
fun isFinishable(currentTimestamp: Long): Boolean {
return totalResultAmount >= amount || currentTimestamp - timestamp >= TerminalBlockEntity.LOCATE_REQUEST_TIMEOUT
// we can't check totalResultAmount >= amount because we need to hear back from all network stack providers to
// correctly sort by priority
return currentTimestamp - timestamp >= TerminalBlockEntity.LOCATE_REQUEST_TIMEOUT
}
}

View File

@ -1,9 +1,17 @@
package net.shadowfacts.phycon.component
import net.shadowfacts.phycon.api.NetworkDevice
import net.shadowfacts.phycon.util.ClientConfigurableDevice
/**
* @author shadowfacts
*/
interface NetworkStackProvider: NetworkDevice {
interface NetworkStackProvider: NetworkDevice, ClientConfigurableDevice {
var providerPriority: Int
fun canConfigureProviderPriority(): Boolean {
return true
}
}

View File

@ -16,6 +16,7 @@ import net.shadowfacts.phycon.block.DeviceBlockEntity
import net.shadowfacts.phycon.block.miner.MinerBlockEntity
import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlockEntity
import net.shadowfacts.phycon.component.ActivationController
import net.shadowfacts.phycon.component.NetworkStackProvider
import org.lwjgl.glfw.GLFW
/**
@ -63,7 +64,14 @@ class DeviceConsoleScreen(
MinerViewController(device)
))
}
if (device is NetworkStackProvider) {
tabs.add(TabViewController.SimpleTab(
Label("P").apply { textColor = Color.TEXT },
TranslatableText("gui.phycon.console.provider"),
ProviderViewController(device),
device::canConfigureProviderPriority
))
}
tabController = TabViewController(tabs)

View File

@ -0,0 +1,57 @@
package net.shadowfacts.phycon.screen.console
import net.minecraft.block.entity.BlockEntity
import net.minecraft.client.MinecraftClient
import net.minecraft.text.TranslatableText
import net.shadowfacts.cacao.util.Color
import net.shadowfacts.cacao.view.Label
import net.shadowfacts.cacao.view.textfield.NumberField
import net.shadowfacts.cacao.viewcontroller.ViewController
import net.shadowfacts.kiwidsl.dsl
import net.shadowfacts.phycon.component.NetworkStackProvider
import net.shadowfacts.phycon.networking.C2SConfigureDevice
/**
* @author shadowfacts
*/
class ProviderViewController<T>(
private val device: T
): ViewController() where T: BlockEntity, T: NetworkStackProvider {
override fun viewDidLoad() {
super.viewDidLoad()
val label = Label(TranslatableText("gui.phycon.console.provider.priority")).apply {
textColor = Color.TEXT
}
view.addSubview(label)
val field = NumberField(device.providerPriority) {
if (it.number != null) {
device.providerPriority = it.number!!
MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureDevice(device))
}
}
view.addSubview(field)
val desc = Label(TranslatableText("gui.phycon.console.provider.priority_desc")).apply {
textColor = Color.TEXT
}
view.addSubview(desc)
view.solver.dsl {
field.widthAnchor equalTo 100
field.heightAnchor equalTo 20
field.rightAnchor equalTo view.rightAnchor
field.topAnchor equalTo view.topAnchor
label.centerYAnchor equalTo field.centerYAnchor
label.rightAnchor equalTo (field.leftAnchor - 4)
desc.topAnchor equalTo (field.bottomAnchor + 4)
desc.leftAnchor equalTo view.leftAnchor
desc.rightAnchor equalTo view.rightAnchor
}
}
}

View File

@ -21,6 +21,9 @@
"gui.phycon.console.remote": "Remote Management",
"gui.phycon.console.remote.mode": "Activation Mode",
"gui.phycon.console.miner.mode": "Miner Mode",
"gui.phycon.console.provider": "Item Provider",
"gui.phycon.console.provider.priority": "Provider Priority",
"gui.phycon.console.provider.priority_desc": "When a device requests items from the network, it send requests to providers (e.g., interfaces) with higher priorities first. Priorities can be negative.",
"gui.phycon.redstone_mode.high": "High",
"gui.phycon.redstone_mode.low": "Low",
"gui.phycon.redstone_mode.toggle": "Toggle",