Revisit quantum battery code and clean it up

This commit is contained in:
DBotThePony 2023-08-16 18:15:14 +07:00
parent 8e2c1f25dc
commit 8e03b4363d
Signed by: DBot
GPG Key ID: DCC23B5715498507
14 changed files with 439 additions and 366 deletions

View File

@ -17,18 +17,37 @@ import net.minecraftforge.fml.loading.FMLLoader
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage
import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.core.collect.WeakHashSet
import ru.dbotthepony.mc.otm.core.util.AtomicallyInvalidatedLazy
import ru.dbotthepony.mc.otm.core.util.IConditionalTickable import ru.dbotthepony.mc.otm.core.util.IConditionalTickable
import ru.dbotthepony.mc.otm.core.util.ITickable import ru.dbotthepony.mc.otm.core.util.ITickable
import ru.dbotthepony.mc.otm.core.util.TickList import ru.dbotthepony.mc.otm.core.util.TickList
import ru.dbotthepony.mc.otm.graph.GraphNodeList import ru.dbotthepony.mc.otm.graph.GraphNodeList
import ru.dbotthepony.mc.otm.network.MatteryNetworkChannel import ru.dbotthepony.mc.otm.network.MatteryNetworkChannel
import java.util.* import java.util.*
import java.util.concurrent.atomic.AtomicInteger
private val preServerTick = TickList() private val preServerTick = TickList()
private val postServerTick = TickList() private val postServerTick = TickList()
private val preWorldTick = WeakHashMap<Level, TickList>() private val preWorldTick = WeakHashMap<Level, TickList>()
private val postWorldTick = WeakHashMap<Level, TickList>() private val postWorldTick = WeakHashMap<Level, TickList>()
private val clientThreads = WeakHashSet<Thread>()
private val serverThreads = WeakHashSet<Thread>()
private val serverCounter = AtomicInteger()
private var _server: MinecraftServer? = null
val isClient: Boolean by lazy { FMLLoader.getDist() == Dist.CLIENT }
fun <V> lazyPerServer(fn: (MinecraftServer) -> V): Lazy<V> {
return AtomicallyInvalidatedLazy(serverCounter) {
if (!SERVER_IS_LIVE)
throw IllegalStateException("No server is running")
fn.invoke(_server!!)
}
}
fun onceServerPre(inTicks: Int, callback: Runnable): TickList.Timer? { fun onceServerPre(inTicks: Int, callback: Runnable): TickList.Timer? {
if (!SERVER_IS_LIVE) { if (!SERVER_IS_LIVE) {
LOGGER.error("Refusing to add timer $callback in ticks $inTicks while server is dying", IllegalStateException("Server is stopping")) LOGGER.error("Refusing to add timer $callback in ticks $inTicks while server is dying", IllegalStateException("Server is stopping"))
@ -47,12 +66,6 @@ fun onceServer(inTicks: Int, callback: Runnable): TickList.Timer? {
return postServerTick.Timer(inTicks, callback) return postServerTick.Timer(inTicks, callback)
} }
private var _server: MinecraftServer? = null
private var _serverThread: Thread? = null
private var _clientThread: Thread? = null
val isClient: Boolean by lazy { FMLLoader.getDist() == Dist.CLIENT }
private val isPausedImpl: Boolean get() { private val isPausedImpl: Boolean get() {
val server = _server val server = _server
@ -64,7 +77,7 @@ private val isPausedImpl: Boolean get() {
} }
val isPaused: Boolean get() { val isPaused: Boolean get() {
if (_clientThread === null) { if (clientThreads.isEmpty()) {
return false return false
} }
@ -72,11 +85,7 @@ val isPaused: Boolean get() {
} }
fun recordClientThread() { fun recordClientThread() {
if (_clientThread != null) { clientThreads.add(Thread.currentThread())
throw IllegalStateException("Already have client channel")
}
_clientThread = Thread.currentThread()
} }
fun runIfClient(lambda: () -> Unit) { fun runIfClient(lambda: () -> Unit) {
@ -119,11 +128,11 @@ fun <V> runOnClient(value: V, lambda: () -> V): V {
} }
fun isServerThread(): Boolean { fun isServerThread(): Boolean {
return Thread.currentThread() === _serverThread return Thread.currentThread() in serverThreads
} }
fun isClientThread(): Boolean { fun isClientThread(): Boolean {
return Thread.currentThread() === _clientThread return Thread.currentThread() in clientThreads
} }
val MINECRAFT_SERVER: MinecraftServer val MINECRAFT_SERVER: MinecraftServer
@ -146,6 +155,7 @@ private val LOGGER = LogManager.getLogger()
fun onServerTick(event: ServerTickEvent) { fun onServerTick(event: ServerTickEvent) {
if (event.phase === TickEvent.Phase.START) { if (event.phase === TickEvent.Phase.START) {
preServerTick.tick() preServerTick.tick()
serverThreads.add(Thread.currentThread())
} else { } else {
postServerTick.tick() postServerTick.tick()
// чтоб не плодить кучу подписчиков, вызовем напрямую отсюда // чтоб не плодить кучу подписчиков, вызовем напрямую отсюда
@ -158,6 +168,12 @@ fun onServerTick(event: ServerTickEvent) {
fun onWorldTick(event: LevelTickEvent) { fun onWorldTick(event: LevelTickEvent) {
if (event.phase === TickEvent.Phase.START) { if (event.phase === TickEvent.Phase.START) {
preWorldTick[event.level]?.tick() preWorldTick[event.level]?.tick()
if (event.side.isClient) {
clientThreads.add(Thread.currentThread())
} else if (event.side.isServer) {
serverThreads.add(Thread.currentThread())
}
} else { } else {
postWorldTick[event.level]?.tick() postWorldTick[event.level]?.tick()
} }
@ -254,13 +270,15 @@ fun onServerStarting(event: ServerAboutToStartEvent) {
clear() clear()
SERVER_IS_LIVE = true SERVER_IS_LIVE = true
_server = event.server _server = event.server
_serverThread = Thread.currentThread() serverThreads.add(Thread.currentThread())
serverCounter.incrementAndGet()
MatteryNetworkChannel.onServerStarting() MatteryNetworkChannel.onServerStarting()
} }
fun onServerStopping(event: ServerStoppingEvent) { fun onServerStopping(event: ServerStoppingEvent) {
clear() clear()
SERVER_IS_LIVE = false SERVER_IS_LIVE = false
serverCounter.incrementAndGet()
MatteryNetworkChannel.onServerStopping() MatteryNetworkChannel.onServerStopping()
} }
@ -273,6 +291,6 @@ fun onServerStopped(event: ServerStoppedEvent) {
} }
_server = null _server = null
_serverThread = null serverCounter.incrementAndGet()
MatteryNetworkChannel.onServerStopped() MatteryNetworkChannel.onServerStopped()
} }

View File

@ -1,6 +1,7 @@
package ru.dbotthepony.mc.otm.capability package ru.dbotthepony.mc.otm.capability
import com.google.common.collect.Streams import com.google.common.collect.Streams
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet
import net.minecraft.ChatFormatting import net.minecraft.ChatFormatting
import net.minecraft.core.Direction import net.minecraft.core.Direction
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
@ -30,6 +31,9 @@ import ru.dbotthepony.mc.otm.container.iterator
import ru.dbotthepony.mc.otm.container.stream import ru.dbotthepony.mc.otm.container.stream
import ru.dbotthepony.mc.otm.core.collect.AwareItemStack import ru.dbotthepony.mc.otm.core.collect.AwareItemStack
import ru.dbotthepony.mc.otm.core.collect.ContainerItemStackEntry import ru.dbotthepony.mc.otm.core.collect.ContainerItemStackEntry
import ru.dbotthepony.mc.otm.core.collect.concatIterators
import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.isNotEmpty import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.orNull import ru.dbotthepony.mc.otm.core.orNull
@ -219,25 +223,25 @@ fun ICapabilityProvider.getMatteryEnergySided(side: Direction? = null): LazyOpti
* *
* Contains all items that player might carry * Contains all items that player might carry
*/ */
fun Player.itemsStream(includeCosmetics: Boolean = true): Stream<out ItemStack> { fun Player.items(includeCosmetics: Boolean = true): Iterator<ItemStack> {
val streams = ArrayList<Stream<out ItemStack>>() val iterators = ArrayList<Iterator<ItemStack>>()
streams.add(inventory.stream()) iterators.add(inventory.iterator())
matteryPlayer?.let { matteryPlayer?.let {
if (it.hasExopack) { if (it.hasExopack) {
streams.add(it.exopackContainer.stream()) iterators.add(it.exopackContainer.iterator())
} }
} }
if (isCuriosLoaded) { if (isCuriosLoaded) {
streams.add(curiosStream(includeCosmetics)) iterators.add(curiosStream(includeCosmetics))
} }
if (includeCosmetics && isCosmeticArmorLoaded) { if (includeCosmetics && isCosmeticArmorLoaded) {
streams.add(cosmeticArmorStream()) iterators.add(cosmeticArmorStream())
} }
return streams.stream().flatMap { it } return concatIterators(iterators)
} }
/** /**
@ -245,20 +249,20 @@ fun Player.itemsStream(includeCosmetics: Boolean = true): Stream<out ItemStack>
* *
* Contains all items that player might see/access ([itemsStream] + open container's items) * Contains all items that player might see/access ([itemsStream] + open container's items)
*/ */
fun Player.allItemsStream(includeCosmetics: Boolean = true): Stream<out ItemStack> { fun Player.allItems(includeCosmetics: Boolean = true): Iterator<ItemStack> {
if (containerMenu == inventoryMenu || containerMenu == matteryPlayer?.exoPackMenu) { if (containerMenu == inventoryMenu || containerMenu == matteryPlayer?.exoPackMenu) {
return itemsStream(includeCosmetics) return items(includeCosmetics)
} }
val seen = IdentityHashMap<ItemStack, Unit>(containerMenu.slots.size) val seen = ReferenceOpenHashSet<ItemStack>(containerMenu.slots.size)
for (slot in containerMenu.slots) { for (slot in containerMenu.slots) {
seen[slot.item] = Unit seen.add(slot.item)
} }
return Streams.concat( return concatIterators(
itemsStream(includeCosmetics).filter { it.isEmpty || it !in seen }, items(includeCosmetics).filter { it.isNotEmpty && it !in seen },
containerMenu.slots.stream().map { it.item }) containerMenu.slots.iterator().map { it.item })
} }
/** /**

View File

@ -24,8 +24,10 @@ import ru.dbotthepony.mc.otm.client.screen.panels.button.RectangleButtonPanel
import ru.dbotthepony.mc.otm.compat.cos.CosmeticToggleButton.Companion.BUTTON_ACTIVE import ru.dbotthepony.mc.otm.compat.cos.CosmeticToggleButton.Companion.BUTTON_ACTIVE
import ru.dbotthepony.mc.otm.compat.cos.CosmeticToggleButton.Companion.BUTTON_INACTIVE import ru.dbotthepony.mc.otm.compat.cos.CosmeticToggleButton.Companion.BUTTON_INACTIVE
import ru.dbotthepony.mc.otm.container.awareStream import ru.dbotthepony.mc.otm.container.awareStream
import ru.dbotthepony.mc.otm.container.iterator
import ru.dbotthepony.mc.otm.container.stream import ru.dbotthepony.mc.otm.container.stream
import ru.dbotthepony.mc.otm.core.collect.AwareItemStack import ru.dbotthepony.mc.otm.core.collect.AwareItemStack
import ru.dbotthepony.mc.otm.core.collect.emptyIterator
import ru.dbotthepony.mc.otm.menu.MatterySlot import ru.dbotthepony.mc.otm.menu.MatterySlot
import java.util.stream.Stream import java.util.stream.Stream
@ -71,16 +73,16 @@ private class CosmeticSlot(container: Container, private val slot: EquipmentSlot
} }
} }
fun Player.cosmeticArmorStream(): Stream<out ItemStack> { fun Player.cosmeticArmorStream(): Iterator<ItemStack> {
if (!isCosmeticArmorLoaded) { if (!isCosmeticArmorLoaded) {
return Stream.empty() return emptyIterator()
} }
return cosmeticArmorStreamImpl() return cosmeticArmorStreamImpl()
} }
private fun Player.cosmeticArmorStreamImpl(): Stream<out ItemStack> { private fun Player.cosmeticArmorStreamImpl(): Iterator<ItemStack> {
val manager = ModObjects.invMan ?: return Stream.empty() val manager = ModObjects.invMan ?: return emptyIterator()
val container = if (this !is ServerPlayer) { val container = if (this !is ServerPlayer) {
manager.getCosArmorInventoryClient(uuid) manager.getCosArmorInventoryClient(uuid)
@ -88,7 +90,7 @@ private fun Player.cosmeticArmorStreamImpl(): Stream<out ItemStack> {
manager.getCosArmorInventory(uuid) manager.getCosArmorInventory(uuid)
} }
return (container as Container).stream() return (container as Container).iterator()
} }
fun Player.cosmeticArmorAwareStream(): Stream<out AwareItemStack> { fun Player.cosmeticArmorAwareStream(): Stream<out AwareItemStack> {

View File

@ -11,8 +11,11 @@ import net.minecraftforge.network.PacketDistributor
import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.matteryPlayer import ru.dbotthepony.mc.otm.capability.matteryPlayer
import ru.dbotthepony.mc.otm.container.awareStream import ru.dbotthepony.mc.otm.container.awareStream
import ru.dbotthepony.mc.otm.container.iterator
import ru.dbotthepony.mc.otm.container.stream import ru.dbotthepony.mc.otm.container.stream
import ru.dbotthepony.mc.otm.core.collect.AwareItemStack import ru.dbotthepony.mc.otm.core.collect.AwareItemStack
import ru.dbotthepony.mc.otm.core.collect.concatIterators
import ru.dbotthepony.mc.otm.core.collect.emptyIterator
import ru.dbotthepony.mc.otm.core.orNull import ru.dbotthepony.mc.otm.core.orNull
import ru.dbotthepony.mc.otm.menu.PlayerSlot import ru.dbotthepony.mc.otm.menu.PlayerSlot
import top.theillusivec4.curios.api.CuriosApi import top.theillusivec4.curios.api.CuriosApi
@ -85,27 +88,27 @@ val Player.curiosSlots: List<PlayerSlot<Slot, Slot>> get() {
return getCuriosSlotsImpl() return getCuriosSlotsImpl()
} }
private fun Player.curiosStreamImpl(includeCosmetics: Boolean): Stream<out ItemStack> { private fun Player.curiosStreamImpl(includeCosmetics: Boolean): Iterator<ItemStack> {
val handler = getCapability(MatteryCapability.CURIOS_INVENTORY).orNull() ?: return Stream.empty() val handler = getCapability(MatteryCapability.CURIOS_INVENTORY).orNull() ?: return emptyIterator()
val result = ArrayList<Stream<out ItemStack>>() val result = ArrayList<Iterator<ItemStack>>()
for ((identifier, curio) in handler.curios) { for ((identifier, curio) in handler.curios) {
result.add(curio.stacks.stream()) result.add(curio.stacks.iterator())
if (includeCosmetics && curio.hasCosmetic()) { if (includeCosmetics && curio.hasCosmetic()) {
result.add(curio.cosmeticStacks.stream()) result.add(curio.cosmeticStacks.iterator())
} }
} }
return result.stream().flatMap { it } return concatIterators(result)
} }
fun Player.curiosStream(includeCosmetics: Boolean = true): Stream<out ItemStack> { fun Player.curiosStream(includeCosmetics: Boolean = true): Iterator<ItemStack> {
return Stream.empty() return emptyIterator()
if (!isCuriosLoaded) { if (!isCuriosLoaded) {
return Stream.empty() return emptyIterator()
} }
return curiosStreamImpl(includeCosmetics) return curiosStreamImpl(includeCosmetics)

View File

@ -6,6 +6,8 @@ import net.minecraft.world.item.ItemStack
import net.minecraftforge.items.IItemHandler import net.minecraftforge.items.IItemHandler
import ru.dbotthepony.mc.otm.core.collect.AwareItemStack import ru.dbotthepony.mc.otm.core.collect.AwareItemStack
import ru.dbotthepony.mc.otm.core.collect.ItemHandlerItemStackEntry import ru.dbotthepony.mc.otm.core.collect.ItemHandlerItemStackEntry
import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.isNotEmpty
import java.util.* import java.util.*
import java.util.stream.Stream import java.util.stream.Stream
import java.util.stream.StreamSupport import java.util.stream.StreamSupport
@ -48,7 +50,7 @@ class ItemHandlerAwareSpliterator(private val handler: IItemHandler, offset: Int
} }
} }
fun IItemHandler.iterator(): Iterator<ItemStack> = Spliterators.iterator(spliterator()) fun IItemHandler.iterator(): Iterator<ItemStack> = Spliterators.iterator(spliterator()).filter { it.isNotEmpty }
fun IItemHandler.spliterator(): Spliterator<out ItemStack> = ItemHandlerSpliterator(this) fun IItemHandler.spliterator(): Spliterator<out ItemStack> = ItemHandlerSpliterator(this)
fun IItemHandler.awareSpliterator(): Spliterator<out AwareItemStack> = ItemHandlerAwareSpliterator(this) fun IItemHandler.awareSpliterator(): Spliterator<out AwareItemStack> = ItemHandlerAwareSpliterator(this)
fun IItemHandler.stream(): Stream<out ItemStack> = StreamSupport.stream(spliterator(), false) fun IItemHandler.stream(): Stream<out ItemStack> = StreamSupport.stream(spliterator(), false)

View File

@ -0,0 +1,31 @@
package ru.dbotthepony.mc.otm.core.collect
import it.unimi.dsi.fastutil.Hash
import java.lang.ref.Reference
object ReferenceHashStrategy : Hash.Strategy<Any?> {
@Suppress("UNCHECKED_CAST")
override fun equals(a: Any?, b: Any?): Boolean {
if (a === b) return true
if (a is Reference<*>) {
if (a.refersTo(null) || b == null) return false
if (b is Reference<*>) {
if (b.refersTo(null)) return false
return (b as Reference<Any>).refersTo(a.get() ?: return false)
} else {
return (a as Reference<Any>).refersTo(b)
}
} else if (b is Reference<*>) {
if (b.refersTo(null) || a == null) return false
return (b as Reference<Any>).refersTo(a)
}
return a == b
}
override fun hashCode(o: Any?): Int {
return o.hashCode()
}
}

View File

@ -23,6 +23,7 @@ import java.util.stream.Collector
*/ */
class FilteringIterator<T>(private val parent: Iterator<T>, private val predicate: Predicate<in T>) : MutableIterator<T> { class FilteringIterator<T>(private val parent: Iterator<T>, private val predicate: Predicate<in T>) : MutableIterator<T> {
private var foundValue: Any? = Companion private var foundValue: Any? = Companion
private var returned = false
override fun hasNext(): Boolean { override fun hasNext(): Boolean {
if (foundValue === Companion) { if (foundValue === Companion) {
@ -58,16 +59,14 @@ class FilteringIterator<T>(private val parent: Iterator<T>, private val predicat
} }
this.foundValue = Companion this.foundValue = Companion
returned = true
return foundValue as T return foundValue as T
} }
override fun remove() { override fun remove() {
if (foundValue === Companion) { if (!returned) throw NoSuchElementException()
throw NoSuchElementException()
}
(parent as MutableIterator<T>).remove() (parent as MutableIterator<T>).remove()
foundValue = Companion returned = false
} }
private companion object private companion object
@ -197,6 +196,10 @@ fun <T> concatIterators(a: Iterator<T>): MutableIterator<T> {
return a as MutableIterator<T> return a as MutableIterator<T>
} }
fun <T> concatIterators(iterators: Iterable<Iterator<T>>): MutableIterator<T> {
return iterators.iterator().flatMap { it }
}
fun <T> concatIterators(vararg iterators: Iterator<T>): MutableIterator<T> { fun <T> concatIterators(vararg iterators: Iterator<T>): MutableIterator<T> {
return iterators.iterator().flatMap { it } return iterators.iterator().flatMap { it }
} }
@ -243,7 +246,7 @@ fun <T> Iterator<T>.reduce(identity: T, reducer: (T, T) -> T): T {
} }
fun <T> Iterator<T>.reduce(identity: T, reducer: BinaryOperator<T>): T = reduce(identity, reducer::apply) fun <T> Iterator<T>.reduce(identity: T, reducer: BinaryOperator<T>): T = reduce(identity, reducer::apply)
fun <T> Iterator<T?>.filterNotNull(): Iterator<T> = filter { it != null } as Iterator<T> fun <T> Iterator<T?>.filterNotNull(): MutableIterator<T> = filter { it != null } as MutableIterator<T>
fun <T> Iterator<T>.any(predicate: Predicate<in T>): Boolean { fun <T> Iterator<T>.any(predicate: Predicate<in T>): Boolean {
for (value in this) { for (value in this) {
@ -363,3 +366,7 @@ fun <T> Iterator<T>.maybe(): T? {
else else
null null
} }
fun <T> emptyIterator(): MutableIterator<T> {
return ObjectIterators.emptyIterator()
}

View File

@ -1,53 +1,76 @@
package ru.dbotthepony.mc.otm.core.collect package ru.dbotthepony.mc.otm.core.collect
import java.util.WeakHashMap import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet
import ru.dbotthepony.mc.otm.core.util.HashedWeakReference
import java.lang.ref.ReferenceQueue
/**
* Wrapper around [WeakHashMap] to behave like set
*/
class WeakHashSet<E : Any> : MutableSet<E> { class WeakHashSet<E : Any> : MutableSet<E> {
private val backing = WeakHashMap<E, Unit>() private val queue = ReferenceQueue<E>()
private val backing = ObjectOpenCustomHashSet<Any>(ReferenceHashStrategy)
private fun purge() {
var next = queue.poll()
while (next != null) {
backing.remove(next)
next = queue.poll()
}
}
override fun add(element: E): Boolean { override fun add(element: E): Boolean {
return backing.put(element, Unit) == null purge()
if (element in backing) return false
return backing.add(HashedWeakReference(element, queue))
} }
override fun addAll(elements: Collection<E>): Boolean { override fun addAll(elements: Collection<E>): Boolean {
return elements.count(::add) > 0 var any = false
elements.forEach { any = add(it) || any }
return any
} }
override fun clear() { override fun clear() {
backing.clear() backing.clear()
while (queue.poll() != null) {}
} }
override fun iterator(): MutableIterator<E> { override fun iterator(): MutableIterator<E> {
return backing.keys.iterator() purge()
return backing.iterator().map { (it as HashedWeakReference<E>).get() }.filterNotNull()
} }
override fun remove(element: E): Boolean { override fun remove(element: E): Boolean {
return backing.remove(element) != null purge()
return backing.remove(element)
} }
override fun removeAll(elements: Collection<E>): Boolean { override fun removeAll(elements: Collection<E>): Boolean {
return backing.keys.removeAll(elements) purge()
return backing.removeAll(elements)
} }
override fun retainAll(elements: Collection<E>): Boolean { override fun retainAll(elements: Collection<E>): Boolean {
return backing.keys.retainAll(elements) purge()
return backing.retainAll(elements)
} }
override val size: Int override val size: Int get() {
get() = backing.size purge()
return backing.size
}
override fun contains(element: E): Boolean { override fun contains(element: E): Boolean {
return backing.get(element) === Unit purge()
return backing.contains(element)
} }
override fun containsAll(elements: Collection<E>): Boolean { override fun containsAll(elements: Collection<E>): Boolean {
return elements.all(::contains) purge()
return backing.containsAll(elements)
} }
override fun isEmpty(): Boolean { override fun isEmpty(): Boolean {
purge()
return backing.isEmpty() return backing.isEmpty()
} }
} }

View File

@ -665,7 +665,7 @@ class Decimal private constructor(val mag: BigInteger, marker: Nothing?) : Numbe
@JvmStatic @JvmStatic
fun read(buff: FriendlyByteBuf): Decimal { fun read(buff: FriendlyByteBuf): Decimal {
return Decimal(BigInteger(buff.readByteArray())) return Decimal(BigInteger(buff.readByteArray()), null)
} }
/** /**

View File

@ -0,0 +1,42 @@
package ru.dbotthepony.mc.otm.core.util
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.locks.ReentrantLock
class AtomicallyInvalidatedLazy<V>(private val invalidator: AtomicInteger, private val initializer: () -> V) : Lazy<V> {
@Volatile
private var thisCounter = -1
@Volatile
private var stored: Any? = Companion
private val lock = ReentrantLock()
override val value: V get() {
if (thisCounter != invalidator.get()) {
lock.lock()
this.stored = Companion
thisCounter = invalidator.get()
lock.unlock()
}
var stored = stored
if (stored !== Companion)
return stored as V
lock.lock()
try {
stored = initializer.invoke()
this.stored = stored
return stored
} finally {
lock.unlock()
}
}
override fun isInitialized(): Boolean {
return stored !== Companion && thisCounter == invalidator.get()
}
private companion object
}

View File

@ -0,0 +1,28 @@
package ru.dbotthepony.mc.otm.core.util
import java.lang.ref.ReferenceQueue
import java.lang.ref.WeakReference
/**
* [WeakReference], but with [hashCode] overridden with hash of referent object
*/
@Suppress("EqualsOrHashCode")
class HashedWeakReference<T : Any> : WeakReference<T> {
constructor(value: T) : super(value) {
hash = value.hashCode()
}
constructor(value: T, queue: ReferenceQueue<T>) : super(value, queue) {
hash = value.hashCode()
}
private val hash: Int
override fun hashCode(): Int {
return hash
}
override fun toString(): String {
return "HashedWeakReference[hash=$hash]"
}
}

View File

@ -7,7 +7,8 @@ import net.minecraft.world.level.storage.loot.LootContext
import net.minecraft.world.level.storage.loot.parameters.LootContextParams import net.minecraft.world.level.storage.loot.parameters.LootContextParams
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition import net.minecraft.world.level.storage.loot.predicates.LootItemCondition
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType
import ru.dbotthepony.mc.otm.capability.itemsStream import ru.dbotthepony.mc.otm.capability.items
import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.data.Codec2Serializer import ru.dbotthepony.mc.otm.data.Codec2Serializer
import ru.dbotthepony.mc.otm.data.get import ru.dbotthepony.mc.otm.data.get
import ru.dbotthepony.mc.otm.registry.MLootItemConditions import ru.dbotthepony.mc.otm.registry.MLootItemConditions
@ -19,7 +20,7 @@ data class ItemInInventoryCondition(
val matchCosmetics: Boolean = true, val matchCosmetics: Boolean = true,
) : LootItemCondition, LootItemCondition.Builder { ) : LootItemCondition, LootItemCondition.Builder {
override fun test(t: LootContext): Boolean { override fun test(t: LootContext): Boolean {
val matches = t[LootContextParams.LAST_DAMAGE_PLAYER]?.itemsStream(matchCosmetics)?.filter { val matches = t[LootContextParams.LAST_DAMAGE_PLAYER]?.items(matchCosmetics)?.filter {
if (it.isEmpty) { if (it.isEmpty) {
return@filter false return@filter false
} }

View File

@ -1,17 +1,18 @@
package ru.dbotthepony.mc.otm.item package ru.dbotthepony.mc.otm.item
import it.unimi.dsi.fastutil.ints.Int2ObjectFunction import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
import it.unimi.dsi.fastutil.ints.IntOpenHashSet
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.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Rarity
import net.minecraft.world.item.TooltipFlag
import net.minecraft.world.level.Level import net.minecraft.world.level.Level
import net.minecraft.world.level.saveddata.SavedData
import net.minecraftforge.client.event.ClientPlayerNetworkEvent import net.minecraftforge.client.event.ClientPlayerNetworkEvent
import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.capabilities.ForgeCapabilities import net.minecraftforge.common.capabilities.ForgeCapabilities
@ -21,63 +22,140 @@ import net.minecraftforge.event.TickEvent
import net.minecraftforge.event.TickEvent.ServerTickEvent import net.minecraftforge.event.TickEvent.ServerTickEvent
import net.minecraftforge.network.NetworkEvent import net.minecraftforge.network.NetworkEvent
import net.minecraftforge.registries.ForgeRegistries import net.minecraftforge.registries.ForgeRegistries
import net.minecraftforge.registries.ForgeRegistry import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.* import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.* import ru.dbotthepony.mc.otm.capability.allItems
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.getBarColor import ru.dbotthepony.mc.otm.capability.energy.getBarColor
import ru.dbotthepony.mc.otm.capability.energy.getBarWidth import ru.dbotthepony.mc.otm.capability.energy.getBarWidth
import ru.dbotthepony.mc.otm.capability.isMekanismLoaded
import ru.dbotthepony.mc.otm.capability.matteryEnergy
import ru.dbotthepony.mc.otm.compat.mekanism.Mattery2MekanismEnergyWrapper import ru.dbotthepony.mc.otm.compat.mekanism.Mattery2MekanismEnergyWrapper
import ru.dbotthepony.mc.otm.config.EnergyBalanceValues import ru.dbotthepony.mc.otm.config.EnergyBalanceValues
import ru.dbotthepony.mc.otm.core.* import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.getID
import ru.dbotthepony.mc.otm.core.getValue
import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.getDecimal
import ru.dbotthepony.mc.otm.core.math.readDecimal import ru.dbotthepony.mc.otm.core.math.readDecimal
import ru.dbotthepony.mc.otm.core.math.set
import ru.dbotthepony.mc.otm.core.math.writeDecimal import ru.dbotthepony.mc.otm.core.math.writeDecimal
import ru.dbotthepony.mc.otm.core.nbt.getUUIDSafe
import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.orThrow
import ru.dbotthepony.mc.otm.core.tagNotNull
import ru.dbotthepony.mc.otm.core.util.formatPower import ru.dbotthepony.mc.otm.core.util.formatPower
import ru.dbotthepony.mc.otm.isClientThread
import ru.dbotthepony.mc.otm.isServerThread
import ru.dbotthepony.mc.otm.lazyPerServer
import ru.dbotthepony.mc.otm.network.GenericNetworkChannel 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 java.util.*
import java.util.function.Function
import java.util.function.Supplier import java.util.function.Supplier
import kotlin.collections.MutableList
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.forEach
import kotlin.collections.iterator
import kotlin.collections.set
class QuantumBatteryItem : Item { class QuantumBatteryItem(val savedataID: String, val balanceValues: EnergyBalanceValues?) : Item(Properties().stacksTo(1).rarity(if (balanceValues == null) Rarity.EPIC else Rarity.UNCOMMON)) {
class Data( val isCreative = balanceValues == null
val parent: SavedCountingMap<Data>?,
val index: Int = -1,
energy: Decimal = Decimal.ZERO,
passed: Decimal = Decimal.ZERO,
received: Decimal = Decimal.ZERO,
) {
constructor(
energy: Decimal = Decimal.ZERO,
passed: Decimal = Decimal.ZERO,
received: Decimal = Decimal.ZERO,
) : this(null, -1, energy, passed, received)
var energy: Decimal = energy interface IValues {
set(value) { val uuid: UUID
if (field != value) { var energy: Decimal
var passed: Decimal
var received: Decimal
val isServer: Boolean
fun serialize(): CompoundTag {
return CompoundTag().also {
it["energy"] = energy
it["passed"] = passed
it["received"] = received
}
}
fun deserialize(nbt: CompoundTag) {
energy = nbt.getDecimal("energy")
passed = nbt.getDecimal("passed")
received = nbt.getDecimal("received")
}
}
class UnboundValues(override val uuid: UUID = UUID.randomUUID()) : IValues {
override var energy: Decimal = Decimal.ZERO
override var passed: Decimal = Decimal.ZERO
override var received: Decimal = Decimal.ZERO
override val isServer: Boolean
get() = false
}
class Data() : SavedData() {
constructor(nbt: CompoundTag) : this() {
load(nbt)
}
inner class Values(override val uuid: UUID) : IValues {
override var energy = Decimal.ZERO
set(value) {
field = value field = value
parent?.isDirty = true isDirty = true
} }
override var passed = Decimal.ZERO
set(value) {
field = value
isDirty = true
}
override var received = Decimal.ZERO
set(value) {
field = value
isDirty = true
}
override val isServer: Boolean
get() = true
}
private val data = Object2ObjectOpenHashMap<UUID, Values>()
fun values(uuid: UUID): Values {
return data.computeIfAbsent(uuid, Function { Values(uuid) })
}
fun values(): Values {
return values(UUID.randomUUID())
}
override fun save(nbt: CompoundTag): CompoundTag {
for ((key, values) in data) {
nbt[key.toString()] = values.serialize()
} }
var passed: Decimal = passed return nbt
set(value) { }
if (field != value) {
field = value
parent?.isDirty = true
}
}
var received: Decimal = received fun load(nbt: CompoundTag) {
set(value) { data.clear()
if (field != value) {
field = value for (key in nbt.allKeys) {
parent?.isDirty = true val id = UUID.fromString(key)
} data[id] = Values(id).also { it.deserialize(nbt.getCompound(key)) }
} }
}
}
val clientData = Object2ObjectOpenHashMap<UUID, IValues>()
val serverData: Data by lazyPerServer {
it.overworld().dataStorage.computeIfAbsent(::Data, ::Data, "otm_$savedataID")
} }
private inner class Power(private val stack: ItemStack) : IMatteryEnergyStorage, ICapabilityProvider { private inner class Power(private val stack: ItemStack) : IMatteryEnergyStorage, ICapabilityProvider {
@ -87,7 +165,18 @@ class QuantumBatteryItem : Item {
override val energyFlow: FlowDirection override val energyFlow: FlowDirection
get() = FlowDirection.BI_DIRECTIONAL get() = FlowDirection.BI_DIRECTIONAL
var data = Data() var values: IValues = UnboundValues()
fun updateValues() {
if (!values.isServer && isServerThread()) {
values = serverData.values(stack.tag?.getUUIDSafe("id") ?: UUID.randomUUID().also { stack.tagNotNull["id"] = it })
} else if (isClientThread()) {
val id = stack.tag?.getUUIDSafe("id") ?: return
if (values.uuid != id)
values = clientData.computeIfAbsent(id, Function { UnboundValues(it) })
}
}
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) {
@ -100,253 +189,107 @@ class QuantumBatteryItem : Item {
} }
override fun extractEnergy(howMuch: Decimal, simulate: Boolean): Decimal { override fun extractEnergy(howMuch: Decimal, simulate: Boolean): Decimal {
if (howMuch.isNegative) { if (!howMuch.isPositive)
return Decimal.ZERO return Decimal.ZERO
}
if (data.parent == null && isServerThread()) { updateValues()
determineQuantumLink()
if (data.parent == null) { if (!values.isServer && !simulate)
return Decimal.ZERO return Decimal.ZERO
}
}
if (isCreative) { if (balanceValues == null) {
val newEnergy = (data.energy - howMuch).moreThanZero() val newEnergy = (values.energy - howMuch).moreThanZero()
val diff = data.energy - newEnergy val diff = values.energy - newEnergy
if (!simulate) { if (!simulate) {
data.energy = newEnergy values.energy = newEnergy
data.passed += diff values.passed += diff
}
return diff
} else {
val newEnergy = (values.energy - howMuch.coerceAtMost(balanceValues.energyThroughput)).moreThanZero()
val diff = values.energy - newEnergy
if (!simulate) {
values.energy = newEnergy
values.passed += diff
} }
return diff return diff
} }
val newEnergy = (data.energy - howMuch.coerceAtMost(throughput!!)).moreThanZero()
val diff = data.energy - newEnergy
if (!simulate) {
data.energy = newEnergy
data.passed += diff
}
return diff
} }
override fun receiveEnergy(howMuch: Decimal, simulate: Boolean): Decimal { override fun receiveEnergy(howMuch: Decimal, simulate: Boolean): Decimal {
if (howMuch.isNegative) { if (!howMuch.isPositive)
return Decimal.ZERO return Decimal.ZERO
}
if (data.parent == null && isServerThread()) { updateValues()
determineQuantumLink()
if (data.parent == null) { if (!values.isServer && !simulate)
return Decimal.ZERO return Decimal.ZERO
}
}
if (isCreative) { if (balanceValues == null) {
if (!simulate) { if (!simulate) {
data.energy += howMuch values.energy += howMuch
data.received += howMuch values.received += howMuch
} }
return howMuch return howMuch
} } else if (values.energy >= balanceValues.energyCapacity) {
if (data.energy >= capacity!!) {
return Decimal.ZERO return Decimal.ZERO
} else {
val newEnergy = (values.energy + howMuch.coerceAtMost(balanceValues.energyThroughput)).coerceAtMost(balanceValues.energyCapacity)
val diff = newEnergy - values.energy
if (!simulate) {
values.energy = newEnergy
values.received += diff
}
return diff
} }
val newEnergy = (data.energy + howMuch.coerceAtMost(throughput!!)).coerceAtMost(capacity!!)
val diff = newEnergy - data.energy
if (!simulate) {
data.energy = newEnergy
data.received += diff
}
return diff
} }
override var batteryLevel: Decimal override var batteryLevel: Decimal
get() { get() {
if (data.parent == null) { updateValues()
determineQuantumLink() return values.energy
}
if (isClientThread()) {
return clientPowerMap[data.index]?.energy ?: data.energy
}
return data.energy
} }
set(value) { set(value) {
if (data.parent == null) { updateValues()
determineQuantumLink() values.energy = value
}
if (isClientThread()) {
val energy1 = clientPowerMap[data.index]
if (energy1 != null) {
energy1.energy = value
} else {
data.energy = value
}
return
}
data.energy = value
} }
val passed: Decimal val passed: Decimal get() {
get() { updateValues()
if (data.parent == null) { return values.passed
determineQuantumLink()
}
if (isClientThread()) {
return clientPowerMap[data.index]?.passed ?: data.passed
}
return data.passed
} }
val received: Decimal val received: Decimal get() {
get() { updateValues()
if (data.parent == null) { return values.received
determineQuantumLink()
}
if (isClientThread()) {
return clientPowerMap[data.index]?.received ?: data.received
}
return data.received
} }
override val maxBatteryLevel: Decimal override val maxBatteryLevel: Decimal
get() = capacity ?: (batteryLevel + Decimal.LONG_MAX_VALUE) get() = balanceValues?.energyCapacity ?: (batteryLevel + Decimal.LONG_MAX_VALUE)
override val missingPower: Decimal override val missingPower: Decimal
get() = if (isCreative) Decimal.LONG_MAX_VALUE else super.missingPower get() = if (isCreative) Decimal.LONG_MAX_VALUE else super.missingPower
private fun determineQuantumLink() {
if (data.parent == null && isServerThread()) {
val existing = stack.tag?.getInt("link_id")
if (existing == null) {
val old = data
data = saveData!!.factorize()
data.energy = old.energy
stack.tagNotNull["link_id"] = data.index
} else {
data = saveData?.computeIfAbsent(existing) ?: Data(null, existing, data.energy, data.passed, data.received)
}
} else if (!isServerThread()) {
// client ?
val existing = stack.tag?.getInt("link_id") ?: return
if (existing != data.index) {
data = Data(data.parent, existing, data.energy, data.passed, data.received)
}
}
}
fun determineQuantumLinkWeak() {
if (data.parent == null && isServerThread()) {
val existing = stack.tag?.getInt("link_id")
if (existing != null) {
data = saveData?.computeIfAbsent(existing) ?: Data(null, existing, data.energy, data.passed, data.received)
}
} else if (!isServerThread()) {
val existing = stack.tag?.getInt("link_id") ?: return
if (existing != data.index) {
data = Data(data.parent, existing, data.energy, data.passed, data.received)
}
}
}
}
val isCreative: Boolean
private val _capacity: () -> Decimal?
private val _throughput: () -> Decimal?
val capacity get() = _capacity.invoke()
val throughput get() = _throughput.invoke()
val saveDataID: String
val saveData: SavedCountingMap<Data>? get() {
if (isServerThread()) {
return MINECRAFT_SERVER.overworld().dataStorage.computeIfAbsent({
SavedCountingMap(Companion::storeValue, Companion::loadValue, ::Data).load(it)
}, {
SavedCountingMap(Companion::storeValue, Companion::loadValue, ::Data)
}, saveDataID) ?: throw NullPointerException("Unable to get save data for $this in ${MINECRAFT_SERVER.overworld()}")
}
return null
}
data class ClientData(
var energy: Decimal = Decimal.ZERO,
var passed: Decimal = Decimal.ZERO,
var received: Decimal = Decimal.ZERO,
)
val clientPowerMap: Int2ObjectOpenHashMap<ClientData> by lazy {
check(isClient) { "Invalid side" }
Int2ObjectOpenHashMap()
}
constructor(saveDataID: String) : super(Properties().stacksTo(1).rarity(Rarity.EPIC)) {
isCreative = true
_capacity = { null }
_throughput = { null }
this.saveDataID = "otm_$saveDataID".intern()
}
constructor(saveDataID: String, capacity: Decimal, io: Decimal) : super(Properties().stacksTo(1)) {
isCreative = false
_capacity = { capacity }
_throughput = { io }
this.saveDataID = "otm_$saveDataID".intern()
}
constructor(saveDataID: String, values: EnergyBalanceValues) : super(Properties().stacksTo(1)) {
isCreative = false
_capacity = values::energyCapacity
_throughput = values::energyThroughput
this.saveDataID = "otm_$saveDataID".intern()
} }
override fun isBarVisible(p_150899_: ItemStack): Boolean { override fun isBarVisible(p_150899_: ItemStack): Boolean {
if (isCreative) if (isCreative) return false
return false
return p_150899_.matteryEnergy != null return p_150899_.matteryEnergy != null
} }
override fun getBarWidth(p_150900_: ItemStack): Int { override fun getBarWidth(p_150900_: ItemStack): Int {
if (isCreative) if (isCreative) return 13
return 13
return p_150900_.matteryEnergy?.getBarWidth() ?: super.getBarWidth(p_150900_) return p_150900_.matteryEnergy?.getBarWidth() ?: super.getBarWidth(p_150900_)
} }
override fun getBarColor(p_150901_: ItemStack): Int { override fun getBarColor(p_150901_: ItemStack): Int {
if (isCreative) if (isCreative) return 0
return 0
return p_150901_.matteryEnergy?.getBarColor() ?: super.getBarColor(p_150901_) return p_150901_.matteryEnergy?.getBarColor() ?: super.getBarColor(p_150901_)
} }
@ -354,29 +297,19 @@ class QuantumBatteryItem : Item {
return Power(stack) return Power(stack)
} }
override fun appendHoverText( override fun appendHoverText(itemStack: ItemStack, level: Level?, components: MutableList<Component>, flags: TooltipFlag) {
itemStack: ItemStack, super.appendHoverText(itemStack, level, components, flags)
p_41422_: Level?,
components: MutableList<Component>,
p_41424_: TooltipFlag
) {
super.appendHoverText(itemStack, p_41422_, components, p_41424_)
val power = itemStack.getCapability(MatteryCapability.ENERGY).orThrow() as Power val power = itemStack.getCapability(MatteryCapability.ENERGY).orThrow() as Power
power.updateValues()
components.add(TranslatableComponent("otm.item.quantum_description").withStyle(ChatFormatting.DARK_GRAY)) components.add(TranslatableComponent("otm.item.quantum_description").withStyle(ChatFormatting.DARK_GRAY))
if (isCreative) { if (balanceValues == null) {
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))
} else { } else {
components.add(TranslatableComponent("otm.item.power.storage", power.batteryLevel.formatPower(), capacity!!.formatPower()).withStyle(ChatFormatting.GRAY)) components.add(TranslatableComponent("otm.item.power.storage", power.batteryLevel.formatPower(), balanceValues.energyCapacity.formatPower()).withStyle(ChatFormatting.GRAY))
components.add(TranslatableComponent("otm.item.power.throughput_mono", balanceValues.energyThroughput.formatPower()).withStyle(ChatFormatting.GRAY))
components.add(
TranslatableComponent(
"otm.item.power.throughput",
throughput!!.formatPower(),
throughput!!.formatPower()
).withStyle(ChatFormatting.GRAY))
} }
components.add(TranslatableComponent("otm.item.power.passed", power.passed.formatPower()).withStyle(ChatFormatting.GRAY)) components.add(TranslatableComponent("otm.item.power.passed", power.passed.formatPower()).withStyle(ChatFormatting.GRAY))
@ -387,18 +320,18 @@ class QuantumBatteryItem : Item {
components.add(TranslatableComponent("otm.item.quantum_battery.creative2").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)) components.add(TranslatableComponent("otm.item.quantum_link_id", power.values.uuid).withStyle(ChatFormatting.DARK_GRAY))
} }
companion object { companion object {
fun clientDisconnect(event: ClientPlayerNetworkEvent.LoggingOut) { fun clientDisconnect(event: ClientPlayerNetworkEvent.LoggingOut) {
ForgeRegistries.ITEMS.values.stream().forEach { if (it is QuantumBatteryItem) it.clientPowerMap.clear() } ForgeRegistries.ITEMS.values.forEach { if (it is QuantumBatteryItem) it.clientData.clear() }
} }
fun readPacket(buff: FriendlyByteBuf): ChargePacket { fun readPacket(buff: FriendlyByteBuf): ChargePacket {
return ChargePacket( return ChargePacket(
(ForgeRegistries.ITEMS as ForgeRegistry<Item>).getValue(buff.readInt()) as QuantumBatteryItem, ForgeRegistries.ITEMS.getValue(buff.readInt()) as QuantumBatteryItem,
buff.readInt(), buff.readUUID(),
buff.readDecimal(), buff.readDecimal(),
buff.readDecimal(), buff.readDecimal(),
buff.readDecimal(), buff.readDecimal(),
@ -408,54 +341,33 @@ class QuantumBatteryItem : Item {
fun tick(event: ServerTickEvent) { fun tick(event: ServerTickEvent) {
if (event.phase == TickEvent.Phase.END) { if (event.phase == TickEvent.Phase.END) {
for (ply in event.server.playerList.players) { for (ply in event.server.playerList.players) {
val networkedChannels = IntOpenHashSet(0) val networkedChannels = ObjectOpenHashSet<UUID>(0)
for (item in ply.allItemsStream().filter { !it.isEmpty && it.item is QuantumBatteryItem }) { for (item in ply.allItems().filter { it.isNotEmpty && it.item is QuantumBatteryItem }) {
val power = item.getCapability(MatteryCapability.ENERGY).orThrow() as Power val power = item.getCapability(MatteryCapability.ENERGY).orThrow() as Power
power.updateValues()
power.determineQuantumLinkWeak() if (power.values.isServer && networkedChannels.add(power.values.uuid)) {
GenericNetworkChannel.send(ply, ChargePacket(item.item as QuantumBatteryItem, power.values.uuid, power.batteryLevel, power.passed, power.received))
if (power.data.index < 0) {
continue
}
if (networkedChannels.add(power.data.index)) {
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, Decimal.deserializeNBT(tag))
} else if (tag is CompoundTag) {
return Data(parent, index, Decimal.deserializeNBT(tag["energy"]), Decimal.deserializeNBT(tag["passed"]), Decimal.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( class ChargePacket(
val type: QuantumBatteryItem, val type: QuantumBatteryItem,
val channel: Int, override val uuid: UUID,
val energy: Decimal, override var energy: Decimal,
val passed: Decimal, override var passed: Decimal,
val received: Decimal, override var received: Decimal,
) : MatteryPacket { ) : MatteryPacket, IValues {
override val isServer: Boolean
get() = false
override fun write(buff: FriendlyByteBuf) { override fun write(buff: FriendlyByteBuf) {
buff.writeInt((ForgeRegistries.ITEMS as ForgeRegistry<Item>).getID(type)) buff.writeInt(ForgeRegistries.ITEMS.getID(type))
buff.writeInt(channel) buff.writeUUID(uuid)
buff.writeDecimal(energy) buff.writeDecimal(energy)
buff.writeDecimal(passed) buff.writeDecimal(passed)
buff.writeDecimal(received) buff.writeDecimal(received)
@ -463,7 +375,7 @@ class QuantumBatteryItem : Item {
override fun play(context: Supplier<NetworkEvent.Context>) { override fun play(context: Supplier<NetworkEvent.Context>) {
context.packetHandled = true context.packetHandled = true
val data = type.clientPowerMap.computeIfAbsent(channel, Int2ObjectFunction { ClientData() }) val data = type.clientData.computeIfAbsent(uuid, Function { UnboundValues(it) })
data.energy = energy data.energy = energy
data.passed = passed data.passed = passed
data.received = received data.received = received

View File

@ -351,7 +351,7 @@ object MItems {
val QUANTUM_BATTERY: QuantumBatteryItem by registry.register(MNames.QUANTUM_BATTERY) { QuantumBatteryItem(MNames.QUANTUM_BATTERY, ItemsConfig.Batteries.QUANTUM_BATTERY) } val QUANTUM_BATTERY: QuantumBatteryItem by registry.register(MNames.QUANTUM_BATTERY) { QuantumBatteryItem(MNames.QUANTUM_BATTERY, ItemsConfig.Batteries.QUANTUM_BATTERY) }
val QUANTUM_CAPACITOR: QuantumBatteryItem by registry.register(MNames.QUANTUM_CAPACITOR) { QuantumBatteryItem(MNames.QUANTUM_CAPACITOR, ItemsConfig.Batteries.QUANTUM_CAPACITOR) } val QUANTUM_CAPACITOR: QuantumBatteryItem by registry.register(MNames.QUANTUM_CAPACITOR) { QuantumBatteryItem(MNames.QUANTUM_CAPACITOR, ItemsConfig.Batteries.QUANTUM_CAPACITOR) }
val QUANTUM_BATTERY_CREATIVE: QuantumBatteryItem by registry.register(MNames.QUANTUM_BATTERY_CREATIVE) { QuantumBatteryItem(MNames.QUANTUM_BATTERY_CREATIVE) } val QUANTUM_BATTERY_CREATIVE: QuantumBatteryItem by registry.register(MNames.QUANTUM_BATTERY_CREATIVE) { QuantumBatteryItem(MNames.QUANTUM_BATTERY_CREATIVE, null) }
val ZPM_BATTERY: ZPMItem by registry.register(MNames.ZPM_BATTERY) { ZPMItem() } val ZPM_BATTERY: ZPMItem by registry.register(MNames.ZPM_BATTERY) { ZPMItem() }
val PROCEDURAL_BATTERY: ProceduralBatteryItem by registry.register(MNames.PROCEDURAL_BATTERY) { ProceduralBatteryItem() } val PROCEDURAL_BATTERY: ProceduralBatteryItem by registry.register(MNames.PROCEDURAL_BATTERY) { ProceduralBatteryItem() }