Update matter interface to match energy one, add profiled matter storage

Add weighted average to profiled storage to make "transferred last second" display less stiff
This commit is contained in:
DBotThePony 2023-06-10 20:18:21 +07:00
parent 0db20bc389
commit 87e2c8d1d8
Signed by: DBot
GPG Key ID: DCC23B5715498507
34 changed files with 495 additions and 306 deletions

View File

@ -15,6 +15,7 @@ import net.minecraftforge.event.server.ServerStoppedEvent
import net.minecraftforge.event.server.ServerStoppingEvent
import net.minecraftforge.fml.loading.FMLLoader
import org.apache.logging.log4j.LogManager
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.core.util.IConditionalTickable
@ -149,7 +150,7 @@ fun onServerTick(event: ServerTickEvent) {
postServerTick.tick()
// чтоб не плодить кучу подписчиков, вызовем напрямую отсюда
Abstract6Graph.tick()
ProfiledEnergyStorage.onServerPostTick()
AbstractProfiledStorage.onServerPostTick()
}
}

View File

@ -21,6 +21,7 @@ import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.IMatterStorage
import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.graph.matter.MatterGraph
@ -99,11 +100,11 @@ class MatterBottlerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
}
})
val matter: MatterStorageImpl = object : MatterStorageImpl(this::setChangedLight, FlowDirection.BI_DIRECTIONAL, ::CAPACITY) {
val matter: ProfiledMatterStorage<MatterStorageImpl> = ProfiledMatterStorage(object : MatterStorageImpl(this::setChangedLight, FlowDirection.BI_DIRECTIONAL, ::CAPACITY) {
override val matterFlow: FlowDirection get() {
return if (this@MatterBottlerBlockEntity.isBottling) FlowDirection.INPUT else FlowDirection.OUTPUT
}
}
})
val matterNode = SimpleMatterNode(matter = matter)
@ -232,7 +233,7 @@ class MatterBottlerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
energy.extractEnergy(ENERGY_CONSUMPTION * matter / MATTER_EXCHANGE_RATE, false)
capability.receiveMatter(matter, false)
this.matter.extractMatterInner(matter, false)
this.matter.extractMatter(matter, false)
if (capability.missingMatter.isZero) {
for (i in 3..5) {
@ -256,7 +257,7 @@ class MatterBottlerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
this.energy.extractEnergy(ENERGY_CONSUMPTION * matter / MATTER_EXCHANGE_RATE,false)
capability.extractMatter(matter, false)
this.matter.receiveMatterInner(matter, false)
this.matter.receiveMatter(matter, false)
if (capability.storedMatter.isZero) {
for (i in 2 downTo 0) {
@ -275,9 +276,9 @@ class MatterBottlerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
}
if (!isBottling && !matter.storedMatter.isZero && graph != null) {
val diff = matter.extractMatterInner(matter.storedMatter, true)
val diff = matter.extractMatter(matter.storedMatter, true)
val diff2 = graph.receiveMatter(diff, true)
matter.extractMatterInner(diff2, false)
matter.extractMatter(diff2, false)
graph.receiveMatter(diff2, false)
}
}

View File

@ -70,7 +70,7 @@ class MatterCapacitorBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState)
for (stack in container) {
if (!stack.isEmpty) {
stack.getCapability(MatteryCapability.MATTER).ifPresent {
val diff = it.receiveMatter(howMuch, simulate)
val diff = it.receiveMatterChecked(howMuch, simulate)
summ += diff
howMuch -= diff
}
@ -99,7 +99,7 @@ class MatterCapacitorBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState)
for (stack in container) {
if (!stack.isEmpty) {
stack.getCapability(MatteryCapability.MATTER).ifPresent {
val diff = it.extractMatter(howMuch, simulate)
val diff = it.extractMatterChecked(howMuch, simulate)
summ += diff
howMuch -= diff
}

View File

@ -16,6 +16,7 @@ import ru.dbotthepony.mc.otm.capability.energy.BlockEnergyStorageImpl
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
import ru.dbotthepony.mc.otm.config.ConciseBalanceValues
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
@ -125,7 +126,7 @@ class MatterDecomposerBlockEntity(pos: BlockPos, state: BlockState)
savetables.stateful(::energy, ENERGY_KEY)
}
val matter = MatterStorageImpl(::setChangedLight, FlowDirection.OUTPUT, ::CAPACITY)
val matter = ProfiledMatterStorage(MatterStorageImpl(::setChangedLight, FlowDirection.OUTPUT, ::CAPACITY))
val matterNode = SimpleMatterNode(matter = matter)
init {
@ -172,7 +173,7 @@ class MatterDecomposerBlockEntity(pos: BlockPos, state: BlockState)
return Status.SUCCESS
}
job.matterValue -= matter.receiveMatterInner(job.matterValue, false)
job.matterValue -= matter.receiveMatter(job.matterValue, false)
if (job.matterValue.isPositive) {
return Status.FAILURE_MATTER
@ -215,10 +216,10 @@ class MatterDecomposerBlockEntity(pos: BlockPos, state: BlockState)
val grid = matterNode.graph as MatterGraph? ?: return
if (!matter.storedMatter.isZero) {
val diff = matter.extractMatterInner(matter.storedMatter, true)
val diff = matter.extractMatter(matter.storedMatter, true)
val diff2 = grid.receiveMatter(diff, true)
matter.extractMatterInner(diff2, false)
matter.extractMatter(diff2, false)
grid.receiveMatter(diff2, false)
}
}

View File

@ -22,6 +22,7 @@ import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.IMatterStorage
import ru.dbotthepony.mc.otm.capability.matter.IPatternState
import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
import ru.dbotthepony.mc.otm.config.ConciseBalanceValues
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
@ -54,7 +55,7 @@ class MatterReconstructorBlockEntity(blockPos: BlockPos, blockState: BlockState)
var isUnableToProcess = false
private set
val matter = MatterStorageImpl(::setChangedLight, FlowDirection.INPUT, ::CAPACITY)
val matter = ProfiledMatterStorage(MatterStorageImpl(::setChangedLight, FlowDirection.INPUT, ::CAPACITY))
val energy = ProfiledEnergyStorage(WorkerEnergyStorage(::setChangedLight, ENERGY_VALUES))
val matterNode = object : MatterNode() {
@ -232,12 +233,12 @@ class MatterReconstructorBlockEntity(blockPos: BlockPos, blockState: BlockState)
if (graph != null) {
val toDrain = (matterPerTick * EXTRACT_TICKS.coerceAtMost(item.damageValue) - matter.storedMatter).coerceAtLeast(Decimal.ZERO).coerceAtMost(matter.missingMatter)
matter.receiveMatterInner(graph.extractMatter(toDrain, false), false)
matter.receiveMatter(graph.extractMatter(toDrain, false), false)
}
}
val toDrain = matterPerTick * (progressPerTick / this.progressPerTick)
val drain = matter.extractMatterInner(toDrain, true)
val drain = matter.extractMatter(toDrain, true)
if (!drain.isPositive) {
isUnableToProcess = true
return
@ -253,7 +254,7 @@ class MatterReconstructorBlockEntity(blockPos: BlockPos, blockState: BlockState)
repairProgress += progressPerTick
energy.extractEnergy(ENERGY_CONSUMPTION * (progressPerTick / this.progressPerTick), false)
matter.extractMatterInner(matterPerTick * (progressPerTick / this.progressPerTick), false)
matter.extractMatter(matterPerTick * (progressPerTick / this.progressPerTick), false)
if (repairProgress >= 1.0) {
item.damageValue = (item.damageValue - repairProgress.toInt()).coerceAtLeast(0)

View File

@ -18,6 +18,7 @@ import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.core.math.Decimal
@ -62,7 +63,7 @@ class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState)
}
}
val matter = MatterStorageImpl(::matterLevelUpdated, FlowDirection.OUTPUT, ::CAPACITY)
val matter = ProfiledMatterStorage(MatterStorageImpl(::matterLevelUpdated, FlowDirection.OUTPUT, ::CAPACITY))
val container = MatteryContainer(::itemContainerUpdated, 1).also(::addDroppableContainer)
val matterNode = SimpleMatterNode(matter = matter)
@ -130,12 +131,12 @@ class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState)
if (receive.isZero)
return Status.SUCCESS
val received = matter.receiveMatterInner(receive, true)
val received = matter.receiveMatter(receive, true)
if (receive != received)
return Status.FAILURE_MATTER
matter.receiveMatterInner(receive, false)
matter.receiveMatter(receive, false)
job.totalMatter -= received
return Status.SUCCESS
}
@ -147,7 +148,7 @@ class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState)
val received = graph.receiveMatter(matter.storedMatter, false)
if (!received.isZero) {
matter.extractMatterInner(received, false)
matter.extractMatter(received, false)
}
}

