diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt index 59d9efe96..40941bf09 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt @@ -413,7 +413,6 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc } private var _subCache: ChunkSubscribers? = null - private val subscription get() = _subCache ?: subscribe() private var playerListUpdated = false private fun unsubscribe() { @@ -430,7 +429,10 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc playerListUpdated = false } - private fun subscribe(): ChunkSubscribers { + private fun subscribe() { + if (!syncher.hasChildren) + return + val level = level check(level is ServerLevel) { "Invalid realm" } unsubscribe() @@ -442,7 +444,6 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc subs.subscribe(this) _subCache = subs playerListUpdated = true - return subs } private class ChunkSubscribers(level: ServerLevel, val chunkPos: Long) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/cable/EnergyCableGraph.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/cable/EnergyCableGraph.kt index c00d7d159..df421a4b4 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/cable/EnergyCableGraph.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/cable/EnergyCableGraph.kt @@ -3,7 +3,6 @@ package ru.dbotthepony.mc.otm.block.entity.cable import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.ReferenceArraySet import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet -import net.minecraft.core.BlockPos import org.apache.logging.log4j.LogManager import ru.dbotthepony.mc.otm.SERVER_IS_LIVE import ru.dbotthepony.mc.otm.UNIVERSE_TICKS @@ -11,7 +10,6 @@ import ru.dbotthepony.mc.otm.capability.receiveEnergy import ru.dbotthepony.mc.otm.config.CablesConfig import ru.dbotthepony.mc.otm.core.addAll import ru.dbotthepony.mc.otm.core.collect.map -import ru.dbotthepony.mc.otm.core.collect.max import ru.dbotthepony.mc.otm.core.collect.reduce import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.RelativeSide @@ -24,6 +22,56 @@ import kotlin.collections.HashSet import kotlin.collections.LinkedHashSet import kotlin.math.ln +private class LinkedPriorityQueue> { + private class Entry(val value: T) { + var next: Entry? = null + } + + private var head: Entry? = null + + fun isNotEmpty(): Boolean { + return head != null + } + + fun add(value: T) { + if (head == null) { + head = Entry(value) + } else if (head!!.value >= value) { + val entry = Entry(value) + entry.next = head + head = entry + } else { + var current = head + var previous = current + + while (current != null) { + if (current.value >= value) { + val entry = Entry(value) + entry.next = current + previous!!.next = entry + return + } + + previous = current + current = current.next + } + + previous!!.next = Entry(value) + } + } + + fun remove(): T? { + val head = head + + if (head != null) { + this.head = head.next + return head.value + } + + return null + } +} + class EnergyCableGraph : GraphNodeList() { private val livelyNodes = HashSet() private val livelyNodesList = ArrayList() @@ -43,6 +91,7 @@ class EnergyCableGraph : GraphNodeList(1) var saturated = false + var forceExploreAlternatives = false } // TODO: LRU cache? @@ -138,7 +187,7 @@ class EnergyCableGraph : GraphNodeList() + val nodes = LinkedHashSet() var paths = ReferenceLinkedOpenHashSet() operator fun contains(node: EnergyCableBlockEntity.Node): Boolean { @@ -184,6 +233,7 @@ class EnergyCableGraph : GraphNodeList 0) { lastPoweredStatus = numState nodes.forEach { if (it.segment === this) it.updatePoweredState(newState) } } @@ -209,7 +259,7 @@ class EnergyCableGraph : GraphNodeList) { - if (nodes.isEmpty()) return + if (nodes.isEmpty() || paths.isEmpty()) return for (segment in with) { if (tryCombineWith(segment)) return @@ -421,14 +478,16 @@ class EnergyCableGraph : GraphNodeList() + val openNodes = LinkedPriorityQueue() val seenNodes = HashSet() openNodes.add(SearchNode(a, b, null, seenTop)) + val existingNodes = HashSet() + existing.forEach { existingNodes.addAll(it.nodes) } + while (openNodes.isNotEmpty()) { - val first = openNodes.remove() - openNodes.remove(first) + val first = openNodes.remove()!! if (first.node === b) { // solution found @@ -466,7 +525,7 @@ class EnergyCableGraph : GraphNodeList { - if (!a.canTraverse || !b.canTraverse) + if (!a.canTraverse || !b.canTraverse || energyToTransfer <= Decimal.ZERO) return listOf() if (searchedOnTick != UNIVERSE_TICKS) { @@ -493,17 +552,19 @@ class EnergyCableGraph : GraphNodeList() + + node.segment.paths.toTypedArray().forEach { pathCache.remove(it.a to it.b) - pathCache.remove(it.b to it.a) + + it.segments.forEach { s -> + touchedSegments.add(s) + s.paths.remove(it) + + // this doesn't cover 100% of cases where previously split path segments + // due to "this" path get recombined, but it should be good enough + s.paths.forEach { touchedSegments.addAll(it.segments) } + } } - node.segment.paths = ReferenceLinkedOpenHashSet() - node.segment.split() + touchedSegments.addAll(node.segment.split()) + touchedSegments.forEach { it.tryCombine(touchedSegments) } } override fun onNodeAdded(node: EnergyCableBlockEntity.Node) { @@ -561,7 +635,7 @@ class EnergyCableGraph : GraphNodeList { override val hasRemotes: Boolean get() = remotes.isNotEmpty() + val hasChildren: Boolean + get() = slots.any { it != null } + override fun iterator(): Iterator { return slots.iterator().map { it?.synchable }.filterNotNull() }