Merge remote-tracking branch 'origin/1.21' into 1.21
This commit is contained in:
commit
bbf0283219
@ -413,7 +413,6 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var _subCache: ChunkSubscribers? = null
|
private var _subCache: ChunkSubscribers? = null
|
||||||
private val subscription get() = _subCache ?: subscribe()
|
|
||||||
private var playerListUpdated = false
|
private var playerListUpdated = false
|
||||||
|
|
||||||
private fun unsubscribe() {
|
private fun unsubscribe() {
|
||||||
@ -430,7 +429,10 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
|
|||||||
playerListUpdated = false
|
playerListUpdated = false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun subscribe(): ChunkSubscribers {
|
private fun subscribe() {
|
||||||
|
if (!syncher.hasChildren)
|
||||||
|
return
|
||||||
|
|
||||||
val level = level
|
val level = level
|
||||||
check(level is ServerLevel) { "Invalid realm" }
|
check(level is ServerLevel) { "Invalid realm" }
|
||||||
unsubscribe()
|
unsubscribe()
|
||||||
@ -442,7 +444,6 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
|
|||||||
subs.subscribe(this)
|
subs.subscribe(this)
|
||||||
_subCache = subs
|
_subCache = subs
|
||||||
playerListUpdated = true
|
playerListUpdated = true
|
||||||
return subs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ChunkSubscribers(level: ServerLevel, val chunkPos: Long) {
|
private class ChunkSubscribers(level: ServerLevel, val chunkPos: Long) {
|
||||||
|
@ -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.Reference2ObjectOpenHashMap
|
||||||
import it.unimi.dsi.fastutil.objects.ReferenceArraySet
|
import it.unimi.dsi.fastutil.objects.ReferenceArraySet
|
||||||
import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet
|
import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet
|
||||||
import net.minecraft.core.BlockPos
|
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import ru.dbotthepony.mc.otm.SERVER_IS_LIVE
|
import ru.dbotthepony.mc.otm.SERVER_IS_LIVE
|
||||||
import ru.dbotthepony.mc.otm.UNIVERSE_TICKS
|
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.config.CablesConfig
|
||||||
import ru.dbotthepony.mc.otm.core.addAll
|
import ru.dbotthepony.mc.otm.core.addAll
|
||||||
import ru.dbotthepony.mc.otm.core.collect.map
|
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.collect.reduce
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
import ru.dbotthepony.mc.otm.core.math.RelativeSide
|
import ru.dbotthepony.mc.otm.core.math.RelativeSide
|
||||||
@ -24,6 +22,56 @@ import kotlin.collections.HashSet
|
|||||||
import kotlin.collections.LinkedHashSet
|
import kotlin.collections.LinkedHashSet
|
||||||
import kotlin.math.ln
|
import kotlin.math.ln
|
||||||
|
|
||||||
|
private class LinkedPriorityQueue<T : Comparable<T>> {
|
||||||
|
private class Entry<T>(val value: T) {
|
||||||
|
var next: Entry<T>? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
private var head: Entry<T>? = 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<EnergyCableBlockEntity.Node, EnergyCableGraph>() {
|
class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableGraph>() {
|
||||||
private val livelyNodes = HashSet<EnergyCableBlockEntity.Node>()
|
private val livelyNodes = HashSet<EnergyCableBlockEntity.Node>()
|
||||||
private val livelyNodesList = ArrayList<EnergyCableBlockEntity.Node>()
|
private val livelyNodesList = ArrayList<EnergyCableBlockEntity.Node>()
|
||||||
@ -43,6 +91,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
|
|||||||
private class CacheEntry {
|
private class CacheEntry {
|
||||||
val paths = ArrayList<SegmentPath>(1)
|
val paths = ArrayList<SegmentPath>(1)
|
||||||
var saturated = false
|
var saturated = false
|
||||||
|
var forceExploreAlternatives = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: LRU cache?
|
// TODO: LRU cache?
|
||||||
@ -138,7 +187,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
|
|||||||
nodes.add(node)
|
nodes.add(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val nodes = LinkedHashSet<EnergyCableBlockEntity.Node>()
|
val nodes = LinkedHashSet<EnergyCableBlockEntity.Node>()
|
||||||
var paths = ReferenceLinkedOpenHashSet<SegmentPath>()
|
var paths = ReferenceLinkedOpenHashSet<SegmentPath>()
|
||||||
|
|
||||||
operator fun contains(node: EnergyCableBlockEntity.Node): Boolean {
|
operator fun contains(node: EnergyCableBlockEntity.Node): Boolean {
|
||||||
@ -184,6 +233,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
|
|||||||
private var lastTick = 0
|
private var lastTick = 0
|
||||||
private var lastPoweredStatus = 0
|
private var lastPoweredStatus = 0
|
||||||
private var lastPoweredSwitchFrequency = 0
|
private var lastPoweredSwitchFrequency = 0
|
||||||
|
private var poweredStatusFlightTicks = 4
|
||||||
|
|
||||||
private fun updateBlockstates(): Boolean {
|
private fun updateBlockstates(): Boolean {
|
||||||
if (nodes.isEmpty()) return false
|
if (nodes.isEmpty()) return false
|
||||||
@ -196,7 +246,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
|
|||||||
val newState = transferredLastTick.isPositive
|
val newState = transferredLastTick.isPositive
|
||||||
val numState = if (newState) 1 else -1
|
val numState = if (newState) 1 else -1
|
||||||
|
|
||||||
if (numState != lastPoweredStatus) {
|
if (numState != lastPoweredStatus || --poweredStatusFlightTicks > 0) {
|
||||||
lastPoweredStatus = numState
|
lastPoweredStatus = numState
|
||||||
nodes.forEach { if (it.segment === this) it.updatePoweredState(newState) }
|
nodes.forEach { if (it.segment === this) it.updatePoweredState(newState) }
|
||||||
}
|
}
|
||||||
@ -209,7 +259,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
|
|||||||
private fun blockstateUpdateStep() {
|
private fun blockstateUpdateStep() {
|
||||||
val new = updateBlockstates()
|
val new = updateBlockstates()
|
||||||
val changed = hasBlockstateTimer != new
|
val changed = hasBlockstateTimer != new
|
||||||
hasBlockstateTimer = updateBlockstates()
|
hasBlockstateTimer = new
|
||||||
lastPoweredSwitchFrequency = (lastPoweredSwitchFrequency shl 1) or (if (changed) 1 else 0)
|
lastPoweredSwitchFrequency = (lastPoweredSwitchFrequency shl 1) or (if (changed) 1 else 0)
|
||||||
|
|
||||||
if (hasBlockstateTimer && SERVER_IS_LIVE) {
|
if (hasBlockstateTimer && SERVER_IS_LIVE) {
|
||||||
@ -335,13 +385,19 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
|
|||||||
v.segment = Segment()
|
v.segment = Segment()
|
||||||
v.segment.throughput = v.energyThroughput
|
v.segment.throughput = v.energyThroughput
|
||||||
v.segment.throughputKnown = true
|
v.segment.throughputKnown = true
|
||||||
|
v.segment.lastTick = lastTick
|
||||||
v.segment.transferredLastTick = transferredLastTick
|
v.segment.transferredLastTick = transferredLastTick
|
||||||
v.segment.lastPoweredStatus = lastPoweredStatus
|
|
||||||
v.segment.paths = paths.clone()
|
v.segment.paths = paths.clone()
|
||||||
|
v.updatePoweredState(false)
|
||||||
|
|
||||||
paths.forEach { it.segments.add(v.segment) }
|
paths.forEach { it.segments.add(v.segment) }
|
||||||
|
|
||||||
result.add(v.segment)
|
result.add(v.segment)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastPoweredStatus = 0
|
||||||
|
poweredStatusFlightTicks = 4
|
||||||
|
paths.forEach { it.nodes.addAll(nodes) }
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -354,6 +410,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
|
|||||||
val segment = Segment()
|
val segment = Segment()
|
||||||
v._segment = segment
|
v._segment = segment
|
||||||
segment.nodes.add(v)
|
segment.nodes.add(v)
|
||||||
|
segment.lastTick = lastTick
|
||||||
segment.throughput = v.energyThroughput
|
segment.throughput = v.energyThroughput
|
||||||
segment.throughputKnown = true
|
segment.throughputKnown = true
|
||||||
segment.transferredLastTick = transferredLastTick
|
segment.transferredLastTick = transferredLastTick
|
||||||
@ -377,7 +434,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun tryCombineWith(segment: Segment): Boolean {
|
private fun tryCombineWith(segment: Segment): Boolean {
|
||||||
if (segment === this || segment.nodes.isEmpty()) return false
|
if (segment === this || segment.nodes.isEmpty() || segment.paths.isEmpty()) return false
|
||||||
|
|
||||||
// if nodes have same set of paths then it is safe to combine this with target segment
|
// if nodes have same set of paths then it is safe to combine this with target segment
|
||||||
if (segment.paths == paths) {
|
if (segment.paths == paths) {
|
||||||
@ -405,7 +462,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun tryCombine(with: Collection<Segment>) {
|
fun tryCombine(with: Collection<Segment>) {
|
||||||
if (nodes.isEmpty()) return
|
if (nodes.isEmpty() || paths.isEmpty()) return
|
||||||
|
|
||||||
for (segment in with) {
|
for (segment in with) {
|
||||||
if (tryCombineWith(segment)) return
|
if (tryCombineWith(segment)) return
|
||||||
@ -421,14 +478,16 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
val openNodes = PriorityQueue<SearchNode>()
|
val openNodes = LinkedPriorityQueue<SearchNode>()
|
||||||
val seenNodes = HashSet<EnergyCableBlockEntity.Node>()
|
val seenNodes = HashSet<EnergyCableBlockEntity.Node>()
|
||||||
|
|
||||||
openNodes.add(SearchNode(a, b, null, seenTop))
|
openNodes.add(SearchNode(a, b, null, seenTop))
|
||||||
|
|
||||||
|
val existingNodes = HashSet<EnergyCableBlockEntity.Node>()
|
||||||
|
existing.forEach { existingNodes.addAll(it.nodes) }
|
||||||
|
|
||||||
while (openNodes.isNotEmpty()) {
|
while (openNodes.isNotEmpty()) {
|
||||||
val first = openNodes.remove()
|
val first = openNodes.remove()!!
|
||||||
openNodes.remove(first)
|
|
||||||
|
|
||||||
if (first.node === b) {
|
if (first.node === b) {
|
||||||
// solution found
|
// solution found
|
||||||
@ -466,7 +525,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
|
|||||||
} else {
|
} else {
|
||||||
for (neighbour in first.node.neighboursView.values) {
|
for (neighbour in first.node.neighboursView.values) {
|
||||||
if (!seenNodes.add(neighbour) || !neighbour.canTraverse) continue
|
if (!seenNodes.add(neighbour) || !neighbour.canTraverse) continue
|
||||||
val seen = existing.any { neighbour in it }
|
val seen = neighbour in existingNodes
|
||||||
if (seen && neighbour.energyThroughput <= threshold) continue
|
if (seen && neighbour.energyThroughput <= threshold) continue
|
||||||
openNodes.add(SearchNode(neighbour, b, first, seen))
|
openNodes.add(SearchNode(neighbour, b, first, seen))
|
||||||
}
|
}
|
||||||
@ -480,7 +539,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
|
|||||||
private var searchedOnTick = 0
|
private var searchedOnTick = 0
|
||||||
|
|
||||||
private fun getPath(a: EnergyCableBlockEntity.Node, b: EnergyCableBlockEntity.Node, energyToTransfer: Decimal, sort: Boolean): List<SegmentPath> {
|
private fun getPath(a: EnergyCableBlockEntity.Node, b: EnergyCableBlockEntity.Node, energyToTransfer: Decimal, sort: Boolean): List<SegmentPath> {
|
||||||
if (!a.canTraverse || !b.canTraverse)
|
if (!a.canTraverse || !b.canTraverse || energyToTransfer <= Decimal.ZERO)
|
||||||
return listOf()
|
return listOf()
|
||||||
|
|
||||||
if (searchedOnTick != UNIVERSE_TICKS) {
|
if (searchedOnTick != UNIVERSE_TICKS) {
|
||||||
@ -493,17 +552,19 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
|
|||||||
val maxSearches = CablesConfig.SEARCHES_PER_TICK
|
val maxSearches = CablesConfig.SEARCHES_PER_TICK
|
||||||
|
|
||||||
if (!list.saturated && lastTickSearches <= maxSearches) {
|
if (!list.saturated && lastTickSearches <= maxSearches) {
|
||||||
var maxThroughput = list.paths.iterator().map { it.availableThroughput }.reduce(Decimal.ZERO, Decimal::plus)
|
var maxThroughput = list.paths.maxOfOrNull { it.availableThroughput } ?: Decimal.ZERO
|
||||||
|
var combinedThroughput = list.paths.iterator().map { it.availableThroughput }.reduce(Decimal.ZERO, Decimal::plus)
|
||||||
|
|
||||||
while (maxThroughput < energyToTransfer && !list.saturated && ++lastTickSearches <= maxSearches) {
|
while ((list.forceExploreAlternatives || combinedThroughput < energyToTransfer) && !list.saturated && ++lastTickSearches <= maxSearches) {
|
||||||
// TODO: Heuristics here is wrong
|
list.forceExploreAlternatives = false
|
||||||
val find = findPath(a, b, list.paths, maxThroughput)
|
val find = findPath(a, b, list.paths, maxThroughput)
|
||||||
|
|
||||||
if (find == null || find in list.paths) {
|
if (find == null || find in list.paths) {
|
||||||
list.saturated = true
|
list.saturated = true
|
||||||
} else {
|
} else {
|
||||||
list.paths.add(find)
|
list.paths.add(find)
|
||||||
maxThroughput += find.availableThroughput
|
combinedThroughput += find.availableThroughput
|
||||||
|
maxThroughput = maxOf(maxThroughput, find.availableThroughput)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -515,7 +576,10 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun notifyThroughputsChanged() {
|
fun notifyThroughputsChanged() {
|
||||||
pathCache.values.forEach { it.saturated = false }
|
pathCache.values.forEach {
|
||||||
|
it.saturated = false
|
||||||
|
it.forceExploreAlternatives = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNodeRemoved(node: EnergyCableBlockEntity.Node) {
|
override fun onNodeRemoved(node: EnergyCableBlockEntity.Node) {
|
||||||
@ -523,13 +587,23 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
|
|||||||
check(livelyNodesList.remove(node))
|
check(livelyNodesList.remove(node))
|
||||||
}
|
}
|
||||||
|
|
||||||
node.segment.paths.forEach {
|
val touchedSegments = HashSet<Segment>()
|
||||||
|
|
||||||
|
node.segment.paths.toTypedArray().forEach {
|
||||||
pathCache.remove(it.a to it.b)
|
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()
|
touchedSegments.addAll(node.segment.split())
|
||||||
node.segment.split()
|
touchedSegments.forEach { it.tryCombine(touchedSegments) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNodeAdded(node: EnergyCableBlockEntity.Node) {
|
override fun onNodeAdded(node: EnergyCableBlockEntity.Node) {
|
||||||
@ -561,7 +635,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
|
|||||||
val it = side.neighbour.get() ?: continue
|
val it = side.neighbour.get() ?: continue
|
||||||
if (it is EnergyCableBlockEntity.CableSide) continue
|
if (it is EnergyCableBlockEntity.CableSide) continue
|
||||||
|
|
||||||
val paths = getPath(fromNode, node, residue, !simulate)
|
val paths = getPath(fromNode, node, it.receiveEnergy(residue, true), !simulate)
|
||||||
hit = true
|
hit = true
|
||||||
|
|
||||||
if (paths.size == 1) {
|
if (paths.size == 1) {
|
||||||
|
@ -66,6 +66,9 @@ class SynchableGroup : Observer, ISynchable, Iterable<ISynchable> {
|
|||||||
override val hasRemotes: Boolean
|
override val hasRemotes: Boolean
|
||||||
get() = remotes.isNotEmpty()
|
get() = remotes.isNotEmpty()
|
||||||
|
|
||||||
|
val hasChildren: Boolean
|
||||||
|
get() = slots.any { it != null }
|
||||||
|
|
||||||
override fun iterator(): Iterator<ISynchable> {
|
override fun iterator(): Iterator<ISynchable> {
|
||||||
return slots.iterator().map { it?.synchable }.filterNotNull()
|
return slots.iterator().map { it?.synchable }.filterNotNull()
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user