Compare commits

...

10 Commits

8 changed files with 117 additions and 47 deletions

View File

@ -348,6 +348,17 @@ abstract class MatteryBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state
private var cache: BlockCapabilityCache<T, in Direction?>? = null private var cache: BlockCapabilityCache<T, in Direction?>? = null
private val listeners = Listenable.Impl<T?>() private val listeners = Listenable.Impl<T?>()
private val lookupProvider = Supplier<T?> {
if (isRemoved)
return@Supplier null
val get = cache?.capability
provider = Supplier { get }
return@Supplier get
}
private var provider: Supplier<T?> = lookupProvider
override fun addListener(listener: Consumer<T?>): Listenable.L { override fun addListener(listener: Consumer<T?>): Listenable.L {
return listeners.addListener(listener) return listeners.addListener(listener)
} }
@ -358,23 +369,21 @@ abstract class MatteryBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state
} }
override fun get(): T? { override fun get(): T? {
if (isRemoved) return provider.get()
return null
return cache?.capability
} }
val isPresent: Boolean val isPresent: Boolean
get() = cache?.capability != null get() = get() != null
val isEmpty: Boolean val isEmpty: Boolean
get() = cache?.capability == null get() = get() == null
fun rebuildCache() { fun rebuildCache() {
if (!SERVER_IS_LIVE) return if (!SERVER_IS_LIVE) return
val level = level as? ServerLevel val level = level as? ServerLevel
val creationVersion = ++currentVersion val creationVersion = ++currentVersion
provider = lookupProvider
if (level == null) { if (level == null) {
cache = null cache = null
@ -388,12 +397,21 @@ abstract class MatteryBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state
{ !isRemoved || creationVersion != currentVersion }, { !isRemoved || creationVersion != currentVersion },
// IllegalStateException("Do not call getCapability on an invalid cache or from the invalidation listener!") // IllegalStateException("Do not call getCapability on an invalid cache or from the invalidation listener!")
// what a shame. // what a shame.
{ if (SERVER_IS_LIVE) onceServer { if (!isRemoved && creationVersion == currentVersion) listeners.accept(cache?.capability) } }) {
provider = lookupProvider
if (SERVER_IS_LIVE) onceServer { if (!isRemoved && creationVersion == currentVersion) listeners.accept(get()) }
})
onceServer { onceServer {
if (!isRemoved && creationVersion == currentVersion) listeners.accept(cache?.capability) if (!isRemoved && creationVersion == currentVersion) listeners.accept(get())
} }
} }
fun removeCache() {
cache = null
currentVersion++
provider = Supplier { null }
}
} }
val syncher = SynchableGroup() val syncher = SynchableGroup()
@ -424,6 +442,7 @@ abstract class MatteryBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state
override fun setRemoved() { override fun setRemoved() {
super.setRemoved() super.setRemoved()
capabilityCaches.forEach { it.removeCache() }
unsubscribe() unsubscribe()
} }

View File

