Quantum batteries now track statistics

This commit is contained in:
DBotThePony 2022-09-04 18:36:42 +07:00
parent e3643d7f61
commit 094f476fea
Signed by: DBot
GPG Key ID: DCC23B5715498507
2 changed files with 154 additions and 46 deletions

View File

@ -130,6 +130,7 @@ private fun misc(provider: MatteryLanguageProvider) {
misc("item.power.infinite.storage", "Stored energy: Infinity / Infinity") misc("item.power.infinite.storage", "Stored energy: Infinity / Infinity")
misc("item.power.infinite.throughput", "Max I/O Infinite / Infinite") misc("item.power.infinite.throughput", "Max I/O Infinite / Infinite")
misc("item.power.passed", "Passed energy: %s") misc("item.power.passed", "Passed energy: %s")
misc("item.power.received", "Received energy: %s")
misc("item.power.average", "Average throughput: %s/t") misc("item.power.average", "Average throughput: %s/t")
misc("item.power.last_20_ticks", "Last second: %s") misc("item.power.last_20_ticks", "Last second: %s")
misc("item.power.last_tick", "Last tick: %s") misc("item.power.last_tick", "Last tick: %s")

View File

@ -4,7 +4,9 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
import net.minecraft.ChatFormatting import net.minecraft.ChatFormatting
import net.minecraft.core.Direction import net.minecraft.core.Direction
import net.minecraft.nbt.ByteArrayTag
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
import net.minecraft.network.FriendlyByteBuf import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.world.item.* import net.minecraft.world.item.*
@ -28,13 +30,50 @@ import ru.dbotthepony.mc.otm.network.GenericNetworkChannel
import ru.dbotthepony.mc.otm.network.MatteryPacket import ru.dbotthepony.mc.otm.network.MatteryPacket
import ru.dbotthepony.mc.otm.network.packetHandled import ru.dbotthepony.mc.otm.network.packetHandled
import ru.dbotthepony.mc.otm.saveddata.SavedCountingMap import ru.dbotthepony.mc.otm.saveddata.SavedCountingMap
import ru.dbotthepony.mc.otm.saveddata.SavedMapDelegate
import java.util.function.Supplier import java.util.function.Supplier
class QuantumBatteryItem : Item { class QuantumBatteryItem : Item {
class Data(
val parent: SavedCountingMap<Data>?,
val index: Int = -1,
energy: ImpreciseFraction = ImpreciseFraction.ZERO,
passed: ImpreciseFraction = ImpreciseFraction.ZERO,
received: ImpreciseFraction = ImpreciseFraction.ZERO,
) {
constructor(
energy: ImpreciseFraction = ImpreciseFraction.ZERO,
passed: ImpreciseFraction = ImpreciseFraction.ZERO,
received: ImpreciseFraction = ImpreciseFraction.ZERO,
) : this(null, -1, energy, passed, received)
var energy: ImpreciseFraction = energy
set(value) {
if (field != value) {
field = value
parent?.isDirty = true
}
}
var passed: ImpreciseFraction = passed
set(value) {
if (field != value) {
field = value
parent?.isDirty = true
}
}
var received: ImpreciseFraction = received
set(value) {
if (field != value) {
field = value
parent?.isDirty = true
}
}
}
private inner class Power(private val stack: ItemStack) : IMatteryEnergyStorage, ICapabilityProvider { private inner class Power(private val stack: ItemStack) : IMatteryEnergyStorage, ICapabilityProvider {
private val resolver = LazyOptional.of { this } private val resolver = LazyOptional.of { this }
var delegate = SavedMapDelegate(ImpreciseFraction.ZERO) var data = Data()
override fun <T : Any?> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> { override fun <T : Any?> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> {
if (cap == ForgeCapabilities.ENERGY || cap == MatteryCapability.ENERGY) { if (cap == ForgeCapabilities.ENERGY || cap == MatteryCapability.ENERGY) {
@ -53,30 +92,32 @@ class QuantumBatteryItem : Item {
return ImpreciseFraction.ZERO return ImpreciseFraction.ZERO
} }
if (delegate.parent == null && isServerThread()) { if (data.parent == null && isServerThread()) {
determineQuantumLink() determineQuantumLink()
if (delegate.parent == null) { if (data.parent == null) {
return ImpreciseFraction.ZERO return ImpreciseFraction.ZERO
} }
} }
if (isCreative) { if (isCreative) {
val newEnergy = (delegate.value - howMuch).moreThanZero() val newEnergy = (data.energy - howMuch).moreThanZero()
val diff = delegate.value - newEnergy val diff = data.energy - newEnergy
if (!simulate) { if (!simulate) {
delegate.value = newEnergy data.energy = newEnergy
data.passed += diff
} }
return diff return diff
} }
val newEnergy = (delegate.value - howMuch.coerceAtMost(throughput!!)).moreThanZero() val newEnergy = (data.energy - howMuch.coerceAtMost(throughput!!)).moreThanZero()
val diff = delegate.value - newEnergy val diff = data.energy - newEnergy
if (!simulate) { if (!simulate) {
delegate.value = newEnergy data.energy = newEnergy
data.passed += diff
} }
return diff return diff
@ -91,31 +132,33 @@ class QuantumBatteryItem : Item {
return ImpreciseFraction.ZERO return ImpreciseFraction.ZERO
} }
if (delegate.parent == null && isServerThread()) { if (data.parent == null && isServerThread()) {
determineQuantumLink() determineQuantumLink()
if (delegate.parent == null) { if (data.parent == null) {
return ImpreciseFraction.ZERO return ImpreciseFraction.ZERO
} }
} }
if (isCreative) { if (isCreative) {
if (!simulate) { if (!simulate) {
delegate.value += howMuch data.energy += howMuch
data.received += howMuch
} }
return howMuch return howMuch
} }
if (delegate.value >= capacity!!) { if (data.energy >= capacity!!) {
return ImpreciseFraction.ZERO return ImpreciseFraction.ZERO
} }
val newEnergy = (delegate.value + howMuch.coerceAtMost(throughput!!)).coerceAtMost(capacity) val newEnergy = (data.energy + howMuch.coerceAtMost(throughput!!)).coerceAtMost(capacity)
val diff = newEnergy - delegate.value val diff = newEnergy - data.energy
if (!simulate) { if (!simulate) {
delegate.value = newEnergy data.energy = newEnergy
data.received += diff
} }
return diff return diff
@ -123,17 +166,41 @@ class QuantumBatteryItem : Item {
override val batteryLevel: ImpreciseFraction override val batteryLevel: ImpreciseFraction
get() { get() {
if (delegate.parent == null) { if (data.parent == null) {
determineQuantumLink() determineQuantumLink()
} }
if (isClientThread()) { if (isClientThread()) {
return clientPowerMap[delegate.index] ?: delegate.value return clientPowerMap[data.index]?.energy ?: data.energy
} }
return delegate.value return data.energy
} }
val passed: ImpreciseFraction get() {
if (data.parent == null) {
determineQuantumLink()
}
if (isClientThread()) {
return clientPowerMap[data.index]?.passed ?: data.passed
}
return data.passed
}
val received: ImpreciseFraction get() {
if (data.parent == null) {
determineQuantumLink()
}
if (isClientThread()) {
return clientPowerMap[data.index]?.received ?: data.received
}
return data.received
}
override val maxBatteryLevel: ImpreciseFraction override val maxBatteryLevel: ImpreciseFraction
get() = capacity ?: (batteryLevel + ImpreciseFraction.LONG_MAX_VALUE) get() = capacity ?: (batteryLevel + ImpreciseFraction.LONG_MAX_VALUE)
@ -149,39 +216,39 @@ class QuantumBatteryItem : Item {
} }
private fun determineQuantumLink() { private fun determineQuantumLink() {
if (delegate.parent == null && isServerThread()) { if (data.parent == null && isServerThread()) {
val existing = stack.tag?.getInt("link_id") val existing = stack.tag?.getInt("link_id")
if (existing == null) { if (existing == null) {
val old = delegate val old = data
delegate = saveData!!.factorize() data = saveData!!.factorize()
delegate.value = old.value data.energy = old.energy
stack.tagNotNull["link_id"] = delegate.index stack.tagNotNull["link_id"] = data.index
} else { } else {
delegate = saveData?.computeIfAbsent(existing) ?: SavedMapDelegate(null, existing, delegate.value) data = saveData?.computeIfAbsent(existing) ?: Data(null, existing, data.energy, data.passed, data.received)
} }
} else if (!isServerThread()) { } else if (!isServerThread()) {
// client ? // client ?
val existing = stack.tag?.getInt("link_id") ?: return val existing = stack.tag?.getInt("link_id") ?: return
if (existing != delegate.index) { if (existing != data.index) {
delegate = SavedMapDelegate(delegate.parent, existing, delegate.value) data = Data(data.parent, existing, data.energy, data.passed, data.received)
} }
} }
} }
fun determineQuantumLinkWeak() { fun determineQuantumLinkWeak() {
if (delegate.parent == null && isServerThread()) { if (data.parent == null && isServerThread()) {
val existing = stack.tag?.getInt("link_id") val existing = stack.tag?.getInt("link_id")
if (existing != null) { if (existing != null) {
delegate = saveData?.computeIfAbsent(existing) ?: SavedMapDelegate(null, existing, delegate.value) data = saveData?.computeIfAbsent(existing) ?: Data(null, existing, data.energy, data.passed, data.received)
} }
} else if (!isServerThread()) { } else if (!isServerThread()) {
val existing = stack.tag?.getInt("link_id") ?: return val existing = stack.tag?.getInt("link_id") ?: return
if (existing != delegate.index) { if (existing != data.index) {
delegate = SavedMapDelegate(delegate.parent, existing, delegate.value) data = Data(data.parent, existing, data.energy, data.passed, data.received)
} }
} }
} }
@ -193,19 +260,25 @@ class QuantumBatteryItem : Item {
val saveDataID: String val saveDataID: String
val saveData: SavedCountingMap<SavedMapDelegate<ImpreciseFraction>>? get() { val saveData: SavedCountingMap<Data>? get() {
if (isServerThread()) { if (isServerThread()) {
return MINECRAFT_SERVER.overworld().dataStorage.computeIfAbsent({ return MINECRAFT_SERVER.overworld().dataStorage.computeIfAbsent({
SavedMapDelegate.makeMap(ImpreciseFraction::serializeNBT, ImpreciseFraction.Companion::deserializeNBT, ImpreciseFraction.ZERO).load(it) SavedCountingMap(Companion::storeValue, Companion::loadValue, ::Data).load(it)
}, { }, {
SavedMapDelegate.makeMap(ImpreciseFraction::serializeNBT, ImpreciseFraction.Companion::deserializeNBT, ImpreciseFraction.ZERO) SavedCountingMap(Companion::storeValue, Companion::loadValue, ::Data)
}, saveDataID) ?: throw NullPointerException("Unable to get save data for $this in ${MINECRAFT_SERVER.overworld()}") }, saveDataID) ?: throw NullPointerException("Unable to get save data for $this in ${MINECRAFT_SERVER.overworld()}")
} }
return null return null
} }
val clientPowerMap: Int2ObjectAVLTreeMap<ImpreciseFraction> by lazy { data class ClientData(
val energy: ImpreciseFraction = ImpreciseFraction.ZERO,
val passed: ImpreciseFraction = ImpreciseFraction.ZERO,
val received: ImpreciseFraction = ImpreciseFraction.ZERO,
)
val clientPowerMap: Int2ObjectAVLTreeMap<ClientData> by lazy {
check(isClient) { "Invalid side" } check(isClient) { "Invalid side" }
Int2ObjectAVLTreeMap() Int2ObjectAVLTreeMap()
} }
@ -242,8 +315,6 @@ class QuantumBatteryItem : Item {
if (isCreative) { if (isCreative) {
components.add(TranslatableComponent("otm.item.quantum_battery.creative_power", power.batteryLevel.formatPower()).withStyle(ChatFormatting.GRAY)) components.add(TranslatableComponent("otm.item.quantum_battery.creative_power", power.batteryLevel.formatPower()).withStyle(ChatFormatting.GRAY))
components.add(TranslatableComponent("otm.item.quantum_battery.creative").withStyle(ChatFormatting.DARK_GRAY))
components.add(TranslatableComponent("otm.item.quantum_battery.creative2").withStyle(ChatFormatting.DARK_GRAY))
} else { } else {
components.add(TranslatableComponent("otm.item.power.normal.storage", power.batteryLevel.formatPower(), capacity!!.formatPower()).withStyle(ChatFormatting.GRAY)) components.add(TranslatableComponent("otm.item.power.normal.storage", power.batteryLevel.formatPower(), capacity!!.formatPower()).withStyle(ChatFormatting.GRAY))
components.add( components.add(
@ -254,7 +325,15 @@ class QuantumBatteryItem : Item {
).withStyle(ChatFormatting.GRAY)) ).withStyle(ChatFormatting.GRAY))
} }
components.add(TranslatableComponent("otm.item.quantum_link_id", power.delegate.index).withStyle(ChatFormatting.DARK_GRAY)) components.add(TranslatableComponent("otm.item.power.passed", power.passed.formatPower()).withStyle(ChatFormatting.GRAY))
components.add(TranslatableComponent("otm.item.power.received", power.received.formatPower()).withStyle(ChatFormatting.GRAY))
if (isCreative) {
components.add(TranslatableComponent("otm.item.quantum_battery.creative").withStyle(ChatFormatting.DARK_GRAY))
components.add(TranslatableComponent("otm.item.quantum_battery.creative2").withStyle(ChatFormatting.DARK_GRAY))
}
components.add(TranslatableComponent("otm.item.quantum_link_id", power.data.index).withStyle(ChatFormatting.DARK_GRAY))
} }
companion object { companion object {
@ -262,7 +341,9 @@ class QuantumBatteryItem : Item {
return ChargePacket( return ChargePacket(
(ForgeRegistries.ITEMS as ForgeRegistry<Item>).getValue(buff.readInt()) as QuantumBatteryItem, (ForgeRegistries.ITEMS as ForgeRegistry<Item>).getValue(buff.readInt()) as QuantumBatteryItem,
buff.readInt(), buff.readInt(),
buff.readImpreciseFraction() buff.readImpreciseFraction(),
buff.readImpreciseFraction(),
buff.readImpreciseFraction(),
) )
} }
@ -277,30 +358,56 @@ class QuantumBatteryItem : Item {
power.determineQuantumLinkWeak() power.determineQuantumLinkWeak()
if (power.delegate.index < 0) { if (power.data.index < 0) {
continue continue
} }
if (networkedChannels.add(power.delegate.index)) { if (networkedChannels.add(power.data.index)) {
GenericNetworkChannel.send(ply, ChargePacket(item.item as QuantumBatteryItem, power.delegate.index, power.batteryLevel)) GenericNetworkChannel.send(ply, ChargePacket(item.item as QuantumBatteryItem, power.data.index, power.batteryLevel, power.data.passed, power.data.received))
} }
} }
} }
} }
} }
} }
private fun loadValue(parent: SavedCountingMap<Data>, tag: Tag, index: Int): Data {
if (tag is ByteArrayTag) {
return Data(parent, index, ImpreciseFraction.deserializeNBT(tag))
} else if (tag is CompoundTag) {
return Data(parent, index, ImpreciseFraction.deserializeNBT(tag["energy"]), ImpreciseFraction.deserializeNBT(tag["passed"]), ImpreciseFraction.deserializeNBT(tag["received"]))
} else {
return Data(parent, index)
}
}
private fun storeValue(parent: SavedCountingMap<Data>, value: Data, index: Int): CompoundTag {
return CompoundTag().also {
it["energy"] = value.energy.serializeNBT()
it["passed"] = value.passed.serializeNBT()
it["received"] = value.received.serializeNBT()
}
}
} }
class ChargePacket(val type: QuantumBatteryItem, val channel: Int, val value: ImpreciseFraction) : MatteryPacket { class ChargePacket(
val type: QuantumBatteryItem,
val channel: Int,
val energy: ImpreciseFraction,
val passed: ImpreciseFraction,
val received: ImpreciseFraction,
) : MatteryPacket {
override fun write(buff: FriendlyByteBuf) { override fun write(buff: FriendlyByteBuf) {
buff.writeInt((ForgeRegistries.ITEMS as ForgeRegistry<Item>).getID(type)) buff.writeInt((ForgeRegistries.ITEMS as ForgeRegistry<Item>).getID(type))
buff.writeInt(channel) buff.writeInt(channel)
buff.writeImpreciseFraction(value) buff.writeImpreciseFraction(energy)
buff.writeImpreciseFraction(passed)
buff.writeImpreciseFraction(received)
} }
override fun play(context: Supplier<NetworkEvent.Context>) { override fun play(context: Supplier<NetworkEvent.Context>) {
context.packetHandled = true context.packetHandled = true
type.clientPowerMap[channel] = value type.clientPowerMap[channel] = ClientData(energy, passed, received)
} }
} }
} }