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 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 {
return listeners.addListener(listener)
}
@ -358,23 +369,21 @@ abstract class MatteryBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state
}
override fun get(): T? {
if (isRemoved)
return null
return cache?.capability
return provider.get()
}
val isPresent: Boolean
get() = cache?.capability != null
get() = get() != null
val isEmpty: Boolean
get() = cache?.capability == null
get() = get() == null
fun rebuildCache() {
if (!SERVER_IS_LIVE) return
val level = level as? ServerLevel
val creationVersion = ++currentVersion
provider = lookupProvider
if (level == null) {
cache = null
@ -388,12 +397,21 @@ abstract class MatteryBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state
{ !isRemoved || creationVersion != currentVersion },
// IllegalStateException("Do not call getCapability on an invalid cache or from the invalidation listener!")
// 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 {
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()
@ -424,6 +442,7 @@ abstract class MatteryBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state
override fun setRemoved() {
super.setRemoved()
capabilityCaches.forEach { it.removeCache() }
unsubscribe()
}

View File

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

View File

@ -74,8 +74,8 @@ private class LinkedPriorityQueue<T : Comparable<T>> {
}
class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableGraph>() {
private val livelyNodes = HashSet<Pair<EnergyCableBlockEntity.Node, RelativeSide>>()
private val livelyNodesList = ArrayList<Pair<EnergyCableBlockEntity.Node, RelativeSide>>()
private val livelyNodes = HashSet<Pair<EnergyCableBlockEntity.Node, EnergyCableBlockEntity.CableSide>>()
private val livelyNodesList = ArrayList<Pair<EnergyCableBlockEntity.Node, EnergyCableBlockEntity.CableSide>>()
fun addLivelyNode(node: EnergyCableBlockEntity.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.CONTAINS -> {
for (dir in RelativeSide.entries) {
val pair = node to dir
val pair = node to node.sides[dir]!!
if (livelyNodes.add(pair)) {
livelyNodesList.add(pair)
@ -589,7 +589,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
override fun onNodeRemoved(node: EnergyCableBlockEntity.Node) {
for (dir in RelativeSide.entries) {
val pair = node to dir
val pair = node to node.sides[dir]!!
if (livelyNodes.remove(pair)) {
check(livelyNodesList.remove(pair))
@ -617,7 +617,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
override fun onNodeAdded(node: EnergyCableBlockEntity.Node) {
for (dir in RelativeSide.entries) {
val pair = node to dir
val pair = node to node.sides[dir]!!
check(livelyNodes.add(pair))
livelyNodesList.add(pair)
}
@ -649,8 +649,7 @@ class EnergyCableGraph : GraphNodeList<EnergyCableBlockEntity.Node, EnergyCableG
continue
try {
val (node, relSide) = pair
val side = node.sides[relSide]!!
val (node, side) = pair
if (!side.isEnabled) {
indicesToRemove.add(i)

View File

@ -198,16 +198,25 @@ class MatterBottlerBlockEntity(blockPos: BlockPos, blockState: BlockState) :
if (matter.storedMatter.isPositive) {
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 (minRatio > Decimal.ZERO) {
if (matterRatio == Decimal.ZERO && matterTransferred > Decimal.ZERO) {
// transferring very little matter, transfer this time for free
isWorking = true
energy.extractEnergy(energyRate * minRatio, false)
matter.extractMatter(capability.receiveMatter(rate * minRatio, false), false)
matter.extractMatter(capability.receiveMatter(matter.storedMatter, false), false)
workProgress = ((capability.storedMatter - initialCapacity!!) / capability.maxStoredMatter).toFloat()
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 {
if (spitItemsWhenCantWork) {
@ -267,16 +276,24 @@ class MatterBottlerBlockEntity(blockPos: BlockPos, blockState: BlockState) :
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 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 (minRatio > Decimal.ZERO) {
if (matterRatio == Decimal.ZERO && matterExtracted > Decimal.ZERO) {
// very little matter extracted, extract this one for free
any = true
energy.extractEnergy(energyRate * energyRatio, false)
matter.receiveMatter(it.extractMatterChecked(rate * minRatio, false), false)
matter.receiveMatter(it.extractMatterChecked(matterExtracted, false), false)
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

View File

@ -1,6 +1,7 @@
package ru.dbotthepony.mc.otm.network
import io.netty.buffer.ByteBuf
import net.minecraft.core.Direction
import net.minecraft.core.UUIDUtil
import net.minecraft.network.FriendlyByteBuf
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 ru.dbotthepony.kommons.math.RGBAColor
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.writeDecimal
import ru.dbotthepony.mc.otm.util.readBlockType
@ -35,6 +37,8 @@ object StreamCodecs {
val RESOURCE_LOCATION = ResourceLocation.STREAM_CODEC.wrap()
val BLOCK_STATE: MatteryStreamCodec<ByteBuf, BlockState> = ByteBufCodecs.idMapper(Block.BLOCK_STATE_REGISTRY).wrap()
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>(
{ 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.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
* 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.
*/
class DynamicSynchableGroup<T : ISynchable>(
class DynamicSynchableGroup<T : Any>(
/**
* Constructs new [T] instance locally, when remote created one.
* Data written by [writer] must be read here, if there is any.
*/
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
* first-time networking of [T] to remote
*/
private val writer: T.(RegistryFriendlyByteBuf) -> Unit = {}
private val writer: T.(RegistryFriendlyByteBuf) -> Unit = {},
) : ISynchable, MutableSet<T> {
constructor(factory: () -> T) : this({ factory() }, {})
private inner class RemoteState(val listener: Runnable) : IRemoteState {
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)
var isRemoved = false
private set
@ -114,7 +124,7 @@ class DynamicSynchableGroup<T : ISynchable>(
firstTime.forEach {
stream.writeByte(ADD_ENTRY)
stream.writeVarInt(it.slot.id)
writer(it.slot.synchable, stream)
writer(it.slot.value, stream)
}
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 value2slot = HashMap<T, Slot<T>>()
private val value2slot = LinkedHashMap<T, Slot<T>>()
private val id2slot = Int2ObjectOpenHashMap<Slot<T>>()
private val idAllocator = IDAllocator()
@ -199,7 +209,7 @@ class DynamicSynchableGroup<T : ISynchable>(
REMOVE_ENTRY -> {
val id = stream.readVarInt()
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) }
}
@ -212,7 +222,7 @@ class DynamicSynchableGroup<T : ISynchable>(
if (id2slot.containsKey(id)) {
val slot = id2slot.remove(id)!!
check(value2slot.remove(slot.synchable) == value2slot)
check(value2slot.remove(slot.value) == value2slot)
remoteStates.forEach { it.remove(slot) }
}
@ -225,7 +235,7 @@ class DynamicSynchableGroup<T : ISynchable>(
SYNC_ENTRY -> {
val id = stream.readVarInt()
val slot = checkNotNull(id2slot.get(id)) { "No such slot with ID: $id" }
slot.synchable.read(stream)
synchable(slot.value).read(stream)
}
CLEAR -> {
@ -299,7 +309,7 @@ class DynamicSynchableGroup<T : ISynchable>(
override fun next(): T {
val slot = parent.next()
last = KOptional(slot)
return slot.synchable
return slot.value
}
override fun remove() {
@ -313,11 +323,7 @@ class DynamicSynchableGroup<T : ISynchable>(
}
override fun remove(element: T): Boolean {
if (element !in value2slot) {
return false
}
val slot = value2slot.remove(element)!!
val slot = value2slot.remove(element) ?: return false
checkNotNull(id2slot.remove(slot.id))
remoteStates.forEach { it.remove(slot) }
idAllocator.release(slot.id)

View File

@ -78,6 +78,17 @@ fun IntArray.shuffle(random: RandomSource): IntArray {
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 {
for (i in lastIndex downTo 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) {
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
}
}
}
}
/**