Refine graphs logic, make neighbouring be not limited on in-world directions, properly add freshly created nodes to graph after they have been properly initialized
This commit is contained in:
parent
be2424055d
commit
93d4329acd
@ -16,12 +16,11 @@ import net.minecraftforge.event.server.ServerStoppingEvent
|
|||||||
import net.minecraftforge.fml.loading.FMLLoader
|
import net.minecraftforge.fml.loading.FMLLoader
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage
|
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage
|
||||||
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
|
|
||||||
import ru.dbotthepony.mc.otm.client.minecraft
|
import ru.dbotthepony.mc.otm.client.minecraft
|
||||||
import ru.dbotthepony.mc.otm.core.util.IConditionalTickable
|
import ru.dbotthepony.mc.otm.core.util.IConditionalTickable
|
||||||
import ru.dbotthepony.mc.otm.core.util.ITickable
|
import ru.dbotthepony.mc.otm.core.util.ITickable
|
||||||
import ru.dbotthepony.mc.otm.core.util.TickList
|
import ru.dbotthepony.mc.otm.core.util.TickList
|
||||||
import ru.dbotthepony.mc.otm.graph.Abstract6Graph
|
import ru.dbotthepony.mc.otm.graph.GraphNodeList
|
||||||
import ru.dbotthepony.mc.otm.network.MatteryNetworkChannel
|
import ru.dbotthepony.mc.otm.network.MatteryNetworkChannel
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@ -150,7 +149,7 @@ fun onServerTick(event: ServerTickEvent) {
|
|||||||
} else {
|
} else {
|
||||||
postServerTick.tick()
|
postServerTick.tick()
|
||||||
// чтоб не плодить кучу подписчиков, вызовем напрямую отсюда
|
// чтоб не плодить кучу подписчиков, вызовем напрямую отсюда
|
||||||
Abstract6Graph.tick()
|
GraphNodeList.tick()
|
||||||
AbstractProfiledStorage.onServerPostTick()
|
AbstractProfiledStorage.onServerPostTick()
|
||||||
MatteryNetworkChannel.onServerPostTick()
|
MatteryNetworkChannel.onServerPostTick()
|
||||||
}
|
}
|
||||||
|
@ -15,20 +15,24 @@ import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
|||||||
|
|
||||||
class MatterCableBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryBlockEntity(MBlockEntities.MATTER_CABLE, p_155229_, p_155230_) {
|
class MatterCableBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryBlockEntity(MBlockEntities.MATTER_CABLE, p_155229_, p_155230_) {
|
||||||
val matterNode = object : MatterNode() {
|
val matterNode = object : MatterNode() {
|
||||||
override fun onNeighbour(direction: Direction) {
|
override fun onNeighbour(link: Link) {
|
||||||
val newState = blockState.setValue(CableBlock.MAPPING_CONNECTION_PROP[direction]!!, true)
|
if (link is DirectionLink) {
|
||||||
|
val newState = blockState.setValue(CableBlock.MAPPING_CONNECTION_PROP[link.direction]!!, true)
|
||||||
|
|
||||||
if (newState !== blockState && SERVER_IS_LIVE)
|
if (newState !== blockState && SERVER_IS_LIVE)
|
||||||
level?.setBlock(blockPos, newState, Block.UPDATE_CLIENTS)
|
level?.setBlock(blockPos, newState, Block.UPDATE_CLIENTS)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onUnNeighbour(direction: Direction) {
|
override fun onUnNeighbour(link: Link) {
|
||||||
val newState = blockState.setValue(CableBlock.MAPPING_CONNECTION_PROP[direction]!!, false)
|
if (link is DirectionLink) {
|
||||||
|
val newState = blockState.setValue(CableBlock.MAPPING_CONNECTION_PROP[link.direction]!!, false)
|
||||||
|
|
||||||
if (newState !== blockState && SERVER_IS_LIVE)
|
if (newState !== blockState && SERVER_IS_LIVE)
|
||||||
level?.setBlock(blockPos, newState, Block.UPDATE_CLIENTS)
|
level?.setBlock(blockPos, newState, Block.UPDATE_CLIENTS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
exposeGlobally(MatteryCapability.MATTER_NODE, matterNode)
|
exposeGlobally(MatteryCapability.MATTER_NODE, matterNode)
|
||||||
@ -50,20 +54,24 @@ class StorageCableBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Matt
|
|||||||
override fun attachComponents(to: StorageGraph) {}
|
override fun attachComponents(to: StorageGraph) {}
|
||||||
override fun removeComponents(from: StorageGraph) {}
|
override fun removeComponents(from: StorageGraph) {}
|
||||||
|
|
||||||
override fun onNeighbour(direction: Direction) {
|
override fun onNeighbour(link: Link) {
|
||||||
val newState = blockState.setValue(CableBlock.MAPPING_CONNECTION_PROP[direction]!!, true)
|
if (link is DirectionLink) {
|
||||||
|
val newState = blockState.setValue(CableBlock.MAPPING_CONNECTION_PROP[link.direction]!!, true)
|
||||||
|
|
||||||
if (newState !== blockState && SERVER_IS_LIVE)
|
if (newState !== blockState && SERVER_IS_LIVE)
|
||||||
level!!.setBlock(blockPos, newState, Block.UPDATE_CLIENTS)
|
level!!.setBlock(blockPos, newState, Block.UPDATE_CLIENTS)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onUnNeighbour(direction: Direction) {
|
override fun onUnNeighbour(link: Link) {
|
||||||
val newState = blockState.setValue(CableBlock.MAPPING_CONNECTION_PROP[direction]!!, false)
|
if (link is DirectionLink) {
|
||||||
|
val newState = blockState.setValue(CableBlock.MAPPING_CONNECTION_PROP[link.direction]!!, false)
|
||||||
|
|
||||||
if (newState !== blockState && SERVER_IS_LIVE)
|
if (newState !== blockState && SERVER_IS_LIVE)
|
||||||
level!!.setBlock(blockPos, newState, Block.UPDATE_CLIENTS)
|
level!!.setBlock(blockPos, newState, Block.UPDATE_CLIENTS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
exposeGlobally(MatteryCapability.STORAGE_NODE, storageNode)
|
exposeGlobally(MatteryCapability.STORAGE_NODE, storageNode)
|
||||||
|
@ -69,20 +69,24 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
|
|||||||
}
|
}
|
||||||
|
|
||||||
val cell: StorageNode = object : StorageNode(energy) {
|
val cell: StorageNode = object : StorageNode(energy) {
|
||||||
override fun onNeighbour(direction: Direction) {
|
override fun onNeighbour(link: Link) {
|
||||||
val newState = this@StorageBusBlockEntity.blockState.setValue(CableBlock.MAPPING_CONNECTION_PROP[direction]!!, true)
|
if (link is DirectionLink) {
|
||||||
|
val newState = this@StorageBusBlockEntity.blockState.setValue(CableBlock.MAPPING_CONNECTION_PROP[link.direction]!!, true)
|
||||||
|
|
||||||
if (newState !== this@StorageBusBlockEntity.blockState && SERVER_IS_LIVE)
|
if (newState !== this@StorageBusBlockEntity.blockState && SERVER_IS_LIVE)
|
||||||
level?.setBlock(blockPos, newState, Block.UPDATE_CLIENTS)
|
level?.setBlock(blockPos, newState, Block.UPDATE_CLIENTS)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onUnNeighbour(direction: Direction) {
|
override fun onUnNeighbour(link: Link) {
|
||||||
val newState = this@StorageBusBlockEntity.blockState.setValue(CableBlock.MAPPING_CONNECTION_PROP[direction]!!, false)
|
if (link is DirectionLink) {
|
||||||
|
val newState = this@StorageBusBlockEntity.blockState.setValue(CableBlock.MAPPING_CONNECTION_PROP[link.direction]!!, false)
|
||||||
|
|
||||||
if (newState !== this@StorageBusBlockEntity.blockState && SERVER_IS_LIVE)
|
if (newState !== this@StorageBusBlockEntity.blockState && SERVER_IS_LIVE)
|
||||||
level?.setBlock(blockPos, newState, Block.UPDATE_CLIENTS)
|
level?.setBlock(blockPos, newState, Block.UPDATE_CLIENTS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
exposeSideless(MatteryCapability.STORAGE_NODE, cell)
|
exposeSideless(MatteryCapability.STORAGE_NODE, cell)
|
||||||
|
@ -58,21 +58,24 @@ abstract class AbstractStorageImportExport<T>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val cell: StorageNode = object : StorageNode(energy) {
|
val cell: StorageNode = object : StorageNode(energy) {
|
||||||
override fun onNeighbour(direction: Direction) {
|
override fun onNeighbour(link: Link) {
|
||||||
|
if (link is DirectionLink) {
|
||||||
level?.once {
|
level?.once {
|
||||||
if (!isRemoved) {
|
if (!isRemoved) {
|
||||||
val newState = this@AbstractStorageImportExport.blockState.setValue(CableBlock.MAPPING_CONNECTION_PROP[direction]!!, true)
|
val newState = this@AbstractStorageImportExport.blockState.setValue(CableBlock.MAPPING_CONNECTION_PROP[link.direction]!!, true)
|
||||||
|
|
||||||
if (newState !== this@AbstractStorageImportExport.blockState && SERVER_IS_LIVE)
|
if (newState !== this@AbstractStorageImportExport.blockState && SERVER_IS_LIVE)
|
||||||
level?.setBlock(blockPos, newState, Block.UPDATE_CLIENTS)
|
level?.setBlock(blockPos, newState, Block.UPDATE_CLIENTS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onUnNeighbour(direction: Direction) {
|
override fun onUnNeighbour(link: Link) {
|
||||||
|
if (link is DirectionLink) {
|
||||||
level?.once {
|
level?.once {
|
||||||
if (!isRemoved) {
|
if (!isRemoved) {
|
||||||
val newState = this@AbstractStorageImportExport.blockState.setValue(CableBlock.MAPPING_CONNECTION_PROP[direction]!!, false)
|
val newState = this@AbstractStorageImportExport.blockState.setValue(CableBlock.MAPPING_CONNECTION_PROP[link.direction]!!, false)
|
||||||
|
|
||||||
if (newState !== this@AbstractStorageImportExport.blockState && SERVER_IS_LIVE)
|
if (newState !== this@AbstractStorageImportExport.blockState && SERVER_IS_LIVE)
|
||||||
level?.setBlock(blockPos, newState, Block.UPDATE_CLIENTS)
|
level?.setBlock(blockPos, newState, Block.UPDATE_CLIENTS)
|
||||||
@ -80,6 +83,7 @@ abstract class AbstractStorageImportExport<T>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
exposeSideless(MatteryCapability.STORAGE_NODE, cell)
|
exposeSideless(MatteryCapability.STORAGE_NODE, cell)
|
||||||
|
@ -1,172 +0,0 @@
|
|||||||
package ru.dbotthepony.mc.otm.graph
|
|
||||||
|
|
||||||
import ru.dbotthepony.mc.otm.core.util.IConditionalTickable
|
|
||||||
import ru.dbotthepony.mc.otm.core.util.ITickable
|
|
||||||
import java.lang.ref.WeakReference
|
|
||||||
import java.util.*
|
|
||||||
import kotlin.collections.ArrayList
|
|
||||||
|
|
||||||
open class Abstract6Graph<N : Graph6Node<N, G>, G : Abstract6Graph<N, G>> : IConditionalTickable {
|
|
||||||
private val nodesInternal = ArrayList<N>()
|
|
||||||
private val conditional = ArrayList<IConditionalTickable>()
|
|
||||||
private val always = ArrayList<ITickable>()
|
|
||||||
|
|
||||||
protected val nodes: List<N> = Collections.unmodifiableList(nodesInternal)
|
|
||||||
|
|
||||||
val size get() = nodesInternal.size
|
|
||||||
|
|
||||||
var isMerged = false
|
|
||||||
private set
|
|
||||||
|
|
||||||
private var isTicking = false
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows storing arbitrary data by external code
|
|
||||||
*/
|
|
||||||
val userData = HashMap<UUID, Any>()
|
|
||||||
|
|
||||||
open fun onNodeRemoved(node: N) {}
|
|
||||||
open fun onNodeAdded(node: N) {}
|
|
||||||
open fun onMergedInto(other: G) {}
|
|
||||||
|
|
||||||
protected open fun innerTick(): Boolean {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun tick(): Boolean {
|
|
||||||
if (isMerged)
|
|
||||||
return false
|
|
||||||
|
|
||||||
// позволяет вершинам изменять список тикающих вершин
|
|
||||||
for (i in conditional.size - 1 downTo 0) {
|
|
||||||
val node = conditional[i]
|
|
||||||
|
|
||||||
if (!node.tick()) {
|
|
||||||
conditional.removeAt(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// позволяет вершинам изменять список тикающих вершин
|
|
||||||
for (node in always.size - 1 downTo 0) {
|
|
||||||
always[node].tick()
|
|
||||||
}
|
|
||||||
|
|
||||||
return innerTick() || always.isNotEmpty() || conditional.isNotEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addNode(node: N): Boolean {
|
|
||||||
if (node in nodesInternal)
|
|
||||||
return false
|
|
||||||
|
|
||||||
nodesInternal.add(node)
|
|
||||||
|
|
||||||
if (node is IConditionalTickable) {
|
|
||||||
conditional.add(node)
|
|
||||||
|
|
||||||
if (!isTicking) {
|
|
||||||
isTicking = true
|
|
||||||
next.add(WeakReference(this))
|
|
||||||
}
|
|
||||||
} else if (node is ITickable) {
|
|
||||||
always.add(node)
|
|
||||||
|
|
||||||
if (!isTicking) {
|
|
||||||
isTicking = true
|
|
||||||
next.add(WeakReference(this))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onNodeAdded(node)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun removeNode(node: N): Boolean {
|
|
||||||
if (!nodesInternal.remove(node))
|
|
||||||
return false
|
|
||||||
|
|
||||||
nodesInternal.remove(node)
|
|
||||||
|
|
||||||
if (node is IConditionalTickable)
|
|
||||||
conditional.remove(node)
|
|
||||||
else if (node is ITickable)
|
|
||||||
always.remove(node)
|
|
||||||
|
|
||||||
onNodeRemoved(node)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun retain(nodes: Set<N>) {
|
|
||||||
for (i in this.nodesInternal.size - 1 downTo 0) {
|
|
||||||
if (this.nodesInternal[i] !in nodes) {
|
|
||||||
val node = this.nodesInternal[i]
|
|
||||||
|
|
||||||
this.nodesInternal.removeAt(i)
|
|
||||||
|
|
||||||
if (node is IConditionalTickable)
|
|
||||||
conditional.remove(node)
|
|
||||||
else if (node is ITickable)
|
|
||||||
always.remove(node)
|
|
||||||
|
|
||||||
onNodeRemoved(node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun merge(other: G, setter: (N, G) -> Unit): G {
|
|
||||||
if (other === this)
|
|
||||||
return this
|
|
||||||
|
|
||||||
if (size >= other.size) {
|
|
||||||
for (node in other.nodesInternal) {
|
|
||||||
nodesInternal.add(node)
|
|
||||||
setter.invoke(node, this as G)
|
|
||||||
onNodeAdded(node)
|
|
||||||
|
|
||||||
if (node is IConditionalTickable) {
|
|
||||||
conditional.add(node)
|
|
||||||
|
|
||||||
if (!isTicking) {
|
|
||||||
isTicking = true
|
|
||||||
next.add(WeakReference(this))
|
|
||||||
}
|
|
||||||
} else if (node is ITickable) {
|
|
||||||
always.add(node)
|
|
||||||
|
|
||||||
if (!isTicking) {
|
|
||||||
isTicking = true
|
|
||||||
next.add(WeakReference(this))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
other.isMerged = true
|
|
||||||
other.onMergedInto(this as G)
|
|
||||||
return this
|
|
||||||
} else {
|
|
||||||
return other.merge(this as G, setter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val graphs = ArrayList<WeakReference<Abstract6Graph<*, *>>>()
|
|
||||||
private val next = ArrayList<WeakReference<Abstract6Graph<*, *>>>()
|
|
||||||
|
|
||||||
fun tick() {
|
|
||||||
if (next.isNotEmpty()) {
|
|
||||||
graphs.addAll(next)
|
|
||||||
next.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
val iterator = graphs.iterator()
|
|
||||||
|
|
||||||
for (value in iterator) {
|
|
||||||
val graph = value.get()
|
|
||||||
|
|
||||||
if (graph == null || !graph.tick()) {
|
|
||||||
graph?.isTicking = false
|
|
||||||
iterator.remove()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,268 +0,0 @@
|
|||||||
package ru.dbotthepony.mc.otm.graph
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet
|
|
||||||
import net.minecraft.core.BlockPos
|
|
||||||
import net.minecraft.core.Direction
|
|
||||||
import net.minecraft.core.SectionPos
|
|
||||||
import net.minecraft.server.level.ServerLevel
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity
|
|
||||||
import net.minecraftforge.common.capabilities.Capability
|
|
||||||
import ru.dbotthepony.mc.otm.addTicker
|
|
||||||
import ru.dbotthepony.mc.otm.core.math.plus
|
|
||||||
import ru.dbotthepony.mc.otm.core.math.unaryMinus
|
|
||||||
import ru.dbotthepony.mc.otm.core.orNull
|
|
||||||
import java.util.EnumMap
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
|
||||||
|
|
||||||
open class Graph6Node<N : Graph6Node<N, G>, G : Abstract6Graph<N, G>>(val graphFactory: () -> G) {
|
|
||||||
private val neighbours = EnumMap<Direction, N>(Direction::class.java)
|
|
||||||
|
|
||||||
var graph: G = graphFactory.invoke()
|
|
||||||
private set
|
|
||||||
|
|
||||||
init {
|
|
||||||
graph.addNode(this as N)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var seen: Int = 0
|
|
||||||
|
|
||||||
operator fun get(direction: Direction): N? = neighbours[direction]
|
|
||||||
|
|
||||||
operator fun set(direction: Direction, node: N?) {
|
|
||||||
set(direction, node, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun set(direction: Direction, node: N?, allowReplacement: Boolean) {
|
|
||||||
check(isValid) { "Can not neighbour any node while this node is invalid" }
|
|
||||||
|
|
||||||
val old = neighbours[direction]
|
|
||||||
if (old === node) return
|
|
||||||
|
|
||||||
if (old != null)
|
|
||||||
breakConnection(this as N, old, direction)
|
|
||||||
|
|
||||||
if (node != null) {
|
|
||||||
require(node.isValid) { "Can not neighbour invalid node" }
|
|
||||||
|
|
||||||
val opposite = -direction
|
|
||||||
|
|
||||||
if (allowReplacement) {
|
|
||||||
node.neighbours[opposite]?.let {
|
|
||||||
breakConnection(node, it, opposite)
|
|
||||||
}
|
|
||||||
|
|
||||||
check(node.neighbours[opposite] == null) { "$node didn't break connection at direction $opposite" }
|
|
||||||
} else {
|
|
||||||
check(node.neighbours[opposite] == null) { "Trying to form connection from $this to $node at direction $direction, but $node already has neighbour at $opposite (${node.neighbours[opposite]})!" }
|
|
||||||
}
|
|
||||||
|
|
||||||
node.neighbours[opposite] = this as N
|
|
||||||
neighbours[direction] = node
|
|
||||||
node.graph.merge(graph, setter)
|
|
||||||
node.onNeighbour(opposite)
|
|
||||||
onNeighbour(direction)
|
|
||||||
} else {
|
|
||||||
neighbours.remove(direction)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var top: N?
|
|
||||||
get() = neighbours[Direction.UP]
|
|
||||||
set(value) {
|
|
||||||
set(Direction.UP, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
var bottom: N?
|
|
||||||
get() = neighbours[Direction.DOWN]
|
|
||||||
set(value) {
|
|
||||||
set(Direction.DOWN, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
var south: N?
|
|
||||||
get() = neighbours[Direction.SOUTH]
|
|
||||||
set(value) {
|
|
||||||
set(Direction.SOUTH, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
var north: N?
|
|
||||||
get() = neighbours[Direction.NORTH]
|
|
||||||
set(value) {
|
|
||||||
set(Direction.NORTH, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
var west: N?
|
|
||||||
get() = neighbours[Direction.WEST]
|
|
||||||
set(value) {
|
|
||||||
set(Direction.WEST, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
var east: N?
|
|
||||||
get() = neighbours[Direction.EAST]
|
|
||||||
set(value) {
|
|
||||||
set(Direction.EAST, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun onNeighbour(direction: Direction) {}
|
|
||||||
open fun onUnNeighbour(direction: Direction) {}
|
|
||||||
|
|
||||||
protected open fun invalidate() {}
|
|
||||||
protected open fun revive() {}
|
|
||||||
|
|
||||||
var isValid: Boolean = true
|
|
||||||
set(value) {
|
|
||||||
if (value == field) return
|
|
||||||
field = value
|
|
||||||
|
|
||||||
if (!value) {
|
|
||||||
val neighbours = ArrayList(neighbours.entries)
|
|
||||||
|
|
||||||
for ((dir, node) in neighbours) {
|
|
||||||
breakConnection(this as N, node, dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
graph.removeNode(this as N)
|
|
||||||
invalidate()
|
|
||||||
} else {
|
|
||||||
revive()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun discover(
|
|
||||||
level: ServerLevel,
|
|
||||||
blockPos: BlockPos,
|
|
||||||
nodeGetter: (BlockEntity) -> N?
|
|
||||||
) {
|
|
||||||
if (!isValid) return
|
|
||||||
|
|
||||||
level.addTicker {
|
|
||||||
isValid && !discoverStep(level, blockPos, nodeGetter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun discover(
|
|
||||||
level: ServerLevel,
|
|
||||||
blockPos: BlockPos,
|
|
||||||
capability: Capability<out N>
|
|
||||||
) {
|
|
||||||
if (!isValid) return
|
|
||||||
|
|
||||||
level.addTicker {
|
|
||||||
isValid && !discoverStep(level, blockPos) { it.getCapability(capability).orNull() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun discover(blockEntity: BlockEntity, nodeGetter: (BlockEntity) -> N?) {
|
|
||||||
discover(blockEntity.level as? ServerLevel ?: return, blockEntity.blockPos, nodeGetter)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun discover(blockEntity: BlockEntity, capability: Capability<out N>) {
|
|
||||||
discover(blockEntity.level as? ServerLevel ?: return, blockEntity.blockPos, capability)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun discoverStep(
|
|
||||||
level: ServerLevel,
|
|
||||||
blockPos: BlockPos,
|
|
||||||
nodeGetter: (BlockEntity) -> N?,
|
|
||||||
): Boolean {
|
|
||||||
if (!isValid) return false
|
|
||||||
var fullDiscovery = true
|
|
||||||
|
|
||||||
for (dir in directions) {
|
|
||||||
val offset = blockPos + dir
|
|
||||||
val chunk = level.chunkSource.getChunkNow(SectionPos.blockToSectionCoord(offset.x), SectionPos.blockToSectionCoord(offset.z))
|
|
||||||
|
|
||||||
if (chunk == null) {
|
|
||||||
fullDiscovery = false
|
|
||||||
set(dir, null)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
val entity = chunk.getBlockEntity(offset)
|
|
||||||
|
|
||||||
if (entity != null) {
|
|
||||||
set(dir, nodeGetter(entity))
|
|
||||||
} else {
|
|
||||||
set(dir, null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fullDiscovery
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val setter = Graph6Node<*, *>::graph::set
|
|
||||||
private val nextSeen = AtomicInteger()
|
|
||||||
private val directions = Direction.values()
|
|
||||||
|
|
||||||
private fun <N : Graph6Node<N, G>, G : Abstract6Graph<N, G>> breakConnection(a: N, b: N, direction: Direction) {
|
|
||||||
val opposite = -direction
|
|
||||||
|
|
||||||
require(a.neighbours[direction] === b) { "$a does not neighbour with $b at direction $direction (forward)" }
|
|
||||||
require(b.neighbours[opposite] === a) { "$b does not neighbour with $a at direction $opposite (backward)" }
|
|
||||||
require(a.graph === b.graph) { "$a and $b belong to different graphs (${a.graph} vs ${b.graph})" }
|
|
||||||
|
|
||||||
a.neighbours.remove(direction)
|
|
||||||
b.neighbours.remove(opposite)
|
|
||||||
|
|
||||||
val seen = nextSeen.incrementAndGet()
|
|
||||||
val flood1 = flood(a, seen)
|
|
||||||
|
|
||||||
if (b.seen != seen) {
|
|
||||||
val flood2 = flood(b, seen)
|
|
||||||
|
|
||||||
val big: ArrayList<N>
|
|
||||||
val small: ArrayList<N>
|
|
||||||
|
|
||||||
if (flood1.size >= flood2.size) {
|
|
||||||
big = flood1
|
|
||||||
small = flood2
|
|
||||||
} else {
|
|
||||||
big = flood2
|
|
||||||
small = flood1
|
|
||||||
}
|
|
||||||
|
|
||||||
a.graph.retain(ReferenceOpenHashSet(big))
|
|
||||||
val newGraph = a.graphFactory.invoke()
|
|
||||||
|
|
||||||
for (node in small) {
|
|
||||||
node.graph = newGraph
|
|
||||||
}
|
|
||||||
|
|
||||||
for (node in small) {
|
|
||||||
if (node.isValid) {
|
|
||||||
newGraph.addNode(node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a.isValid)
|
|
||||||
a.onUnNeighbour(direction)
|
|
||||||
|
|
||||||
if (b.isValid)
|
|
||||||
b.onUnNeighbour(opposite)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <N : Graph6Node<N, G>, G : Abstract6Graph<N, G>> flood(startingNode: N, seen: Int): ArrayList<N> {
|
|
||||||
val unopen = ArrayList<N>()
|
|
||||||
val result = ArrayList<N>()
|
|
||||||
unopen.add(startingNode)
|
|
||||||
|
|
||||||
while (unopen.isNotEmpty()) {
|
|
||||||
val last = unopen.removeLast()
|
|
||||||
|
|
||||||
if (last.seen < seen) {
|
|
||||||
result.add(last)
|
|
||||||
last.seen = seen
|
|
||||||
|
|
||||||
for (node in last.neighbours.values) {
|
|
||||||
if (node.seen < seen) {
|
|
||||||
unopen.add(node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
292
src/main/kotlin/ru/dbotthepony/mc/otm/graph/GraphNode.kt
Normal file
292
src/main/kotlin/ru/dbotthepony/mc/otm/graph/GraphNode.kt
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.graph
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||||||
|
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.core.Direction
|
||||||
|
import net.minecraft.core.SectionPos
|
||||||
|
import net.minecraft.server.level.ServerLevel
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity
|
||||||
|
import net.minecraftforge.common.capabilities.Capability
|
||||||
|
import ru.dbotthepony.mc.otm.addTicker
|
||||||
|
import ru.dbotthepony.mc.otm.core.math.plus
|
||||||
|
import ru.dbotthepony.mc.otm.core.orNull
|
||||||
|
|
||||||
|
open class GraphNode<N : GraphNode<N, G>, G : GraphNodeList<N, G>>(val graphFactory: () -> G) {
|
||||||
|
interface Link {
|
||||||
|
val opposite: Link
|
||||||
|
operator fun unaryMinus() = opposite
|
||||||
|
}
|
||||||
|
|
||||||
|
data class DirectionLink(val direction: Direction) : Link {
|
||||||
|
override val opposite: Link
|
||||||
|
get() = wrapped[direction.opposite.ordinal]
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "Link[$direction]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val neighbours = Object2ObjectOpenHashMap<Link, N>()
|
||||||
|
|
||||||
|
var graph: G = graphFactory.invoke()
|
||||||
|
internal set
|
||||||
|
|
||||||
|
init {
|
||||||
|
check(graph.addNodeQueued(this as N))
|
||||||
|
}
|
||||||
|
|
||||||
|
private var seen: Int = 0
|
||||||
|
|
||||||
|
operator fun get(key: Link): N? = neighbours[key]
|
||||||
|
|
||||||
|
operator fun set(key: Link, node: N?) {
|
||||||
|
set(key, node, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun set(key: Link, target: N?, allowReplacement: Boolean) {
|
||||||
|
check(isValid) { "Can not neighbour any node while this node is invalid" }
|
||||||
|
|
||||||
|
val old = neighbours[key]
|
||||||
|
if (old === target) return
|
||||||
|
|
||||||
|
if (old != null)
|
||||||
|
breakConnection(this as N, old, key)
|
||||||
|
|
||||||
|
if (target != null) {
|
||||||
|
require(target.isValid) { "Can not neighbour invalid node" }
|
||||||
|
|
||||||
|
val opposite = key.opposite
|
||||||
|
|
||||||
|
if (allowReplacement) {
|
||||||
|
target.neighbours[opposite]?.let {
|
||||||
|
breakConnection(target, it, opposite)
|
||||||
|
}
|
||||||
|
|
||||||
|
check(target.neighbours[opposite] == null) { "$target didn't break connection at direction $opposite" }
|
||||||
|
} else {
|
||||||
|
check(target.neighbours[opposite] == null) { "Trying to form connection from $this to $target at $key, but $target already has neighbour at $opposite (${target.neighbours[opposite]})!" }
|
||||||
|
}
|
||||||
|
|
||||||
|
target.neighbours[opposite] = this as N
|
||||||
|
neighbours[key] = target
|
||||||
|
target.graph.merge(graph)
|
||||||
|
target.onNeighbour(opposite)
|
||||||
|
onNeighbour(key)
|
||||||
|
} else {
|
||||||
|
neighbours.remove(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var top: N?
|
||||||
|
get() = neighbours[UP]
|
||||||
|
set(value) {
|
||||||
|
set(UP, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
var bottom: N?
|
||||||
|
get() = neighbours[DOWN]
|
||||||
|
set(value) {
|
||||||
|
set(DOWN, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
var south: N?
|
||||||
|
get() = neighbours[SOUTH]
|
||||||
|
set(value) {
|
||||||
|
set(SOUTH, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
var north: N?
|
||||||
|
get() = neighbours[NORTH]
|
||||||
|
set(value) {
|
||||||
|
set(NORTH, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
var west: N?
|
||||||
|
get() = neighbours[WEST]
|
||||||
|
set(value) {
|
||||||
|
set(WEST, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
var east: N?
|
||||||
|
get() = neighbours[EAST]
|
||||||
|
set(value) {
|
||||||
|
set(EAST, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun onNeighbour(link: Link) {}
|
||||||
|
protected open fun onUnNeighbour(link: Link) {}
|
||||||
|
|
||||||
|
protected open fun invalidate() {}
|
||||||
|
protected open fun revive() {}
|
||||||
|
|
||||||
|
var isValid: Boolean = true
|
||||||
|
set(value) {
|
||||||
|
if (value == field) return
|
||||||
|
field = value
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
val neighbours = neighbours.entries.map { it.key to it.value }
|
||||||
|
|
||||||
|
for ((link, node) in neighbours) {
|
||||||
|
removeNeighbours(this as N, node, link)
|
||||||
|
}
|
||||||
|
|
||||||
|
graph.removeNode(this as N)
|
||||||
|
rebuildGraphs(neighbours.map { it.second })
|
||||||
|
invalidate()
|
||||||
|
} else {
|
||||||
|
revive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun discover(
|
||||||
|
level: ServerLevel,
|
||||||
|
blockPos: BlockPos,
|
||||||
|
nodeGetter: (BlockEntity) -> N?
|
||||||
|
) {
|
||||||
|
if (!isValid) return
|
||||||
|
|
||||||
|
level.addTicker {
|
||||||
|
isValid && !discoverStep(level, blockPos, nodeGetter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun discover(
|
||||||
|
level: ServerLevel,
|
||||||
|
blockPos: BlockPos,
|
||||||
|
capability: Capability<out N>
|
||||||
|
) {
|
||||||
|
if (!isValid) return
|
||||||
|
|
||||||
|
level.addTicker {
|
||||||
|
isValid && !discoverStep(level, blockPos) { it.getCapability(capability).orNull() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun discover(blockEntity: BlockEntity, nodeGetter: (BlockEntity) -> N?) {
|
||||||
|
discover(blockEntity.level as? ServerLevel ?: return, blockEntity.blockPos, nodeGetter)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun discover(blockEntity: BlockEntity, capability: Capability<out N>) {
|
||||||
|
discover(blockEntity.level as? ServerLevel ?: return, blockEntity.blockPos, capability)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun discoverStep(
|
||||||
|
level: ServerLevel,
|
||||||
|
blockPos: BlockPos,
|
||||||
|
nodeGetter: (BlockEntity) -> N?,
|
||||||
|
): Boolean {
|
||||||
|
if (!isValid) return false
|
||||||
|
var fullDiscovery = true
|
||||||
|
|
||||||
|
for (dir in wrapped) {
|
||||||
|
val offset = blockPos + dir.direction
|
||||||
|
val chunk = level.chunkSource.getChunkNow(SectionPos.blockToSectionCoord(offset.x), SectionPos.blockToSectionCoord(offset.z))
|
||||||
|
|
||||||
|
if (chunk == null) {
|
||||||
|
fullDiscovery = false
|
||||||
|
set(dir, null)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val entity = chunk.getBlockEntity(offset)
|
||||||
|
|
||||||
|
if (entity != null) {
|
||||||
|
set(dir, nodeGetter(entity))
|
||||||
|
} else {
|
||||||
|
set(dir, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fullDiscovery
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private var nextSeen = 0
|
||||||
|
private val wrapped = Direction.entries.map { DirectionLink(it) }
|
||||||
|
|
||||||
|
fun link(direction: Direction): Link {
|
||||||
|
return wrapped[direction.ordinal]
|
||||||
|
}
|
||||||
|
|
||||||
|
val UP = link(Direction.UP)
|
||||||
|
val DOWN = link(Direction.DOWN)
|
||||||
|
val NORTH = link(Direction.NORTH)
|
||||||
|
val SOUTH = link(Direction.SOUTH)
|
||||||
|
val WEST = link(Direction.WEST)
|
||||||
|
val EAST = link(Direction.EAST)
|
||||||
|
|
||||||
|
private fun <N : GraphNode<N, G>, G : GraphNodeList<N, G>> removeNeighbours(a: N, b: N, key: Link) {
|
||||||
|
val opposite = key.opposite
|
||||||
|
|
||||||
|
// проверяем, действительно ли a соединён с b по key
|
||||||
|
require(a.neighbours[key] === b) { "$a does not neighbour with $b at $key (a -> b)" }
|
||||||
|
|
||||||
|
// проверяем обратную связь
|
||||||
|
require(b.neighbours[opposite] === a) { "$b does not neighbour with $a at $opposite (b -> a)" }
|
||||||
|
|
||||||
|
// находятся ли они в одном графе
|
||||||
|
require(a.graph === b.graph) { "$a and $b belong to different graphs (${a.graph} / ${b.graph})" }
|
||||||
|
|
||||||
|
a.neighbours.remove(key)
|
||||||
|
b.neighbours.remove(opposite)
|
||||||
|
|
||||||
|
if (a.isValid) a.onUnNeighbour(key)
|
||||||
|
if (b.isValid) b.onUnNeighbour(key.opposite)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <N : GraphNode<N, G>, G : GraphNodeList<N, G>> rebuildGraphs(nodes: Collection<N>) {
|
||||||
|
if (nodes.isEmpty())
|
||||||
|
return
|
||||||
|
|
||||||
|
val seen = ++nextSeen
|
||||||
|
val floods = ArrayList<ArrayList<N>>()
|
||||||
|
|
||||||
|
for (node in nodes) {
|
||||||
|
if (node.seen < seen) {
|
||||||
|
floods.add(flood(node, seen))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
floods.sortedBy { it.first().graph.size }
|
||||||
|
floods.first().first().graph.retain(ReferenceOpenHashSet(floods.first()))
|
||||||
|
|
||||||
|
for (i in 1 until floods.size) {
|
||||||
|
val graph = floods[i].first().graphFactory.invoke()
|
||||||
|
|
||||||
|
for (node in floods[i]) {
|
||||||
|
graph.addNode(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <N : GraphNode<N, G>, G : GraphNodeList<N, G>> breakConnection(a: N, b: N, key: Link) {
|
||||||
|
removeNeighbours(a, b, key)
|
||||||
|
rebuildGraphs(listOf(a, b))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <N : GraphNode<N, G>, G : GraphNodeList<N, G>> flood(startingNode: N, seen: Int): ArrayList<N> {
|
||||||
|
val unopen = ArrayList<N>()
|
||||||
|
val result = ArrayList<N>()
|
||||||
|
unopen.add(startingNode)
|
||||||
|
|
||||||
|
while (unopen.isNotEmpty()) {
|
||||||
|
val last = unopen.removeLast()
|
||||||
|
|
||||||
|
if (last.seen < seen) {
|
||||||
|
result.add(last)
|
||||||
|
last.seen = seen
|
||||||
|
|
||||||
|
for (node in last.neighbours.values) {
|
||||||
|
if (node.seen < seen) {
|
||||||
|
unopen.add(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
199
src/main/kotlin/ru/dbotthepony/mc/otm/graph/GraphNodeList.kt
Normal file
199
src/main/kotlin/ru/dbotthepony/mc/otm/graph/GraphNodeList.kt
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.graph
|
||||||
|
|
||||||
|
import ru.dbotthepony.mc.otm.core.util.IConditionalTickable
|
||||||
|
import ru.dbotthepony.mc.otm.core.util.ITickable
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.collections.ArrayDeque
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
|
open class GraphNodeList<N : GraphNode<N, G>, G : GraphNodeList<N, G>> : IConditionalTickable {
|
||||||
|
private val queuedAdd = ArrayDeque<N>()
|
||||||
|
private val queuedRemove = ArrayDeque<N>()
|
||||||
|
private val nodesInternal = ArrayList<N>()
|
||||||
|
private val conditional = ArrayList<IConditionalTickable>()
|
||||||
|
private val always = ArrayList<ITickable>()
|
||||||
|
private var isTicking = false
|
||||||
|
private var shouldQueueChanges = false
|
||||||
|
|
||||||
|
protected val nodes: List<N> = Collections.unmodifiableList(nodesInternal)
|
||||||
|
|
||||||
|
val size get() = nodesInternal.size
|
||||||
|
|
||||||
|
var isValid = true
|
||||||
|
private set
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows storing arbitrary data by external code
|
||||||
|
*/
|
||||||
|
val userData = HashMap<UUID, Any>()
|
||||||
|
|
||||||
|
protected open fun onNodeRemoved(node: N) {}
|
||||||
|
protected open fun onNodeAdded(node: N) {}
|
||||||
|
|
||||||
|
protected open fun innerTick(): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addNow(node: N) {
|
||||||
|
node.graph = this as G
|
||||||
|
nodesInternal.add(node)
|
||||||
|
|
||||||
|
if (node is IConditionalTickable)
|
||||||
|
conditional.add(node)
|
||||||
|
else if (node is ITickable)
|
||||||
|
always.add(node)
|
||||||
|
|
||||||
|
onNodeAdded(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeNow(node: N, removeFromList: Boolean = true) {
|
||||||
|
if (removeFromList)
|
||||||
|
nodesInternal.remove(node)
|
||||||
|
|
||||||
|
if (node is IConditionalTickable)
|
||||||
|
conditional.remove(node)
|
||||||
|
else if (node is ITickable)
|
||||||
|
always.remove(node)
|
||||||
|
|
||||||
|
onNodeRemoved(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processAddQueue() {
|
||||||
|
while (queuedAdd.isNotEmpty()) {
|
||||||
|
addNow(queuedAdd.removeFirst())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processRemoveQueue() {
|
||||||
|
while (queuedRemove.isNotEmpty()) {
|
||||||
|
removeNow(queuedRemove.removeFirst())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processQueues() {
|
||||||
|
processRemoveQueue()
|
||||||
|
processAddQueue()
|
||||||
|
}
|
||||||
|
|
||||||
|
final override fun tick(): Boolean {
|
||||||
|
if (!isValid)
|
||||||
|
return false
|
||||||
|
|
||||||
|
processQueues()
|
||||||
|
conditional.removeIf { !it.tick() }
|
||||||
|
for (node in always) node.tick()
|
||||||
|
|
||||||
|
return innerTick() || always.isNotEmpty() || conditional.isNotEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addNodeQueued(node: N): Boolean {
|
||||||
|
check(isValid) { "$this is no longer valid" }
|
||||||
|
|
||||||
|
if (node in nodesInternal || node in queuedAdd)
|
||||||
|
return false
|
||||||
|
|
||||||
|
queuedAdd.add(node)
|
||||||
|
beginTicking()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addNode(node: N): Boolean {
|
||||||
|
check(isValid) { "$this is no longer valid" }
|
||||||
|
|
||||||
|
if (node in nodesInternal)
|
||||||
|
return false
|
||||||
|
|
||||||
|
if (shouldQueueChanges) {
|
||||||
|
if (node in queuedAdd) return false
|
||||||
|
queuedAdd.add(node)
|
||||||
|
beginTicking()
|
||||||
|
} else {
|
||||||
|
queuedAdd.remove(node)
|
||||||
|
addNow(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeNode(node: N): Boolean {
|
||||||
|
check(isValid) { "$this is no longer valid" }
|
||||||
|
|
||||||
|
if (node !in nodesInternal)
|
||||||
|
return false
|
||||||
|
|
||||||
|
if (shouldQueueChanges) {
|
||||||
|
if (node in queuedRemove) return false
|
||||||
|
queuedRemove.add(node)
|
||||||
|
beginTicking()
|
||||||
|
} else {
|
||||||
|
queuedRemove.remove(node)
|
||||||
|
removeNow(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun retain(nodes: Set<N>) {
|
||||||
|
check(isValid) { "$this is no longer valid" }
|
||||||
|
queuedAdd.retainAll(nodes)
|
||||||
|
nodesInternal.removeIf {
|
||||||
|
if (it !in nodes) {
|
||||||
|
removeNow(it, false)
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun merge(other: G): G {
|
||||||
|
if (other === this)
|
||||||
|
return this
|
||||||
|
|
||||||
|
check(isValid) { "$this is no longer valid" }
|
||||||
|
|
||||||
|
if (size >= other.size) {
|
||||||
|
shouldQueueChanges = true
|
||||||
|
other.processQueues()
|
||||||
|
other.nodesInternal.forEach { if (it.isValid) addNow(it) }
|
||||||
|
processQueues()
|
||||||
|
shouldQueueChanges = false
|
||||||
|
|
||||||
|
other.isValid = false
|
||||||
|
return this as G
|
||||||
|
} else {
|
||||||
|
return other.merge(this as G)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun beginTicking() {
|
||||||
|
if (!isTicking) {
|
||||||
|
isTicking = true
|
||||||
|
queue.add(WeakReference(this))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val graphs = ArrayList<WeakReference<GraphNodeList<*, *>>>()
|
||||||
|
private val queue = ArrayList<WeakReference<GraphNodeList<*, *>>>()
|
||||||
|
|
||||||
|
fun tick() {
|
||||||
|
if (queue.isNotEmpty()) {
|
||||||
|
graphs.addAll(queue)
|
||||||
|
queue.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
val iterator = graphs.iterator()
|
||||||
|
|
||||||
|
for (value in iterator) {
|
||||||
|
val graph = value.get()
|
||||||
|
|
||||||
|
if (graph == null || !graph.tick()) {
|
||||||
|
graph?.isTicking = false
|
||||||
|
iterator.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,13 +6,13 @@ import ru.dbotthepony.mc.otm.capability.FlowDirection
|
|||||||
import ru.dbotthepony.mc.otm.capability.matter.*
|
import ru.dbotthepony.mc.otm.capability.matter.*
|
||||||
import ru.dbotthepony.mc.otm.core.filterNotNull
|
import ru.dbotthepony.mc.otm.core.filterNotNull
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
import ru.dbotthepony.mc.otm.graph.Abstract6Graph
|
import ru.dbotthepony.mc.otm.graph.GraphNodeList
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.function.Predicate
|
import java.util.function.Predicate
|
||||||
import java.util.stream.Stream
|
import java.util.stream.Stream
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
class MatterGraph : Abstract6Graph<MatterNode, MatterGraph>(), IMatterGraphListener {
|
class MatterGraph : GraphNodeList<MatterNode, MatterGraph>(), IMatterGraphListener {
|
||||||
private val listeners = ObjectOpenHashSet<IMatterGraphListener>()
|
private val listeners = ObjectOpenHashSet<IMatterGraphListener>()
|
||||||
|
|
||||||
fun addListener(listener: IMatterGraphListener) = listeners.add(listener)
|
fun addListener(listener: IMatterGraphListener) = listeners.add(listener)
|
||||||
|
@ -5,9 +5,9 @@ import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
|||||||
import ru.dbotthepony.mc.otm.capability.matter.IMatterStorage
|
import ru.dbotthepony.mc.otm.capability.matter.IMatterStorage
|
||||||
import ru.dbotthepony.mc.otm.capability.matter.IPatternStorage
|
import ru.dbotthepony.mc.otm.capability.matter.IPatternStorage
|
||||||
import ru.dbotthepony.mc.otm.capability.matter.IReplicationTaskProvider
|
import ru.dbotthepony.mc.otm.capability.matter.IReplicationTaskProvider
|
||||||
import ru.dbotthepony.mc.otm.graph.Graph6Node
|
import ru.dbotthepony.mc.otm.graph.GraphNode
|
||||||
|
|
||||||
open class MatterNode : Graph6Node<MatterNode, MatterGraph>(::MatterGraph), IMatterGraphListener {
|
open class MatterNode : GraphNode<MatterNode, MatterGraph>(::MatterGraph), IMatterGraphListener {
|
||||||
open fun getMatterHandler(): IMatterStorage? = null
|
open fun getMatterHandler(): IMatterStorage? = null
|
||||||
open fun getPatternHandler(): IPatternStorage? = null
|
open fun getPatternHandler(): IPatternStorage? = null
|
||||||
open fun getTaskHandler(): IReplicationTaskProvider? = null
|
open fun getTaskHandler(): IReplicationTaskProvider? = null
|
||||||
|
@ -2,19 +2,12 @@ package ru.dbotthepony.mc.otm.graph.storage
|
|||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
||||||
import net.minecraft.server.level.ServerLevel
|
|
||||||
import net.minecraft.world.level.Level
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity
|
|
||||||
import ru.dbotthepony.mc.otm.addTickerPre
|
|
||||||
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
||||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
import ru.dbotthepony.mc.otm.graph.GraphNodeList
|
||||||
import ru.dbotthepony.mc.otm.graph.Abstract6Graph
|
|
||||||
import ru.dbotthepony.mc.otm.graph.Graph6Node
|
|
||||||
import ru.dbotthepony.mc.otm.core.orNull
|
|
||||||
import ru.dbotthepony.mc.otm.storage.*
|
import ru.dbotthepony.mc.otm.storage.*
|
||||||
import java.util.LinkedList
|
import java.util.LinkedList
|
||||||
|
|
||||||
class StorageGraph : Abstract6Graph<StorageNode, StorageGraph>() {
|
class StorageGraph : GraphNodeList<StorageNode, StorageGraph>() {
|
||||||
private val virtualComponents = Object2ObjectArrayMap<StorageStackType<*>, VirtualComponent<*>>()
|
private val virtualComponents = Object2ObjectArrayMap<StorageStackType<*>, VirtualComponent<*>>()
|
||||||
val powerDemandingNodes = LinkedList<IMatteryEnergyStorage>()
|
val powerDemandingNodes = LinkedList<IMatteryEnergyStorage>()
|
||||||
|
|
||||||
@ -46,11 +39,11 @@ class StorageGraph : Abstract6Graph<StorageNode, StorageGraph>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onNodeAdded(node: StorageNode) {
|
override fun onNodeAdded(node: StorageNode) {
|
||||||
//node.attachComponents(this)
|
node.attachComponents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNodeRemoved(node: StorageNode) {
|
override fun onNodeRemoved(node: StorageNode) {
|
||||||
//node.removeComponents(this)
|
node.removeComponents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,13 @@ package ru.dbotthepony.mc.otm.graph.storage
|
|||||||
import net.minecraft.world.level.block.entity.BlockEntity
|
import net.minecraft.world.level.block.entity.BlockEntity
|
||||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
||||||
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
||||||
import ru.dbotthepony.mc.otm.graph.Graph6Node
|
import ru.dbotthepony.mc.otm.graph.GraphNode
|
||||||
import ru.dbotthepony.mc.otm.storage.IStorage
|
import ru.dbotthepony.mc.otm.storage.IStorage
|
||||||
import ru.dbotthepony.mc.otm.storage.IStorageStack
|
import ru.dbotthepony.mc.otm.storage.IStorageStack
|
||||||
import ru.dbotthepony.mc.otm.storage.StorageStackType
|
import ru.dbotthepony.mc.otm.storage.StorageStackType
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
open class StorageNode(private val energyDemander: IMatteryEnergyStorage? = null) : Graph6Node<StorageNode, StorageGraph>(::StorageGraph) {
|
open class StorageNode(private val energyDemander: IMatteryEnergyStorage? = null) : GraphNode<StorageNode, StorageGraph>(::StorageGraph) {
|
||||||
protected val components = ArrayList<IStorage<*>>()
|
protected val components = ArrayList<IStorage<*>>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -37,7 +37,6 @@ open class StorageNode(private val energyDemander: IMatteryEnergyStorage? = null
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (components != null)
|
|
||||||
for (component in components) {
|
for (component in components) {
|
||||||
to.add(component)
|
to.add(component)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user