Compare commits
10 Commits
cc7fe1ea2f
...
822ab97066
Author | SHA1 | Date | |
---|---|---|---|
822ab97066 | |||
43c02b37a8 | |||
8f9103ca48 | |||
9a6cc53189 | |||
2453c60d76 | |||
a54382b74e | |||
d35da8c7f4 | |||
afb6cd5907 | |||
578d86410b | |||
9e1ae327ea |
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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) },
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user