Try to prevent recursive energy transfer on cables
This commit is contained in:
parent
3e5f47c9a3
commit
69e4747363
@ -1,5 +1,6 @@
|
|||||||
package ru.dbotthepony.mc.otm.block.entity.cable
|
package ru.dbotthepony.mc.otm.block.entity.cable
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
||||||
import net.minecraft.core.BlockPos
|
import net.minecraft.core.BlockPos
|
||||||
import net.minecraft.core.Direction
|
import net.minecraft.core.Direction
|
||||||
import net.minecraft.world.level.Level
|
import net.minecraft.world.level.Level
|
||||||
@ -114,6 +115,8 @@ abstract class EnergyCableBlockEntity(type: BlockEntityType<*>, blockPos: BlockP
|
|||||||
|
|
||||||
val sides get() = energySides
|
val sides get() = energySides
|
||||||
|
|
||||||
|
val currentlyTransferringTo = ObjectArraySet<Pair<Node, RelativeSide>>()
|
||||||
|
|
||||||
override fun onNeighbour(link: Link) {
|
override fun onNeighbour(link: Link) {
|
||||||
if (link is DirectionLink) {
|
if (link is DirectionLink) {
|
||||||
updateBlockState(link.direction, true)
|
updateBlockState(link.direction, true)
|
||||||
|
@ -19,6 +19,7 @@ import ru.dbotthepony.mc.otm.core.shuffle
|
|||||||
import ru.dbotthepony.mc.otm.graph.GraphNodeList
|
import ru.dbotthepony.mc.otm.graph.GraphNodeList
|
||||||
import ru.dbotthepony.mc.otm.onceServer
|
import ru.dbotthepony.mc.otm.onceServer
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.concurrent.atomic.AtomicLong
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.collections.HashSet
|
import kotlin.collections.HashSet
|
||||||
import kotlin.collections.LinkedHashSet
|
import kotlin.collections.LinkedHashSet
|
||||||
@ -635,96 +636,104 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
|
|||||||
val snapshot = Reference2ObjectOpenHashMap<Segment, Decimal>()
|
val snapshot = Reference2ObjectOpenHashMap<Segment, Decimal>()
|
||||||
|
|
||||||
for (pair in itr) {
|
for (pair in itr) {
|
||||||
val (node, relSide) = pair
|
// recursion prevention
|
||||||
val side = node.sides[relSide]!!
|
if (!fromNode.currentlyTransferringTo.add(pair))
|
||||||
|
|
||||||
if (!side.isEnabled) {
|
|
||||||
itr.remove()
|
|
||||||
check(livelyNodes.remove(pair)) { "Lively nodes Set does not contain $pair" }
|
|
||||||
continue
|
continue
|
||||||
} else if (fromNode === node && side.side === fromSide) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
val it = side.neighbour.get()
|
try {
|
||||||
|
val (node, relSide) = pair
|
||||||
|
val side = node.sides[relSide]!!
|
||||||
|
|
||||||
if (it == null || it is EnergyCableBlockEntity.CableSide) {
|
if (!side.isEnabled) {
|
||||||
itr.remove()
|
itr.remove()
|
||||||
check(livelyNodes.remove(pair)) { "Lively nodes Set does not contain $pair" }
|
check(livelyNodes.remove(pair)) { "Lively nodes Set does not contain $pair" }
|
||||||
continue
|
continue
|
||||||
}
|
} else if (fromNode === node && side.side === fromSide) {
|
||||||
|
|
||||||
val paths = getPath(fromNode, node, it.receiveEnergy(residue, true), !simulate)
|
|
||||||
|
|
||||||
if (paths.size == 1) {
|
|
||||||
// Single path, fast scenario
|
|
||||||
val path = paths[0]
|
|
||||||
val pathTransferred = path.transfer(residue, simulate, snapshot)
|
|
||||||
if (pathTransferred <= Decimal.ZERO) continue
|
|
||||||
val thisReceived = it.receiveEnergy(pathTransferred, simulate)
|
|
||||||
|
|
||||||
// If cable transferred more than machine accepted, then "refund" energy
|
|
||||||
// so cables record actual value transferred through them
|
|
||||||
if (thisReceived != pathTransferred) {
|
|
||||||
path.refund(pathTransferred - thisReceived, simulate, snapshot)
|
|
||||||
|
|
||||||
if (!simulate && thisReceived != Decimal.ZERO) {
|
|
||||||
path.triggerBlockstateUpdates()
|
|
||||||
}
|
|
||||||
} else if (!simulate) {
|
|
||||||
path.triggerBlockstateUpdates()
|
|
||||||
}
|
|
||||||
|
|
||||||
received += thisReceived
|
|
||||||
residue -= thisReceived
|
|
||||||
if (!residue.isPositive) return received
|
|
||||||
} else if (paths.size >= 2) {
|
|
||||||
// Multiple paths, a bit more complicated
|
|
||||||
// Determine how much machine is likely to accept
|
|
||||||
val potentiallyAccepted = it.receiveEnergy(residue, true)
|
|
||||||
|
|
||||||
// Won't accept anything
|
|
||||||
if (potentiallyAccepted <= Decimal.ZERO) continue
|
|
||||||
|
|
||||||
// Now determine combined available throughput
|
|
||||||
// Make a copy of snapshot, so we can freely write into it
|
|
||||||
val copy = snapshot.clone()
|
|
||||||
var calcResidue = potentiallyAccepted
|
|
||||||
|
|
||||||
// TODO: Currently, all transfers cause Braess's paradox, because of greedy selection of "fastest" cable
|
|
||||||
// Need to implement heuristics to better distribute load across different paths/segments
|
|
||||||
for (path in paths) {
|
|
||||||
val passed = path.transfer(calcResidue, true, copy)
|
|
||||||
calcResidue -= passed
|
|
||||||
if (calcResidue <= Decimal.ZERO) break
|
|
||||||
}
|
|
||||||
|
|
||||||
if (calcResidue == potentiallyAccepted) {
|
|
||||||
// мда
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var thisReceived = it.receiveEnergy(potentiallyAccepted - calcResidue, simulate)
|
val it = side.neighbour.get()
|
||||||
received += thisReceived
|
|
||||||
residue -= thisReceived
|
|
||||||
|
|
||||||
for (path in paths) {
|
if (it == null || it is EnergyCableBlockEntity.CableSide) {
|
||||||
val passed = path.transfer(thisReceived, simulate, snapshot)
|
itr.remove()
|
||||||
|
check(livelyNodes.remove(pair)) { "Lively nodes Set does not contain $pair" }
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if (!simulate)
|
val paths = getPath(fromNode, node, it.receiveEnergy(residue, true), !simulate)
|
||||||
|
|
||||||
|
if (paths.size == 1) {
|
||||||
|
// Single path, fast scenario
|
||||||
|
val path = paths[0]
|
||||||
|
val pathTransferred = path.transfer(residue, simulate, snapshot)
|
||||||
|
if (pathTransferred <= Decimal.ZERO) continue
|
||||||
|
val thisReceived = it.receiveEnergy(pathTransferred, simulate)
|
||||||
|
|
||||||
|
// If cable transferred more than machine accepted, then "refund" energy
|
||||||
|
// so cables record actual value transferred through them
|
||||||
|
if (thisReceived != pathTransferred) {
|
||||||
|
path.refund(pathTransferred - thisReceived, simulate, snapshot)
|
||||||
|
|
||||||
|
if (!simulate && thisReceived != Decimal.ZERO) {
|
||||||
|
path.triggerBlockstateUpdates()
|
||||||
|
}
|
||||||
|
} else if (!simulate) {
|
||||||
path.triggerBlockstateUpdates()
|
path.triggerBlockstateUpdates()
|
||||||
|
}
|
||||||
|
|
||||||
thisReceived -= passed
|
received += thisReceived
|
||||||
if (thisReceived <= Decimal.ZERO) break
|
residue -= thisReceived
|
||||||
}
|
if (!residue.isPositive) return received
|
||||||
|
} else if (paths.size >= 2) {
|
||||||
if (!residue.isPositive) return received
|
// Multiple paths, a bit more complicated
|
||||||
//check(thisReceived <= Decimal.ZERO) { "Путом, алло, Путом, какого чёрта Путом? Путом почему ты заблокировал логику, а Путом?" }
|
// Determine how much machine is likely to accept
|
||||||
|
val potentiallyAccepted = it.receiveEnergy(residue, true)
|
||||||
if (thisReceived > Decimal.ZERO) {
|
|
||||||
LOGGER.warn("Cable path from $fromNode to $node doesn't follow common sense, disabling.")
|
// Won't accept anything
|
||||||
paths.forEach { it.shortCircuit = true }
|
if (potentiallyAccepted <= Decimal.ZERO) continue
|
||||||
|
|
||||||
|
// Now determine combined available throughput
|
||||||
|
// Make a copy of snapshot, so we can freely write into it
|
||||||
|
val copy = snapshot.clone()
|
||||||
|
var calcResidue = potentiallyAccepted
|
||||||
|
|
||||||
|
// TODO: Currently, all transfers cause Braess's paradox, because of greedy selection of "fastest" cable
|
||||||
|
// Need to implement heuristics to better distribute load across different paths/segments
|
||||||
|
for (path in paths) {
|
||||||
|
val passed = path.transfer(calcResidue, true, copy)
|
||||||
|
calcResidue -= passed
|
||||||
|
if (calcResidue <= Decimal.ZERO) break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (calcResidue == potentiallyAccepted) {
|
||||||
|
// мда
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var thisReceived = it.receiveEnergy(potentiallyAccepted - calcResidue, simulate)
|
||||||
|
received += thisReceived
|
||||||
|
residue -= thisReceived
|
||||||
|
|
||||||
|
for (path in paths) {
|
||||||
|
val passed = path.transfer(thisReceived, simulate, snapshot)
|
||||||
|
|
||||||
|
if (!simulate)
|
||||||
|
path.triggerBlockstateUpdates()
|
||||||
|
|
||||||
|
thisReceived -= passed
|
||||||
|
if (thisReceived <= Decimal.ZERO) break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!residue.isPositive) return received
|
||||||
|
//check(thisReceived <= Decimal.ZERO) { "Путом, алло, Путом, какого чёрта Путом? Путом почему ты заблокировал логику, а Путом?" }
|
||||||
|
|
||||||
|
if (thisReceived > Decimal.ZERO) {
|
||||||
|
LOGGER.warn("Cable path from $fromNode to $node doesn't follow common sense, disabling.")
|
||||||
|
paths.forEach { it.shortCircuit = true }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
fromNode.currentlyTransferringTo.remove(pair)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user