Cable segment merging

This commit is contained in:
DBotThePony 2025-01-15 21:27:13 +07:00
parent 90dc125b1b
commit b4e37fbd1f
Signed by: DBot
GPG Key ID: DCC23B5715498507
2 changed files with 65 additions and 14 deletions

View File

@ -99,7 +99,8 @@ abstract class EnergyCableBlockEntity(type: BlockEntityType<*>, blockPos: BlockP
}
inner class Node : GraphNode<Node, EnergyCableGraph>(::EnergyCableGraph) {
private var _segment = EnergyCableGraph.Segment(this)
@Deprecated("internal property")
var _segment = EnergyCableGraph.Segment(this)
var segment: EnergyCableGraph.Segment
get() { return _segment }

View File

@ -3,6 +3,7 @@ package ru.dbotthepony.mc.otm.block.entity.cable
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
import it.unimi.dsi.fastutil.objects.ReferenceArraySet
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet
import net.minecraft.core.BlockPos
import org.apache.logging.log4j.LogManager
@ -118,7 +119,6 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
}
}
// TODO: Actual segment logic (merging segments)
class Segment() {
constructor(node: EnergyCableBlockEntity.Node) : this() {
nodes.add(node)
@ -136,7 +136,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
private fun checkThroughput() {
if (!throughputKnown) {
throughput = nodes.maxOf { it.energyThroughput }
throughput = nodes.maxOfOrNull { it.energyThroughput } ?: Decimal.ZERO
throughputKnown = true
}
}
@ -154,6 +154,8 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
private var lastPoweredStatus = 0
private fun updateBlockstates(): Boolean {
if (nodes.isEmpty()) return false
if (lastTick + 2 < UNIVERSE_TICKS) {
transferredLastTick = Decimal.ZERO
lastTick = UNIVERSE_TICKS
@ -199,6 +201,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
lastTick = UNIVERSE_TICKS
}
checkThroughput()
val currentTransferred = instantSnapshot[this] ?: transferredLastTick
if (currentTransferred >= throughput || !amount.isPositive) {
@ -217,6 +220,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
lastTick = UNIVERSE_TICKS
}
checkThroughput()
if (transferredLastTick >= throughput || !amount.isPositive) {
return Decimal.ZERO
}
@ -238,33 +242,30 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
instantSnapshot[this] = maxOf(instantSnapshot[this]!! - amount, Decimal.ZERO)
}
} else {
transferredLastTick = maxOf(transferredLastTick, Decimal.ZERO)
transferredLastTick = maxOf(transferredLastTick - amount, Decimal.ZERO)
}
}
fun remove(node: EnergyCableBlockEntity.Node) {
check(nodes.remove(node)) { "Tried to remove node $node from segment $this, but that node does not belong to this segment" }
if (nodes.isEmpty())
throughput = Decimal.ZERO
else
throughput = nodes.maxOf { it.energyThroughput }
throughputKnown = false
}
fun add(node: EnergyCableBlockEntity.Node) {
check(nodes.add(node)) { "Tried to add node $node to segment $this, but we already have that node added" }
throughput = maxOf(throughput, node.energyThroughput)
if (throughputKnown)
throughput = maxOf(throughput, node.energyThroughput)
}
fun add(path: SegmentPath) {
check(paths.add(path)) { "Path $path should already contain $this" }
check(paths.add(path)) { "Path $path shouldn't contain $this" }
check(path.segments.add(this)) { "Path set and Segment disagree whenever $this is absent from $path" }
checkThroughput()
}
fun remove(path: SegmentPath) {
check(paths.remove(path)) { "Path $path shouldn't contain $this" }
check(paths.remove(path)) { "Path $path should already contain $this" }
check(path.segments.remove(this)) { "Path set and Segment disagree whenever $this is present in $path" }
}
@ -285,6 +286,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
for (v in itr) {
v.segment = Segment()
v.segment.transferredLastTick = transferredLastTick
v.segment.paths.addAll(paths)
paths.forEach { it.segments.add(v.segment) }
result.add(v.segment)
@ -293,6 +295,52 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
return result
}
}
private fun combineInto(segment: Segment) {
nodes.forEach { it._segment = segment }
segment.nodes.addAll(nodes)
// this should match on both segments
// segment.transferredLastTick = maxOf(segment.transferredLastTick, transferredLastTick)
paths.forEach { it.segments.remove(this) }
segment.throughputKnown = false
nodes.clear() // mark segment as dead
}
private fun tryCombineWith(segment: Segment): Boolean {
if (segment === this || segment.nodes.isEmpty()) return false
// if nodes have same set of paths then it is safe to combine this with target segment
if (segment.paths == paths) {
if (nodes.size >= segment.nodes.size) {
segment.combineInto(this)
} else {
combineInto(segment)
}
return true
}
return false
}
fun tryCombine() {
if (nodes.isEmpty()) return
// TODO: This is inefficient as fk
for (path in paths) {
for (segment in path.segments) {
if (tryCombineWith(segment)) return
}
}
}
fun tryCombine(with: Collection<Segment>) {
if (nodes.isEmpty()) return
for (segment in with) {
if (tryCombineWith(segment)) return
}
}
}
fun invalidatePathCache() {
@ -329,11 +377,13 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
last = last.parent
}
val touchedSegments = ArrayList<Segment>()
val touchedSegments = ReferenceArraySet<Segment>()
solution.forEach { touchedSegments.addAll(it.segment.split()) }
val path = SegmentPath(a.blockEntity.blockPos, b.blockEntity.blockPos)
touchedSegments.forEach { it.add(path) }
touchedSegments.forEach { it.tryCombine(touchedSegments) }
return path
} else {
for (neighbour in first.node.neighboursView.values) {