View File

@ -20,6 +20,7 @@ import ru.dbotthepony.mc.otm.capability.matter.IPatternState
import ru.dbotthepony.mc.otm.capability.matter.IReplicationTask
import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl
import ru.dbotthepony.mc.otm.capability.matter.PatternState
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
import ru.dbotthepony.mc.otm.capability.matter.ReplicationTask
import ru.dbotthepony.mc.otm.config.ConciseBalanceValues
import ru.dbotthepony.mc.otm.container.HandlerFilter
@ -101,7 +102,7 @@ class MatterReplicatorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
}
val energy = ProfiledEnergyStorage(WorkerEnergyStorage(::powerLevelUpdated, ENERGY_VALUES))
val matter = MatterStorageImpl(::matterLevelUpdated, FlowDirection.INPUT, ::MATTER_CAPACITY)
val matter = ProfiledMatterStorage(MatterStorageImpl(::matterLevelUpdated, FlowDirection.INPUT, ::MATTER_CAPACITY))
val container = MatteryContainer(::itemContainerUpdated, 5).also(::addDroppableContainer)
val energyConfig = ConfigurableEnergy(energy)
@ -217,12 +218,12 @@ class MatterReplicatorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
override fun onWorkTick(requiredPower: Decimal, extractedPower: Decimal, ticksAdvanced: Double, job: ReplicatorJob): Status {
val drainPerTick = job.matterPerTick * ticksAdvanced
if (matter.extractMatterInner(drainPerTick, true) < drainPerTick) {
if (matter.extractMatter(drainPerTick, true) < drainPerTick) {
// в машине недостаточно материи
if (drainPerTick > matter.maxStoredMatter) {
// в тик требуется больше материи, чем её может хранить репликатор
val toExtract = drainPerTick - matter.extractMatterInner(drainPerTick, true)
val toExtract = drainPerTick - matter.extractMatter(drainPerTick, true)
val drain = matterNode.graph.extractMatter(toExtract, true)
if (drain != toExtract) {
@ -231,7 +232,7 @@ class MatterReplicatorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
}
// достаточно материи в сети + внутри машины
matter.extractMatterInner(drainPerTick, false)
matter.extractMatter(drainPerTick, false)
matterNode.graph.extractMatter(drain, false)
return Status.SUCCESS
} else {
@ -250,8 +251,8 @@ class MatterReplicatorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
matter.receiveMatter(drain, false)
// получили материю, проверяем возможность работы
if (matter.extractMatterInner(drainPerTick, true) >= drainPerTick) {
matter.extractMatterInner(drainPerTick, false)
if (matter.extractMatter(drainPerTick, true) >= drainPerTick) {
matter.extractMatter(drainPerTick, false)
return Status.SUCCESS
} else {
// :(
@ -261,7 +262,7 @@ class MatterReplicatorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
}
// в машине достаточно материи
matter.extractMatterInner(drainPerTick, false)
matter.extractMatter(drainPerTick, false)
visualProgress = workProgress
return Status.SUCCESS
}

View File

@ -0,0 +1,206 @@
package ru.dbotthepony.mc.otm.capability
import com.google.common.collect.ImmutableList
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.nbt.NumericTag
import net.minecraftforge.common.util.INBTSerializable
import ru.dbotthepony.mc.otm.core.forValidRefs
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.set
import java.lang.ref.WeakReference
import java.util.*
import kotlin.collections.ArrayList
abstract class AbstractProfiledStorage<out P>(val parent: P) : INBTSerializable<CompoundTag?> {
init {
storages.add(WeakReference(this))
}
private val historyReceiveInternal = ArrayList<Decimal>(HISTORY_SIZE)
private val historyTransferInternal = ArrayList<Decimal>(HISTORY_SIZE)
val historyReceive: List<Decimal> = Collections.unmodifiableList(historyReceiveInternal)
val historyTransfer: List<Decimal> = Collections.unmodifiableList(historyTransferInternal)
val lastTickReceive
get() = historyReceiveInternal[tick]
val lastTickTransfer
get() = historyTransferInternal[tick]
private var thisTickReceive = Decimal.ZERO
private var thisTickTransfer = Decimal.ZERO
var tick = 0
private set
init {
for (i in 0 until HISTORY_SIZE) {
historyReceiveInternal.add(Decimal.ZERO)
historyTransferInternal.add(Decimal.ZERO)
}
}
protected fun recordTransfer(value: Decimal, simulate: Boolean): Decimal {
if (!simulate) {
thisTickTransfer += value
}
return value
}
protected fun recordReceive(value: Decimal, simulate: Boolean): Decimal {
if (!simulate) {
thisTickReceive += value
}
return value
}
private fun tick() {
tick = (tick + 1) % HISTORY_SIZE
historyReceiveInternal[tick] = thisTickReceive
historyTransferInternal[tick] = thisTickTransfer
thisTickReceive = Decimal.ZERO
thisTickTransfer = Decimal.ZERO
}
val savedata: INBTSerializable<CompoundTag?> = object : INBTSerializable<CompoundTag?> {
override fun serializeNBT(): CompoundTag {
return CompoundTag().also { tag ->
tag["historyReceive"] = ListTag().also {
for (value in historyReceiveInternal) {
it.add(value.serializeNBT())
}
}
tag["historyTransfer"] = ListTag().also {
for (value in historyTransferInternal) {
it.add(value.serializeNBT())
}
}
tag["historyTick"] = tick
}
}
override fun deserializeNBT(nbt: CompoundTag?) {
tick = 0
thisTickReceive = Decimal.ZERO
thisTickTransfer = Decimal.ZERO
for (i in 0 until HISTORY_SIZE) {
historyReceiveInternal[i] = Decimal.ZERO
historyTransferInternal[i] = Decimal.ZERO
}
nbt ?: return
nbt.map("historyTick") { it: NumericTag ->
tick = it.asInt
}
nbt.map("historyReceive") { it: ListTag ->
for (i in 0 until HISTORY_SIZE.coerceAtMost(it.size)) {
historyReceiveInternal[i] = Decimal.deserializeNBT(it[i])
}
}
nbt.map("historyTransfer") { it: ListTag ->
for (i in 0 until HISTORY_SIZE.coerceAtMost(it.size)) {
historyTransferInternal[i] = Decimal.deserializeNBT(it[i])
}
}
}
}
override fun serializeNBT(): CompoundTag {
val tag: CompoundTag
if (parent is INBTSerializable<*>) {
tag = (parent as INBTSerializable<CompoundTag?>).serializeNBT() ?: CompoundTag()
} else {
tag = CompoundTag()
}
val tag2 = savedata.serializeNBT()!!
for (k in tag2.allKeys) {
tag[k] = tag2[k]!!
}
return tag
}
val weightedTransfer: Decimal get() {
return calcWeightedAverage(historyTransfer, tick)
}
val weightedReceive: Decimal get() {
return calcWeightedAverage(historyReceive, tick)
}
override fun deserializeNBT(nbt: CompoundTag?) {
if (parent is INBTSerializable<*>) {
(parent as INBTSerializable<CompoundTag?>).deserializeNBT(nbt)
}
savedata.deserializeNBT(nbt)
}
companion object {
const val HISTORY_SIZE = 20
private val storages = ArrayList<WeakReference<AbstractProfiledStorage<*>>>()
val HISTORY_WEIGHTERS: ImmutableList<Decimal> = ImmutableList.of(
Decimal("0.313335967"),
Decimal("0.146176397"),
Decimal("0.09357867"),
Decimal("0.068193701"),
Decimal("0.053351084"),
Decimal("0.043655993"),
Decimal("0.036847023"),
Decimal("0.031813486"),
Decimal("0.027947534"),
Decimal("0.024889161"),
Decimal("0.02241188"),
Decimal("0.020366241"),
Decimal("0.018649731"),
Decimal("0.017189744"),
Decimal("0.015933452"),
Decimal("0.014841516"),
Decimal("0.013884059"),
Decimal("0.013037985"),
Decimal("0.012285173"),
Decimal("0.011611204"),
)
fun calcWeightedAverage(inputs: List<Decimal>, pointer: Int): Decimal {
require(inputs.size == HISTORY_WEIGHTERS.size) { "Expected list of size ${HISTORY_WEIGHTERS.size}, got ${inputs.size}" }
require(pointer in 0 until HISTORY_WEIGHTERS.size) { "Invalid pointer position: $pointer" }
var result = Decimal.ZERO
var i = 0
for (i2 in pointer downTo 0)
result += inputs[i2] * HISTORY_WEIGHTERS[i++]
for (i2 in HISTORY_WEIGHTERS.size - 1 downTo pointer + 1)
result += inputs[i2] * HISTORY_WEIGHTERS[i++]
return result
}
// после раздумий, наиболее корректное место для обновления состояния профилированных
// хранилищ энергии и материи будет после всех остальных хуков на тики после тика сервера
// разумеется, такое решение может несколько снизить производительность сервера,
// но ничего страшного произойти не должно
internal fun onServerPostTick() {
storages.forValidRefs { it.tick() }
}
}
}

View File

@ -1,25 +1,10 @@
package ru.dbotthepony.mc.otm.capability.energy
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.nbt.NumericTag
import net.minecraft.nbt.Tag
import net.minecraftforge.common.util.INBTSerializable
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage
import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.core.forValidRefs
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.set
import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.set
import java.lang.ref.WeakReference
import java.util.*
import kotlin.collections.ArrayList
class ProfiledEnergyStorage<E : IMatteryEnergyStorage>(val parent: E) : IMatteryEnergyStorage, INBTSerializable<CompoundTag?> {
init {
storages.add(WeakReference(this))
}
class ProfiledEnergyStorage<out E : IMatteryEnergyStorage>(parent: E) : AbstractProfiledStorage<E>(parent), IMatteryEnergyStorage {
override fun extractEnergy(howMuch: Decimal, simulate: Boolean): Decimal {
return recordTransfer(parent.extractEnergy(howMuch, simulate), simulate)
}
@ -53,143 +38,4 @@ class ProfiledEnergyStorage<E : IMatteryEnergyStorage>(val parent: E) : IMattery
override val maxBatteryLevel: Decimal get() = parent.maxBatteryLevel
override val energyFlow: FlowDirection get() = parent.energyFlow
private val historyReceiveInternal = ArrayList<Decimal>(HISTORY_SIZE)
private val historyTransferInternal = ArrayList<Decimal>(HISTORY_SIZE)
val historyReceive: List<Decimal> = Collections.unmodifiableList(historyReceiveInternal)
val historyTransfer: List<Decimal> = Collections.unmodifiableList(historyTransferInternal)
val lastTickReceive
get() = historyReceiveInternal[tick]
val lastTickTransfer
get() = historyTransferInternal[tick]
private var thisTickReceive = Decimal.ZERO
private var thisTickTransfer = Decimal.ZERO
var tick = 0
private set
init {
for (i in 0 until HISTORY_SIZE) {
historyReceiveInternal.add(Decimal.ZERO)
historyTransferInternal.add(Decimal.ZERO)
}
}
private fun recordTransfer(value: Decimal, simulate: Boolean): Decimal {
if (!simulate) {
thisTickTransfer += value
}
return value
}
private fun recordReceive(value: Decimal, simulate: Boolean): Decimal {
if (!simulate) {
thisTickReceive += value
}
return value
}
private fun tick() {
tick = (tick + 1) % HISTORY_SIZE
historyReceiveInternal[tick] = thisTickReceive
historyTransferInternal[tick] = thisTickTransfer
thisTickReceive = Decimal.ZERO
thisTickTransfer = Decimal.ZERO
}
val savedata: INBTSerializable<CompoundTag?> = object : INBTSerializable<CompoundTag?> {
override fun serializeNBT(): CompoundTag {
return CompoundTag().also { tag ->
tag["historyReceive"] = ListTag().also {
for (value in historyReceiveInternal) {
it.add(value.serializeNBT())
}
}
tag["historyTransfer"] = ListTag().also {
for (value in historyTransferInternal) {
it.add(value.serializeNBT())
}
}
tag["historyTick"] = tick
}
}
override fun deserializeNBT(nbt: CompoundTag?) {
tick = 0
thisTickReceive = Decimal.ZERO
thisTickTransfer = Decimal.ZERO
for (i in 0 until HISTORY_SIZE) {
historyReceiveInternal[i] = Decimal.ZERO
historyTransferInternal[i] = Decimal.ZERO
}
nbt ?: return
nbt.map("historyTick") { it: NumericTag ->
tick = it.asInt
}
nbt.map("historyReceive") { it: ListTag ->
for (i in 0 until HISTORY_SIZE.coerceAtMost(it.size)) {
historyReceiveInternal[i] = Decimal.deserializeNBT(it[i])
}
}
nbt.map("historyTransfer") { it: ListTag ->
for (i in 0 until HISTORY_SIZE.coerceAtMost(it.size)) {
historyTransferInternal[i] = Decimal.deserializeNBT(it[i])
}
}
}
}
override fun serializeNBT(): CompoundTag {
val tag: CompoundTag
if (parent is INBTSerializable<*>) {
tag = (parent as INBTSerializable<CompoundTag?>).serializeNBT() ?: CompoundTag()
} else {
tag = CompoundTag()
}
val tag2 = savedata.serializeNBT()!!
for (k in tag2.allKeys) {
tag[k] = tag2[k]!!
}
return tag
}
override fun deserializeNBT(nbt: CompoundTag?) {
if (parent is INBTSerializable<*>) {
(parent as INBTSerializable<CompoundTag?>).deserializeNBT(nbt)
}
savedata.deserializeNBT(nbt)
}
companion object {
const val HISTORY_SIZE = 20
private val storages = ArrayList<WeakReference<ProfiledEnergyStorage<*>>>()
// после раздумий, наиболее корректное место для обновления состояния профилированных
// хранилищ энергии будет после всех остальных хуков на тики после тика сервера
// разумеется, такое решение может несколько снизить производительность сервера,
// но ничего страшного произойти не должно
internal fun onServerPostTick() {
storages.forValidRefs { it.tick() }
}
}
}

View File

@ -6,10 +6,13 @@ import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.RGBAColor
import ru.dbotthepony.mc.otm.core.orNull
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import kotlin.math.roundToInt
/**
* Matter interface in Overdrive That Matters
*
* Contracts of this interface follow the ones of [IMatteryEnergyStorage]
*/
interface IMatterStorage {
/**
@ -37,7 +40,7 @@ interface IMatterStorage {
val maxStoredMatter: Decimal
/**
* Empties matter stored of this matter storage
* Empties matter stored of this matter storage, if possible
*
* @throws [UnsupportedOperationException]
* @see storedMatter
@ -47,7 +50,7 @@ interface IMatterStorage {
}
/**
* Fully fills matter stored in this matter storage
* Fully fills matter stored, if possible
*
* @throws [UnsupportedOperationException]
* @see storedMatter
@ -57,19 +60,45 @@ interface IMatterStorage {
}
/**
* Fill matter into this object
* Fill matter into this object, ignoring [matterFlow]
*
* @return matter accepted
*/
fun receiveMatter(howMuch: Decimal, simulate: Boolean): Decimal
/**
* Extract matter from this matter storage
* Extract matter from this matter storage, ignoring [matterFlow]
*
* @return matter extracted
*/
fun extractMatter(howMuch: Decimal, simulate: Boolean): Decimal
/**
* Fill matter into this object, accounting for [matterFlow]
*
* @return matter accepted
*/
fun receiveMatterChecked(howMuch: Decimal, simulate: Boolean): Decimal {
if (matterFlow.input) {
return receiveMatter(howMuch, simulate)
}
return Decimal.ZERO
}
/**
* Extract matter from this matter storage, accounting for [matterFlow]
*
* @return matter extracted
*/
fun extractMatterChecked(howMuch: Decimal, simulate: Boolean): Decimal {
if (matterFlow.output) {
return extractMatter(howMuch, simulate)
}
return Decimal.ZERO
}
/**
* How much matter (estimated) is missing in this matter storage. Why estimated? Because some objects can be bottomless.
*

View File

@ -63,13 +63,6 @@ open class MatterStorageImpl @JvmOverloads constructor(
}
override fun receiveMatter(howMuch: Decimal, simulate: Boolean): Decimal {
if (!canReceiveMatter)
return Decimal.ZERO
return receiveMatterInner(howMuch, simulate)
}
fun receiveMatterInner(howMuch: Decimal, simulate: Boolean): Decimal {
val new: Decimal
if (maxReceive == null) {
@ -89,13 +82,6 @@ open class MatterStorageImpl @JvmOverloads constructor(
}
override fun extractMatter(howMuch: Decimal, simulate: Boolean): Decimal {
if (canExtractMatter)
return Decimal.ZERO
return extractMatterInner(howMuch, simulate)
}
fun extractMatterInner(howMuch: Decimal, simulate: Boolean): Decimal {
val new: Decimal
if (maxExtract == null) {

View File

@ -0,0 +1,45 @@
package ru.dbotthepony.mc.otm.capability.matter
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage
import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.core.math.Decimal
class ProfiledMatterStorage<out M : IMatterStorage>(parent: M) : AbstractProfiledStorage<M>(parent), IMatterStorage {
override var storedMatter by parent::storedMatter
override val maxStoredMatter: Decimal
get() = parent.maxStoredMatter
override fun receiveMatter(howMuch: Decimal, simulate: Boolean): Decimal {
return recordReceive(parent.receiveMatter(howMuch, simulate), simulate)
}
override fun extractMatter(howMuch: Decimal, simulate: Boolean): Decimal {
return recordTransfer(parent.extractMatter(howMuch, simulate), simulate)
}
override fun receiveMatterChecked(howMuch: Decimal, simulate: Boolean): Decimal {
return recordReceive(parent.receiveMatterChecked(howMuch, simulate), simulate)
}
override fun extractMatterChecked(howMuch: Decimal, simulate: Boolean): Decimal {
return recordTransfer(parent.extractMatterChecked(howMuch, simulate), simulate)
}
override val matterFlow: FlowDirection
get() = parent.matterFlow
override val canSetMatterLevel: Boolean
get() = parent.canSetMatterLevel
override fun drainMatter() {
parent.drainMatter()
}
override fun fillMatter() {
parent.fillMatter()
}
override val missingMatter: Decimal
get() = parent.missingMatter
}

View File

@ -11,6 +11,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.slot.BatterySlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.MatterCapacitorSlotPanel
import ru.dbotthepony.mc.otm.client.screen.widget.MatterGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.PowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProfiledMatterGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProfiledPowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
import ru.dbotthepony.mc.otm.menu.matter.MatterBottlerMenu
@ -24,7 +25,7 @@ class MatterBottlerScreen(menu: MatterBottlerMenu, inventory: Inventory, title:
val frame = super.makeMainFrame()!!
val p = ProfiledPowerGaugePanel(this, frame, menu.profiledEnergy, LEFT_MARGIN, GAUGE_TOP_WITH_SLOT)
MatterGaugePanel(this, frame, menu.matterWidget, LEFT_MARGIN + p.width, GAUGE_TOP_WITH_SLOT)
ProfiledMatterGaugePanel(this, frame, menu.matterWidget, LEFT_MARGIN + p.width, GAUGE_TOP_WITH_SLOT)
BatterySlotPanel(this, frame, menu.batterySlot, LEFT_MARGIN, SLOT_TOP_UNDER_GAUGE)

View File

@ -10,6 +10,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.button.makeDeviceControls
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.widget.MatterGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.PowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProfiledMatterGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProfiledPowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
@ -20,7 +21,7 @@ class MatterDecomposerScreen(p_97741_: MatterDecomposerMenu, p_97742_: Inventory
val frame = super.makeMainFrame()!!
val m = ProfiledPowerGaugePanel(this, frame, menu.profiledEnergy, LEFT_MARGIN, GAUGE_TOP_WITH_SLOT)
MatterGaugePanel(this, frame, menu.matterWidget, LEFT_MARGIN + m.width, GAUGE_TOP_WITH_SLOT)
ProfiledMatterGaugePanel(this, frame, menu.matterWidget, LEFT_MARGIN + m.width, GAUGE_TOP_WITH_SLOT)
BatterySlotPanel(this, frame, menu.batterySlot, LEFT_MARGIN, SLOT_TOP_UNDER_GAUGE)

View File

@ -13,6 +13,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.slot.BatterySlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.widget.MatterGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.PowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProfiledMatterGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProfiledPowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
import ru.dbotthepony.mc.otm.menu.matter.MatterReconstructorMenu
@ -22,7 +23,7 @@ class MatterReconstructorScreen(menu: MatterReconstructorMenu, inventory: Invent
val frame = super.makeMainFrame()!!
val p = ProfiledPowerGaugePanel(this, frame, menu.profiledEnergy, LEFT_MARGIN, GAUGE_TOP_WITH_SLOT)
MatterGaugePanel(this, frame, menu.matterWidget, LEFT_MARGIN + p.width, GAUGE_TOP_WITH_SLOT)
ProfiledMatterGaugePanel(this, frame, menu.matterWidget, LEFT_MARGIN + p.width, GAUGE_TOP_WITH_SLOT)
BatterySlotPanel(this, frame, menu.batterySlot, LEFT_MARGIN, SLOT_TOP_UNDER_GAUGE)

View File

@ -9,6 +9,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.button.makeDeviceControls
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.widget.MatterGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.PowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProfiledMatterGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProfiledPowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
import ru.dbotthepony.mc.otm.menu.matter.MatterRecyclerMenu
@ -18,7 +19,7 @@ class MatterRecyclerScreen(menu: MatterRecyclerMenu, inventory: Inventory, title
val frame = super.makeMainFrame()!!
val m = ProfiledPowerGaugePanel(this, frame, menu.profiledEnergy, LEFT_MARGIN, GAUGE_TOP_WITH_SLOT)
MatterGaugePanel(this, frame, menu.matter, LEFT_MARGIN + m.width, GAUGE_TOP_WITH_SLOT)
ProfiledMatterGaugePanel(this, frame, menu.matter, LEFT_MARGIN + m.width, GAUGE_TOP_WITH_SLOT)
BatterySlotPanel(this, frame, menu.batterySlot, LEFT_MARGIN, SLOT_TOP_UNDER_GAUGE)

View File

@ -9,6 +9,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.button.makeDeviceControls
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.widget.MatterGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.PowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProfiledMatterGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProfiledPowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
import ru.dbotthepony.mc.otm.menu.matter.MatterReplicatorMenu
@ -19,7 +20,7 @@ class MatterReplicatorScreen(p_97741_: MatterReplicatorMenu, p_97742_: Inventory
val frame = super.makeMainFrame()!!
val m = ProfiledPowerGaugePanel(this, frame, menu.profiledEnergy, LEFT_MARGIN, GAUGE_TOP_WITH_SLOT)
MatterGaugePanel(this, frame, menu.matter, LEFT_MARGIN + m.width, GAUGE_TOP_WITH_SLOT)
ProfiledMatterGaugePanel(this, frame, menu.matter, LEFT_MARGIN + m.width, GAUGE_TOP_WITH_SLOT)
BatterySlotPanel(this, frame, menu.batterySlot, LEFT_MARGIN, SLOT_TOP_UNDER_GAUGE)

View File

@ -1,13 +1,12 @@
package ru.dbotthepony.mc.otm.client.screen.widget
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.screens.Screen
import ru.dbotthepony.mc.otm.client.render.UVWindingOrder
import ru.dbotthepony.mc.otm.client.render.WidgetLocation
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledEnergyGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
private fun PowerGaugePanel<*>.doRender(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float, flop: Boolean) {
if (height >= 18f) {
@ -74,7 +73,7 @@ fun <S : Screen> TallHorizontalPowerGaugePanel(
open class HorizontalProfiledPowerGaugePanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>? = null,
widget: ProfiledEnergyGaugeWidget,
widget: ProfiledLevelGaugeWidget<*>,
x: Float = 0f,
y: Float = 0f,
width: Float = HorizontalPowerGaugePanel.GAUGE_BACKGROUND.width,
@ -94,7 +93,7 @@ open class HorizontalProfiledPowerGaugePanel<out S : Screen>(
fun <S : Screen> TallHorizontalProfiledPowerGaugePanel(
screen: S,
parent: EditablePanel<*>? = null,
widget: ProfiledEnergyGaugeWidget,
widget: ProfiledLevelGaugeWidget<*>,
x: Float = 0f,
y: Float = 0f,
width: Float = HorizontalPowerGaugePanel.GAUGE_BACKGROUND_TALL.width,

View File

@ -5,18 +5,29 @@ import com.mojang.blaze3d.vertex.BufferUploader
import com.mojang.blaze3d.vertex.DefaultVertexFormat
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.blaze3d.vertex.VertexFormat
import it.unimi.dsi.fastutil.ints.IntArrayList
import net.minecraft.ChatFormatting
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.screens.Screen
import net.minecraft.client.renderer.GameRenderer
import net.minecraft.network.chat.Component
import org.lwjgl.opengl.GL11
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
import ru.dbotthepony.mc.otm.client.ShiftPressedCond
import ru.dbotthepony.mc.otm.client.isShiftDown
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.WidgetLocation
import ru.dbotthepony.mc.otm.client.render.tesselator
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.util.formatMatter
import ru.dbotthepony.mc.otm.core.util.formatMatterLevel
import ru.dbotthepony.mc.otm.core.util.formatPower
import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
import ru.dbotthepony.mc.otm.nanoTime
import kotlin.math.absoluteValue
import kotlin.math.cos
@ -121,3 +132,54 @@ open class MatterGaugePanel<out S : Screen> @JvmOverloads constructor(
val GAUGE_FOREGROUND = WidgetLocation.VERTICAL_GAUGES.sprite(x = 27f, width = 9f)
}
}
private fun formatLevel(a: Decimal, b: Decimal): Component {
val diff = a - b
val fa = a.formatMatter(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.DARK_GREEN)
val fb = b.formatMatter(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.DARK_RED)
if (diff.isZero) {
return TranslatableComponent("otm.gui.diff", diff.formatMatter(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.GRAY), fa, fb)
} else if (diff.isPositive) {
return TranslatableComponent("otm.gui.diff", diff.formatMatter(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.DARK_GREEN), fa, fb)
} else {
return TranslatableComponent("otm.gui.diff", (-diff).formatMatter(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.DARK_RED), fa, fb)
}
}
open class ProfiledMatterGaugePanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>? = null,
val profiledWidget: ProfiledLevelGaugeWidget<*>,
x: Float = 0f,
y: Float = 0f
): MatterGaugePanel<S>(screen, parent, profiledWidget.gauge, x, y) {
override fun makeTooltip(): MutableList<Component> {
return super.makeTooltip().also {
it.add(TextComponent(""))
if (minecraft.window.isShiftDown) {
it.add(formatLevel(profiledWidget.lastTickReceive, profiledWidget.lastTickTransfer))
it.add(TextComponent("---"))
}
it.add(formatLevel(
profiledWidget.weightedReceive,
profiledWidget.weightedTransfer,
))
if (minecraft.window.isShiftDown && minecraft.options.advancedItemTooltips) {
it.add(TextComponent("---"))
val values = IntArrayList()
values.addAll(profiledWidget.tick downTo 0)
values.addAll(AbstractProfiledStorage.HISTORY_SIZE - 1 downTo profiledWidget.tick + 1)
for (i in values.intIterator()) {
it.add(formatLevel(profiledWidget.historyReceive[i].value, profiledWidget.historyTransfer[i].value))
}
}
}
}
}

View File

@ -1,11 +1,11 @@
package ru.dbotthepony.mc.otm.client.screen.widget
import com.mojang.blaze3d.vertex.PoseStack
import it.unimi.dsi.fastutil.ints.IntArrayList
import net.minecraft.ChatFormatting
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.screens.Screen
import net.minecraft.network.chat.Component
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.client.ShiftPressedCond
import ru.dbotthepony.mc.otm.client.isShiftDown
@ -19,7 +19,7 @@ import ru.dbotthepony.mc.otm.core.stream
import ru.dbotthepony.mc.otm.core.util.formatPower
import ru.dbotthepony.mc.otm.core.util.formatPowerLevel
import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledEnergyGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
open class PowerGaugePanel<out S : Screen>(
screen: S,
@ -113,7 +113,7 @@ private fun formatLevel(a: Decimal, b: Decimal): Component {
open class ProfiledPowerGaugePanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>? = null,
val profiledWidget: ProfiledEnergyGaugeWidget,
val profiledWidget: ProfiledLevelGaugeWidget<*>,
x: Float = 0f,
y: Float = 0f,
width: Float = GAUGE_BACKGROUND.width,
@ -129,16 +129,8 @@ open class ProfiledPowerGaugePanel<out S : Screen>(
}
it.add(formatLevel(
profiledWidget.historyReceive
.stream()
.map { it.value }
.reduce(Decimal.ZERO) { a, b -> a + b }
.div(profiledWidget.historyReceive.size),
profiledWidget.historyTransfer.stream()
.map { it.value }
.reduce(Decimal.ZERO) { a, b -> a + b }
.div(profiledWidget.historyReceive.size),
profiledWidget.weightedReceive,
profiledWidget.weightedTransfer,
))
if (minecraft.window.isShiftDown && minecraft.options.advancedItemTooltips) {
@ -146,7 +138,7 @@ open class ProfiledPowerGaugePanel<out S : Screen>(
val values = IntArrayList()
values.addAll(profiledWidget.tick downTo 0)
values.addAll(ProfiledEnergyStorage.HISTORY_SIZE - 1 downTo profiledWidget.tick + 1)
values.addAll(AbstractProfiledStorage.HISTORY_SIZE - 1 downTo profiledWidget.tick + 1)
for (i in values.intIterator()) {
it.add(formatLevel(profiledWidget.historyReceive[i].value, profiledWidget.historyTransfer[i].value))
@ -163,7 +155,7 @@ open class ProfiledPowerGaugePanel<out S : Screen>(
fun <S : Screen> WideProfiledPowerGaugePanel(
screen: S,
parent: EditablePanel<*>? = null,
widget: ProfiledEnergyGaugeWidget,
widget: ProfiledLevelGaugeWidget<*>,
x: Float = 0f,
y: Float = 0f,
width: Float = 18f,

View File

@ -110,7 +110,7 @@ class MatterGraph : Abstract6Graph<MatterNode, MatterGraph>(), IMatterGraphListe
val matter = node.getMatterHandler()
if (matter != null) {
val value = matter.extractMatter(howMuch, simulate)
val value = matter.extractMatterChecked(howMuch, simulate)
howMuch -= value
extracted += value
@ -134,7 +134,7 @@ class MatterGraph : Abstract6Graph<MatterNode, MatterGraph>(), IMatterGraphListe
val matter = node.getMatterHandler()
if (matter != null && matter.matterFlow == FlowDirection.BI_DIRECTIONAL) {
val value = matter.receiveMatter(howMuch, simulate)
val value = matter.receiveMatterChecked(howMuch, simulate)
howMuch -= value
received += value
@ -158,7 +158,7 @@ class MatterGraph : Abstract6Graph<MatterNode, MatterGraph>(), IMatterGraphListe
val matter = node.getMatterHandler()
if (matter != null && matter.matterFlow != FlowDirection.OUTPUT) {
val value = matter.receiveMatter(howMuch, simulate)
val value = matter.receiveMatterChecked(howMuch, simulate)
howMuch -= value
received += value

View File

@ -6,6 +6,7 @@ import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.block.entity.matter.MatterBottlerBlockEntity
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
import ru.dbotthepony.mc.otm.capability.matter.canExtractMatter
import ru.dbotthepony.mc.otm.capability.matter.canReceiveMatter
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
@ -14,7 +15,7 @@ import ru.dbotthepony.mc.otm.menu.widget.ProgressGaugeWidget
import ru.dbotthepony.mc.otm.core.orNull
import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu
import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.widget.ProfiledEnergyGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
import ru.dbotthepony.mc.otm.registry.MMenus
class MatterBottlerMenu @JvmOverloads constructor(
@ -26,16 +27,15 @@ class MatterBottlerMenu @JvmOverloads constructor(
) {
val workFlow = BooleanInputWithFeedback(this)
val progressWidget = ProgressGaugeWidget(this)
val matterWidget = LevelGaugeWidget(this)
val matterWidget = ProfiledLevelGaugeWidget(this, tile?.matter, LevelGaugeWidget(this, tile?.matter))
val storageSlots: List<MatterySlot>
val profiledEnergy = ProfiledEnergyGaugeWidget(this, tile?.energy, energyWidget)
val profiledEnergy = ProfiledLevelGaugeWidget(this, tile?.energy, energyWidget)
init {
val container = tile?.container ?: SimpleContainer(6)
if (tile != null) {
progressWidget.with(tile::getWorkProgress)
matterWidget.with(tile.matter)
workFlow.with(tile::isBottling)
}

View File

@ -15,7 +15,7 @@ import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu
import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.widget.ProfiledEnergyGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
import ru.dbotthepony.mc.otm.registry.MMenus
class MatterDecomposerMenu @JvmOverloads constructor(
@ -30,10 +30,10 @@ class MatterDecomposerMenu @JvmOverloads constructor(
val outputMain: MachineOutputSlot
val outputStacking: MachineOutputSlot
val progressWidget = ProgressGaugeWidget(this, tile)
val matterWidget = LevelGaugeWidget(this, tile?.getCapability(MatteryCapability.MATTER)?.orNull())
val matterWidget = ProfiledLevelGaugeWidget(this, tile?.matter, LevelGaugeWidget(this, tile?.matter))
val itemConfig = ItemConfigPlayerInput(this, tile?.itemConfig)
val energyConfig = EnergyConfigPlayerInput(this, tile?.energyConfig)
val profiledEnergy = ProfiledEnergyGaugeWidget(this, tile?.energy, energyWidget)
val profiledEnergy = ProfiledLevelGaugeWidget(this, tile?.energy, energyWidget)
init {
val container = tile?.outputContainer ?: SimpleContainer(2)

View File

@ -9,7 +9,7 @@ import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledEnergyGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProgressGaugeWidget
import ru.dbotthepony.mc.otm.registry.MMenus
@ -18,7 +18,7 @@ class MatterReconstructorMenu(
inventory: Inventory,
tile: MatterReconstructorBlockEntity? = null
) : MatteryPoweredMenu(MMenus.ITEM_REPAIER, containerId, inventory, tile) {
val matterWidget = LevelGaugeWidget(this, tile?.matter)
val matterWidget = ProfiledLevelGaugeWidget(this, tile?.matter, LevelGaugeWidget(this, tile?.matter))
val slot = object : MatterySlot(tile?.repairContainer ?: SimpleContainer(1), 0) {
override fun mayPlace(itemStack: ItemStack): Boolean {
return itemStack.isRepairable && itemStack.isDamaged && super.mayPlace(itemStack)
@ -30,7 +30,7 @@ class MatterReconstructorMenu(
val energyConfig = EnergyConfigPlayerInput(this, tile?.energyConfig)
val itemConfig = ItemConfigPlayerInput(this, tile?.itemConfig)
val profiledEnergy = ProfiledEnergyGaugeWidget(this, tile?.energy, energyWidget)
val profiledEnergy = ProfiledLevelGaugeWidget(this, tile?.energy, energyWidget)
init {
addStorageSlot(slot)

View File

@ -10,7 +10,7 @@ import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledEnergyGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProgressGaugeWidget
import ru.dbotthepony.mc.otm.registry.MMenus
@ -26,10 +26,10 @@ class MatterRecyclerMenu @JvmOverloads constructor(
}
val progress = ProgressGaugeWidget(this, tile)
val matter = LevelGaugeWidget(this, tile?.matter)
val matter = ProfiledLevelGaugeWidget(this, tile?.matter, LevelGaugeWidget(this, tile?.matter))
val itemConfig = ItemConfigPlayerInput(this, tile?.itemConfig)
val energyConfig = EnergyConfigPlayerInput(this, tile?.energyConfig)
val profiledEnergy = ProfiledEnergyGaugeWidget(this, tile?.energy, energyWidget)
val profiledEnergy = ProfiledLevelGaugeWidget(this, tile?.energy, energyWidget)
init {
addStorageSlot(input)

View File

@ -11,7 +11,7 @@ import ru.dbotthepony.mc.otm.menu.MachineOutputSlot
import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu
import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.widget.ProfiledEnergyGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
import ru.dbotthepony.mc.otm.registry.MMenus
class MatterReplicatorMenu @JvmOverloads constructor(
@ -19,12 +19,12 @@ class MatterReplicatorMenu @JvmOverloads constructor(
inventory: Inventory,
tile: MatterReplicatorBlockEntity? = null
) : MatteryPoweredMenu(MMenus.MATTER_REPLICATOR, p_38852_, inventory, tile) {
val matter = LevelGaugeWidget(this, tile?.matter)
val matter = ProfiledLevelGaugeWidget(this, tile?.matter, LevelGaugeWidget(this, tile?.matter))
val progress = ProgressGaugeWidget(this, tile)
val storageSlots: List<MachineOutputSlot>
val itemConfig = ItemConfigPlayerInput(this, tile?.itemConfig)
val energyConfig = EnergyConfigPlayerInput(this, tile?.energyConfig)
val profiledEnergy = ProfiledEnergyGaugeWidget(this, tile?.energy, energyWidget)
val profiledEnergy = ProfiledLevelGaugeWidget(this, tile?.energy, energyWidget)
init {
val container = tile?.container ?: SimpleContainer(5)

View File

@ -13,7 +13,7 @@ import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu
import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.widget.ProfiledEnergyGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
import ru.dbotthepony.mc.otm.registry.MMenus
class MatterScannerMenu @JvmOverloads constructor(
@ -26,7 +26,7 @@ class MatterScannerMenu @JvmOverloads constructor(
val patterns = LevelGaugeWidget(this)
val itemConfig = ItemConfigPlayerInput(this, tile?.itemConfig)
val energyConfig = EnergyConfigPlayerInput(this, tile?.energyConfig)
val profiledEnergy = ProfiledEnergyGaugeWidget(this, tile?.energy, energyWidget)
val profiledEnergy = ProfiledLevelGaugeWidget(this, tile?.energy, energyWidget)
init {
val container = tile?.container ?: SimpleContainer(1)

View File

@ -13,7 +13,7 @@ import ru.dbotthepony.mc.otm.capability.matteryPlayer
import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu
import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.widget.ProfiledEnergyGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
import ru.dbotthepony.mc.otm.registry.MMenus
import kotlin.reflect.KMutableProperty0
@ -113,7 +113,7 @@ class AndroidStationMenu @JvmOverloads constructor(
val equipment = makeEquipmentSlots()
val energyConfig = EnergyConfigPlayerInput(this, tile?.energyConfig)
val profiledEnergy = ProfiledEnergyGaugeWidget(this, tile?.energy, energyWidget)
val profiledEnergy = ProfiledLevelGaugeWidget(this, tile?.energy, energyWidget)
init {
addInventorySlots()

View File

@ -7,16 +7,13 @@ import ru.dbotthepony.mc.otm.block.entity.tech.BatteryBankBlockEntity
import net.minecraft.world.SimpleContainer
import ru.dbotthepony.mc.otm.block.entity.RedstoneSetting
import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.core.orNull
import ru.dbotthepony.mc.otm.menu.BatterySlot
import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.input.EnumInputWithFeedback
import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.widget.ProfiledEnergyGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
import ru.dbotthepony.mc.otm.registry.MMenus
class BatteryBankMenu @JvmOverloads constructor(
@ -24,7 +21,7 @@ class BatteryBankMenu @JvmOverloads constructor(
inventory: Inventory,
tile: BatteryBankBlockEntity? = null,
) : MatteryMenu(MMenus.BATTERY_BANK, p_38852_, inventory, tile) {
val powerLevel = ProfiledEnergyGaugeWidget(this, tile?.energyConfig?.capability)
val powerLevel = ProfiledLevelGaugeWidget(this, tile?.energyConfig?.capability)
val storageSlots: List<MatterySlot>
val redstone = EnumInputWithFeedback(this, RedstoneSetting::class.java)
val energyConfig = EnergyConfigPlayerInput(this, allowPull = false, allowPush = true)

View File

@ -13,8 +13,7 @@ import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.input.EnumInputWithFeedback
import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledEnergyGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProgressGaugeWidget
import ru.dbotthepony.mc.otm.registry.MMenus
@ -54,7 +53,7 @@ class ChemicalGeneratorMenu @JvmOverloads constructor(id: Int, inv: Inventory, t
}
val progress = ProgressGaugeWidget(this)
val energy = ProfiledEnergyGaugeWidget(this, tile?.energy)
val energy = ProfiledLevelGaugeWidget(this, tile?.energy)
var burnTime by mSynchronizer.int().property
init {

View File

@ -11,8 +11,7 @@ import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.input.EnumInputWithFeedback
import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledEnergyGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
import ru.dbotthepony.mc.otm.registry.MMenus
class EnergyServoMenu @JvmOverloads constructor(
@ -34,7 +33,7 @@ class EnergyServoMenu @JvmOverloads constructor(
val equipment = makeEquipmentSlots(mapMoveToExternal = true)
val powerGauge = ProfiledEnergyGaugeWidget(this, tile?.energy)
val powerGauge = ProfiledLevelGaugeWidget(this, tile?.energy)
val itemConfig = ItemConfigPlayerInput(this, tile?.itemConfig)
val energyConfig = EnergyConfigPlayerInput(this, tile?.energyConfig, allowPull = true, allowPush = true)
val redstoneConfig = EnumInputWithFeedback<RedstoneSetting>(this)

View File

@ -9,7 +9,7 @@ import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu
import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.widget.ProfiledEnergyGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProgressGaugeWidget
import ru.dbotthepony.mc.otm.registry.MMenus
@ -24,7 +24,7 @@ class PlatePressMenu @JvmOverloads constructor(
val progressGauge = ProgressGaugeWidget(this, tile)
val itemConfig = ItemConfigPlayerInput(this, tile?.itemConfig, allowPush = true)
val energyConfig = EnergyConfigPlayerInput(this, tile?.energyConfig, allowPull = true)
val profiledEnergy = ProfiledEnergyGaugeWidget(this, tile?.energy, energyWidget)
val profiledEnergy = ProfiledLevelGaugeWidget(this, tile?.energy, energyWidget)
init {
addStorageSlot(inputSlot)

View File

@ -1,44 +0,0 @@
package ru.dbotthepony.mc.otm.menu.widget
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.util.DecimalValueCodec
import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu
import ru.dbotthepony.mc.otm.network.synchronizer.FieldSynchronizer
class ProfiledEnergyGaugeWidget(synchronizer: FieldSynchronizer, val gauge: LevelGaugeWidget = LevelGaugeWidget(synchronizer)) {
var parent: ProfiledEnergyStorage<*>? = null
private set
val historyReceive = immutableList(ProfiledEnergyStorage.HISTORY_SIZE) {
synchronizer.ComputedField({ parent?.historyReceive?.get(it) ?: Decimal.ZERO }, DecimalValueCodec)
}
val historyTransfer = immutableList(ProfiledEnergyStorage.HISTORY_SIZE) {
synchronizer.ComputedField({ parent?.historyTransfer?.get(it) ?: Decimal.ZERO }, DecimalValueCodec)
}
val tick by synchronizer.ComputedIntField({ parent?.tick ?: 0 }).property
val lastTickReceive by synchronizer.ComputedField({ parent?.lastTickReceive ?: Decimal.ZERO }, DecimalValueCodec)
val lastTickTransfer by synchronizer.ComputedField({ parent?.lastTickTransfer ?: Decimal.ZERO }, DecimalValueCodec)
constructor(synchronizer: FieldSynchronizer, storage: ProfiledEnergyStorage<*>?, gauge: LevelGaugeWidget = LevelGaugeWidget(synchronizer)) : this(synchronizer, gauge = gauge) {
if (storage != null) {
with(storage)
}
}
constructor(menu: MatteryMenu, storage: ProfiledEnergyStorage<*>?, gauge: LevelGaugeWidget = LevelGaugeWidget(menu)) : this(menu.mSynchronizer, storage, gauge = gauge)
constructor(menu: MatteryMenu, gauge: LevelGaugeWidget = LevelGaugeWidget(menu)) : this(menu.mSynchronizer, gauge = gauge)
constructor(menu: MatteryPoweredMenu, storage: ProfiledEnergyStorage<*>?) : this(menu.mSynchronizer, storage, menu.energyWidget)
constructor(menu: MatteryPoweredMenu) : this(menu.mSynchronizer, menu.energyWidget)
fun with(storage: ProfiledEnergyStorage<*>): ProfiledEnergyGaugeWidget {
gauge.with(storage)
parent = storage
return this
}
}

View File

@ -0,0 +1,62 @@
package ru.dbotthepony.mc.otm.menu.widget
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.IMatterStorage
import ru.dbotthepony.mc.otm.core.collect.SupplierList
import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.util.DecimalValueCodec
import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu
import ru.dbotthepony.mc.otm.network.synchronizer.FieldSynchronizer
class ProfiledLevelGaugeWidget<P : AbstractProfiledStorage<*>>(synchronizer: FieldSynchronizer, val gauge: LevelGaugeWidget = LevelGaugeWidget(synchronizer)) {
var parent: P? = null
private set
val historyReceive = immutableList(AbstractProfiledStorage.HISTORY_SIZE) {
synchronizer.ComputedField({ parent?.historyReceive?.get(it) ?: Decimal.ZERO }, DecimalValueCodec)
}
val historyTransfer = immutableList(AbstractProfiledStorage.HISTORY_SIZE) {
synchronizer.ComputedField({ parent?.historyTransfer?.get(it) ?: Decimal.ZERO }, DecimalValueCodec)
}
val directHistoryReceive = SupplierList(historyReceive)
val directHistoryTransfer = SupplierList(historyTransfer)
val tick by synchronizer.ComputedIntField({ parent?.tick ?: 0 }).property
val lastTickReceive by synchronizer.ComputedField({ parent?.lastTickReceive ?: Decimal.ZERO }, DecimalValueCodec)
val lastTickTransfer by synchronizer.ComputedField({ parent?.lastTickTransfer ?: Decimal.ZERO }, DecimalValueCodec)
constructor(synchronizer: FieldSynchronizer, storage: P?, gauge: LevelGaugeWidget = LevelGaugeWidget(synchronizer)) : this(synchronizer, gauge = gauge) {
if (storage != null) {
with(storage)
}
}
constructor(menu: MatteryMenu, storage: P?, gauge: LevelGaugeWidget = LevelGaugeWidget(menu)) : this(menu.mSynchronizer, storage, gauge = gauge)
constructor(menu: MatteryMenu, gauge: LevelGaugeWidget = LevelGaugeWidget(menu)) : this(menu.mSynchronizer, gauge = gauge)
constructor(menu: MatteryPoweredMenu, storage: P?) : this(menu.mSynchronizer, storage, menu.energyWidget)
constructor(menu: MatteryPoweredMenu) : this(menu.mSynchronizer, menu.energyWidget)
fun with(storage: P): ProfiledLevelGaugeWidget<P> {
if (storage is IMatterStorage)
gauge.with(storage)
else if (storage is IMatteryEnergyStorage)
gauge.with(storage)
parent = storage
return this
}
val weightedTransfer: Decimal get() {
return AbstractProfiledStorage.calcWeightedAverage(directHistoryTransfer, tick)
}
val weightedReceive: Decimal get() {
return AbstractProfiledStorage.calcWeightedAverage(directHistoryReceive, tick)
}
}