@ -115,7 +115,7 @@ abstract class EnergyCableBlockEntity(type: BlockEntityType<*>, blockPos: BlockP
val sides get() = energySides val sides get() = energySides
val currentlyTransferringTo = ObjectArraySet<Pair<Node, RelativeSide>>() val currentlyTransferringTo = ObjectArraySet<Pair<Node, CableSide>>()
override fun onNeighbour(link: Link) { override fun onNeighbour(link: Link) {
if (link is DirectionLink) { if (link is DirectionLink) {

View File

@ -74,8 +74,8 @@ private class LinkedPriorityQueue<T : Comparable<T>> {
} }
class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableGraph>() { class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableGraph>() {
private val livelyNodes = HashSet<Pair<EnergyCableBlockEntity.Node, RelativeSide>>() private val livelyNodes = HashSet<Pair<EnergyCableBlockEntity.Node, EnergyCableBlockEntity.CableSide>>()
private val livelyNodesList = ArrayList<Pair<EnergyCableBlockEntity.Node, RelativeSide>>() private val livelyNodesList = ArrayList<Pair<EnergyCableBlockEntity.Node, EnergyCableBlockEntity.CableSide>>()
fun addLivelyNode(node: EnergyCableBlockEntity.Node) { fun addLivelyNode(node: EnergyCableBlockEntity.Node) {
when (contains(node)) { when (contains(node)) {
@ -83,7 +83,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
ContainsStatus.DOES_NOT_BELONG -> throw IllegalArgumentException("$node does not belong to $this") ContainsStatus.DOES_NOT_BELONG -> throw IllegalArgumentException("$node does not belong to $this")
ContainsStatus.CONTAINS -> { ContainsStatus.CONTAINS -> {
for (dir in RelativeSide.entries) { for (dir in RelativeSide.entries) {
val pair = node to dir val pair = node to node.sides[dir]!!
if (livelyNodes.add(pair)) { if (livelyNodes.add(pair)) {
livelyNodesList.add(pair) livelyNodesList.add(pair)
@ -589,7 +589,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
override fun onNodeRemoved(node: EnergyCableBlockEntity.Node) { override fun onNodeRemoved(node: EnergyCableBlockEntity.Node) {
for (dir in RelativeSide.entries) { for (dir in RelativeSide.entries) {
val pair = node to dir val pair = node to node.sides[dir]!!
if (livelyNodes.remove(pair)) { if (livelyNodes.remove(pair)) {
check(livelyNodesList.remove(pair)) check(livelyNodesList.remove(pair))
@ -617,7 +617,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
override fun onNodeAdded(node: EnergyCableBlockEntity.Node) { override fun onNodeAdded(node: EnergyCableBlockEntity.Node) {
for (dir in RelativeSide.entries) { for (dir in RelativeSide.entries) {
val pair = node to dir val pair = node to node.sides[dir]!!
check(livelyNodes.add(pair)) check(livelyNodes.add(pair))
livelyNodesList.add(pair) livelyNodesList.add(pair)
} }
@ -649,8 +649,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
continue continue
try { try {
val (node, relSide) = pair val (node, side) = pair
val side = node.sides[relSide]!!
if (!side.isEnabled) { if (!side.isEnabled) {
indicesToRemove.add(i) indicesToRemove.add(i)

View File

@ -198,16 +198,25 @@ class MatterBottlerBlockEntity(blockPos: BlockPos, blockState: BlockState) :
if (matter.storedMatter.isPositive) { if (matter.storedMatter.isPositive) {
val energyRatio = if (energyRate <= Decimal.ZERO) Decimal.ONE else energy.extractEnergy(energyRate, true) / energyRate val energyRatio = if (energyRate <= Decimal.ZERO) Decimal.ONE else energy.extractEnergy(energyRate, true) / energyRate
val matterRatio = matter.extractMatter(capability.receiveMatter(rate.coerceAtMost(matter.storedMatter), true), true) / rate val matterTransferred = matter.extractMatter(capability.receiveMatter(rate.coerceAtMost(matter.storedMatter), true), true)
val matterRatio = matterTransferred / rate
val minRatio = minOf(matterRatio, energyRatio) if (matterRatio == Decimal.ZERO && matterTransferred > Decimal.ZERO) {
// transferring very little matter, transfer this time for free
if (minRatio > Decimal.ZERO) {
isWorking = true isWorking = true
energy.extractEnergy(energyRate * minRatio, false) matter.extractMatter(capability.receiveMatter(matter.storedMatter, false), false)
matter.extractMatter(capability.receiveMatter(rate * minRatio, false), false)
workProgress = ((capability.storedMatter - initialCapacity!!) / capability.maxStoredMatter).toFloat() workProgress = ((capability.storedMatter - initialCapacity!!) / capability.maxStoredMatter).toFloat()
slot.setChanged() slot.setChanged()
} else {
val minRatio = minOf(matterRatio, energyRatio)
if (minRatio > Decimal.ZERO) {
isWorking = true
energy.extractEnergy(energyRate * minRatio, false)
matter.extractMatter(capability.receiveMatter(rate * minRatio, false), false)
workProgress = ((capability.storedMatter - initialCapacity!!) / capability.maxStoredMatter).toFloat()
slot.setChanged()
}
} }
} else { } else {
if (spitItemsWhenCantWork) { if (spitItemsWhenCantWork) {
@ -267,16 +276,24 @@ class MatterBottlerBlockEntity(blockPos: BlockPos, blockState: BlockState) :
val energyRate = MachinesConfig.MatterBottler.VALUES.energyConsumption * (Decimal.ONE + upgrades.speedBonus.toDecimal()) val energyRate = MachinesConfig.MatterBottler.VALUES.energyConsumption * (Decimal.ONE + upgrades.speedBonus.toDecimal())
val energyRatio = if (energyRate <= Decimal.ZERO) Decimal.ONE else energy.extractEnergy(energyRate, true) / energyRate val energyRatio = if (energyRate <= Decimal.ZERO) Decimal.ONE else energy.extractEnergy(energyRate, true) / energyRate
val matterRatio = matter.receiveMatter(it.extractMatterChecked(rate, true), true) / rate val matterExtracted = matter.receiveMatter(it.extractMatterChecked(rate, true), true)
val matterRatio = matterExtracted / rate
val minRatio = minOf(energyRatio, matterRatio) if (matterRatio == Decimal.ZERO && matterExtracted > Decimal.ZERO) {
// very little matter extracted, extract this one for free
if (minRatio > Decimal.ZERO) {
any = true any = true
energy.extractEnergy(energyRate * energyRatio, false) matter.receiveMatter(it.extractMatterChecked(matterExtracted, false), false)
matter.receiveMatter(it.extractMatterChecked(rate * minRatio, false), false)
workProgress = 1f - (it.storedMatter / initialCapacity!!).toFloat() workProgress = 1f - (it.storedMatter / initialCapacity!!).toFloat()
} else {
val minRatio = minOf(energyRatio, matterRatio)
if (minRatio > Decimal.ZERO) {
any = true
energy.extractEnergy(energyRate * energyRatio, false)
matter.receiveMatter(it.extractMatterChecked(rate * minRatio, false), false)
workProgress = 1f - (it.storedMatter / initialCapacity!!).toFloat()
}
} }
break break

View File

@ -1,6 +1,7 @@
package ru.dbotthepony.mc.otm.network package ru.dbotthepony.mc.otm.network
import io.netty.buffer.ByteBuf import io.netty.buffer.ByteBuf
import net.minecraft.core.Direction
import net.minecraft.core.UUIDUtil import net.minecraft.core.UUIDUtil
import net.minecraft.network.FriendlyByteBuf import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.codec.ByteBufCodecs import net.minecraft.network.codec.ByteBufCodecs
@ -10,6 +11,7 @@ import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import ru.dbotthepony.kommons.math.RGBAColor import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.mc.otm.container.ItemFilter import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.util.math.RelativeSide
import ru.dbotthepony.mc.otm.util.readDecimal import ru.dbotthepony.mc.otm.util.readDecimal
import ru.dbotthepony.mc.otm.util.writeDecimal import ru.dbotthepony.mc.otm.util.writeDecimal
import ru.dbotthepony.mc.otm.util.readBlockType import ru.dbotthepony.mc.otm.util.readBlockType
@ -35,6 +37,8 @@ object StreamCodecs {
val RESOURCE_LOCATION = ResourceLocation.STREAM_CODEC.wrap() val RESOURCE_LOCATION = ResourceLocation.STREAM_CODEC.wrap()
val BLOCK_STATE: MatteryStreamCodec<ByteBuf, BlockState> = ByteBufCodecs.idMapper(Block.BLOCK_STATE_REGISTRY).wrap() val BLOCK_STATE: MatteryStreamCodec<ByteBuf, BlockState> = ByteBufCodecs.idMapper(Block.BLOCK_STATE_REGISTRY).wrap()
val BLOCK_TYPE = MatteryStreamCodec.Of(FriendlyByteBuf::writeBlockType, FriendlyByteBuf::readBlockType) val BLOCK_TYPE = MatteryStreamCodec.Of(FriendlyByteBuf::writeBlockType, FriendlyByteBuf::readBlockType)
val RELATIVE_SIDE = MatteryStreamCodec.Enum<FriendlyByteBuf, RelativeSide>(RelativeSide::class.java)
val DIRECTION = MatteryStreamCodec.Enum<FriendlyByteBuf, Direction>(Direction::class.java)
val RGBA: MatteryStreamCodec<ByteBuf, RGBAColor> = StreamCodec.of<ByteBuf, RGBAColor>( val RGBA: MatteryStreamCodec<ByteBuf, RGBAColor> = StreamCodec.of<ByteBuf, RGBAColor>(
{ s, v -> s.writeFloat(v.red); s.writeFloat(v.green); s.writeFloat(v.blue); s.writeFloat(v.alpha) }, { s, v -> s.writeFloat(v.red); s.writeFloat(v.green); s.writeFloat(v.blue); s.writeFloat(v.alpha) },

View File

@ -10,29 +10,39 @@ import java.io.Closeable
import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
/**
* Constructs a new [DynamicSynchableGroup] where [T] is itself an [ISynchable]
*/
fun <T : ISynchable> DynamicSynchableGroup(reader: RegistryFriendlyByteBuf.() -> T, writer: T.(RegistryFriendlyByteBuf) -> Unit): DynamicSynchableGroup<T> {
return DynamicSynchableGroup(reader, { this }, writer)
}
/** /**
* Syncher group/set, which deals with synchables of only one type, and which are created and removed * Syncher group/set, which deals with synchables of only one type, and which are created and removed
* on remote (e.g. server adding and removing synchables at will), which makes it distinct from * on remote (e.g. server adding and removing synchables at will), which makes it distinct from
* [SynchableGroup], in which attached synchables are created/removed manually on both sides. * [SynchableGroup], in which attached synchables are created/removed manually on both sides.
*/ */
class DynamicSynchableGroup<T : ISynchable>( class DynamicSynchableGroup<T : Any>(
/** /**
* Constructs new [T] instance locally, when remote created one. * Constructs new [T] instance locally, when remote created one.
* Data written by [writer] must be read here, if there is any. * Data written by [writer] must be read here, if there is any.
*/ */
private val reader: RegistryFriendlyByteBuf.() -> T, private val reader: RegistryFriendlyByteBuf.() -> T,
/**
* Gets an [ISynchable] out of value [T] for synching state
*/
private val synchable: T.() -> ISynchable,
/** /**
* Allows to write additional data to network stream during * Allows to write additional data to network stream during
* first-time networking of [T] to remote * first-time networking of [T] to remote
*/ */
private val writer: T.(RegistryFriendlyByteBuf) -> Unit = {} private val writer: T.(RegistryFriendlyByteBuf) -> Unit = {},
) : ISynchable, MutableSet<T> { ) : ISynchable, MutableSet<T> {
constructor(factory: () -> T) : this({ factory() }, {})
private inner class RemoteState(val listener: Runnable) : IRemoteState { private inner class RemoteState(val listener: Runnable) : IRemoteState {
private inner class RemoteSlot(val slot: Slot<T>, fromConstructor: Boolean) : Runnable, Closeable { private inner class RemoteSlot(val slot: Slot<T>, fromConstructor: Boolean) : Runnable, Closeable {
val remoteState = slot.synchable.createRemoteState(this) val remoteState = synchable(slot.value).createRemoteState(this)
val isDirty = AtomicBoolean(true) val isDirty = AtomicBoolean(true)
var isRemoved = false var isRemoved = false
private set private set
@ -114,7 +124,7 @@ class DynamicSynchableGroup<T : ISynchable>(
firstTime.forEach { firstTime.forEach {
stream.writeByte(ADD_ENTRY) stream.writeByte(ADD_ENTRY)
stream.writeVarInt(it.slot.id) stream.writeVarInt(it.slot.id)
writer(it.slot.synchable, stream) writer(it.slot.value, stream)
} }
firstTime.clear() firstTime.clear()
@ -177,10 +187,10 @@ class DynamicSynchableGroup<T : ISynchable>(
} }
} }
private data class Slot<T : Any>(val synchable: T, val id: Int) private data class Slot<T : Any>(val value: T, val id: Int)
private val remoteStates = ArrayList<RemoteState>() private val remoteStates = ArrayList<RemoteState>()
private val value2slot = HashMap<T, Slot<T>>() private val value2slot = LinkedHashMap<T, Slot<T>>()
private val id2slot = Int2ObjectOpenHashMap<Slot<T>>() private val id2slot = Int2ObjectOpenHashMap<Slot<T>>()
private val idAllocator = IDAllocator() private val idAllocator = IDAllocator()
@ -199,7 +209,7 @@ class DynamicSynchableGroup<T : ISynchable>(
REMOVE_ENTRY -> { REMOVE_ENTRY -> {
val id = stream.readVarInt() val id = stream.readVarInt()
val slot = checkNotNull(id2slot.remove(id)) { "No such slot with ID: $id" } val slot = checkNotNull(id2slot.remove(id)) { "No such slot with ID: $id" }
check(value2slot.remove(slot.synchable) == value2slot) check(value2slot.remove(slot.value) == value2slot)
remoteStates.forEach { it.remove(slot) } remoteStates.forEach { it.remove(slot) }
} }
@ -212,7 +222,7 @@ class DynamicSynchableGroup<T : ISynchable>(
if (id2slot.containsKey(id)) { if (id2slot.containsKey(id)) {
val slot = id2slot.remove(id)!! val slot = id2slot.remove(id)!!
check(value2slot.remove(slot.synchable) == value2slot) check(value2slot.remove(slot.value) == value2slot)
remoteStates.forEach { it.remove(slot) } remoteStates.forEach { it.remove(slot) }
} }
@ -225,7 +235,7 @@ class DynamicSynchableGroup<T : ISynchable>(
SYNC_ENTRY -> { SYNC_ENTRY -> {
val id = stream.readVarInt() val id = stream.readVarInt()
val slot = checkNotNull(id2slot.get(id)) { "No such slot with ID: $id" } val slot = checkNotNull(id2slot.get(id)) { "No such slot with ID: $id" }
slot.synchable.read(stream) synchable(slot.value).read(stream)
} }
CLEAR -> { CLEAR -> {
@ -299,7 +309,7 @@ class DynamicSynchableGroup<T : ISynchable>(
override fun next(): T { override fun next(): T {
val slot = parent.next() val slot = parent.next()
last = KOptional(slot) last = KOptional(slot)
return slot.synchable return slot.value
} }
override fun remove() { override fun remove() {
@ -313,11 +323,7 @@ class DynamicSynchableGroup<T : ISynchable>(
} }
override fun remove(element: T): Boolean { override fun remove(element: T): Boolean {
if (element !in value2slot) { val slot = value2slot.remove(element) ?: return false
return false
}
val slot = value2slot.remove(element)!!
checkNotNull(id2slot.remove(slot.id)) checkNotNull(id2slot.remove(slot.id))
remoteStates.forEach { it.remove(slot) } remoteStates.forEach { it.remove(slot) }
idAllocator.release(slot.id) idAllocator.release(slot.id)

View File

@ -78,6 +78,17 @@ fun IntArray.shuffle(random: RandomSource): IntArray {
return this return this
} }
fun <T> Array<T>.shuffle(random: RandomSource): Array<T> {
for (i in lastIndex downTo 1) {
val j = random.nextInt(i + 1)
val copy = this[i]
this[i] = this[j]
this[j] = copy
}
return this
}
fun <L : IntList> L.shuffle(random: RandomSource): L { fun <L : IntList> L.shuffle(random: RandomSource): L {
for (i in lastIndex downTo 1) { for (i in lastIndex downTo 1) {
val j = random.nextInt(i + 1) val j = random.nextInt(i + 1)

View File

@ -20,7 +20,21 @@ operator fun BlockPos.plus(other: BlockRotation): BlockPos {
} }
enum class RelativeSide(val default: Direction) { enum class RelativeSide(val default: Direction) {
FRONT(Direction.NORTH), BACK(Direction.SOUTH), LEFT(Direction.WEST), RIGHT(Direction.EAST), TOP(Direction.UP), BOTTOM(Direction.DOWN) FRONT(Direction.NORTH), BACK(Direction.SOUTH), LEFT(Direction.WEST), RIGHT(Direction.EAST), TOP(Direction.UP), BOTTOM(Direction.DOWN);
companion object {
@JvmStatic
fun defaultOf(direction: Direction): RelativeSide {
return when (direction) {
Direction.DOWN -> BOTTOM
Direction.UP -> TOP
Direction.NORTH -> FRONT
Direction.SOUTH -> BACK
Direction.WEST -> LEFT
Direction.EAST -> RIGHT
}
}
}
} }
/** /**