Improved syncher API, proper history graph class
This commit is contained in:
parent
a06fd6a31a
commit
16f5f4c9c9
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
package ru.dbotthepony.mc.otm
|
package ru.dbotthepony.mc.otm
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.ThreadFactoryBuilder
|
||||||
import net.minecraft.client.server.IntegratedServer
|
import net.minecraft.client.server.IntegratedServer
|
||||||
import net.minecraft.core.HolderLookup
|
import net.minecraft.core.HolderLookup
|
||||||
import net.minecraft.server.MinecraftServer
|
import net.minecraft.server.MinecraftServer
|
||||||
@ -27,6 +28,7 @@ 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 java.lang.ref.Cleaner
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
@ -38,6 +40,8 @@ private val postWorldTick = WeakHashMap<Level, TickList>()
|
|||||||
private val clientThreads = WeakHashSet<Thread>()
|
private val clientThreads = WeakHashSet<Thread>()
|
||||||
private val serverThreads = WeakHashSet<Thread>()
|
private val serverThreads = WeakHashSet<Thread>()
|
||||||
|
|
||||||
|
val OTM_CLEANER: Cleaner = Cleaner.create(ThreadFactoryBuilder().setNameFormat("OverdriveThatMatters-Cleaner-%d").build())
|
||||||
|
|
||||||
private val serverCounter = AtomicInteger()
|
private val serverCounter = AtomicInteger()
|
||||||
private var _server: MinecraftServer? = null
|
private var _server: MinecraftServer? = null
|
||||||
val isClient: Boolean by lazy { FMLLoader.getDist() == Dist.CLIENT }
|
val isClient: Boolean by lazy { FMLLoader.getDist() == Dist.CLIENT }
|
||||||
|
@ -2,10 +2,9 @@ package ru.dbotthepony.mc.otm.android
|
|||||||
|
|
||||||
import net.minecraft.core.HolderLookup
|
import net.minecraft.core.HolderLookup
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
|
||||||
import net.neoforged.neoforge.common.util.INBTSerializable
|
import net.neoforged.neoforge.common.util.INBTSerializable
|
||||||
import net.neoforged.neoforge.event.entity.living.LivingIncomingDamageEvent
|
import net.neoforged.neoforge.event.entity.living.LivingIncomingDamageEvent
|
||||||
import ru.dbotthepony.mc.otm.network.DelegateSyncher
|
import ru.dbotthepony.mc.otm.network.syncher.Syncher
|
||||||
import ru.dbotthepony.kommons.util.getValue
|
import ru.dbotthepony.kommons.util.getValue
|
||||||
import ru.dbotthepony.kommons.util.setValue
|
import ru.dbotthepony.kommons.util.setValue
|
||||||
import ru.dbotthepony.mc.otm.capability.MatteryPlayer
|
import ru.dbotthepony.mc.otm.capability.MatteryPlayer
|
||||||
@ -13,7 +12,7 @@ import ru.dbotthepony.mc.otm.core.nbt.set
|
|||||||
|
|
||||||
abstract class AndroidFeature(val type: AndroidFeatureType<*>, val android: MatteryPlayer) : INBTSerializable<CompoundTag> {
|
abstract class AndroidFeature(val type: AndroidFeatureType<*>, val android: MatteryPlayer) : INBTSerializable<CompoundTag> {
|
||||||
val ply get() = android.ply
|
val ply get() = android.ply
|
||||||
val syncher = DelegateSyncher()
|
val syncher = Syncher()
|
||||||
val syncherRemote = syncher.Remote()
|
val syncherRemote = syncher.Remote()
|
||||||
|
|
||||||
open var level by syncher.int(setter = setter@{ field, value ->
|
open var level by syncher.int(setter = setter@{ field, value ->
|
||||||
|
@ -10,7 +10,7 @@ import net.minecraft.world.entity.player.Player
|
|||||||
import net.neoforged.bus.api.Event
|
import net.neoforged.bus.api.Event
|
||||||
import net.neoforged.neoforge.common.NeoForge
|
import net.neoforged.neoforge.common.NeoForge
|
||||||
import net.neoforged.neoforge.common.util.INBTSerializable
|
import net.neoforged.neoforge.common.util.INBTSerializable
|
||||||
import ru.dbotthepony.mc.otm.network.DelegateSyncher
|
import ru.dbotthepony.mc.otm.network.syncher.Syncher
|
||||||
import ru.dbotthepony.kommons.util.getValue
|
import ru.dbotthepony.kommons.util.getValue
|
||||||
import ru.dbotthepony.kommons.util.setValue
|
import ru.dbotthepony.kommons.util.setValue
|
||||||
import ru.dbotthepony.mc.otm.OverdriveThatMatters
|
import ru.dbotthepony.mc.otm.OverdriveThatMatters
|
||||||
@ -48,7 +48,7 @@ class AndroidResearch(val type: AndroidResearchType, val capability: MatteryPlay
|
|||||||
|
|
||||||
val ply: Player get() = capability.ply
|
val ply: Player get() = capability.ply
|
||||||
|
|
||||||
val syncher = DelegateSyncher()
|
val syncher = Syncher()
|
||||||
val syncherRemote = syncher.Remote()
|
val syncherRemote = syncher.Remote()
|
||||||
|
|
||||||
var isResearched by syncher.boolean()
|
var isResearched by syncher.boolean()
|
||||||
|
@ -37,7 +37,6 @@ import net.neoforged.neoforge.event.server.ServerStoppingEvent
|
|||||||
import net.neoforged.neoforge.event.tick.LevelTickEvent
|
import net.neoforged.neoforge.event.tick.LevelTickEvent
|
||||||
import net.neoforged.neoforge.network.PacketDistributor
|
import net.neoforged.neoforge.network.PacketDistributor
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import ru.dbotthepony.mc.otm.network.DelegateSyncher
|
|
||||||
import ru.dbotthepony.kommons.util.Listenable
|
import ru.dbotthepony.kommons.util.Listenable
|
||||||
import ru.dbotthepony.mc.otm.SERVER_IS_LIVE
|
import ru.dbotthepony.mc.otm.SERVER_IS_LIVE
|
||||||
import ru.dbotthepony.mc.otm.block.INeighbourChangeListener
|
import ru.dbotthepony.mc.otm.block.INeighbourChangeListener
|
||||||
@ -54,6 +53,7 @@ import ru.dbotthepony.mc.otm.core.util.Savetables
|
|||||||
import ru.dbotthepony.mc.otm.core.util.TickList
|
import ru.dbotthepony.mc.otm.core.util.TickList
|
||||||
import ru.dbotthepony.mc.otm.core.util.countingLazy
|
import ru.dbotthepony.mc.otm.core.util.countingLazy
|
||||||
import ru.dbotthepony.mc.otm.network.BlockEntitySyncPacket
|
import ru.dbotthepony.mc.otm.network.BlockEntitySyncPacket
|
||||||
|
import ru.dbotthepony.mc.otm.network.syncher.Syncher
|
||||||
import ru.dbotthepony.mc.otm.onceServer
|
import ru.dbotthepony.mc.otm.onceServer
|
||||||
import ru.dbotthepony.mc.otm.registry.MBlocks
|
import ru.dbotthepony.mc.otm.registry.MBlocks
|
||||||
import ru.dbotthepony.mc.otm.sometimeServer
|
import ru.dbotthepony.mc.otm.sometimeServer
|
||||||
@ -369,8 +369,8 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val syncher = DelegateSyncher()
|
val syncher = Syncher()
|
||||||
private val synchers = Object2ObjectArrayMap<ServerPlayer, DelegateSyncher.Remote>()
|
private val synchers = Object2ObjectArrayMap<ServerPlayer, Syncher.Remote>()
|
||||||
|
|
||||||
override fun setLevel(level: Level) {
|
override fun setLevel(level: Level) {
|
||||||
val old = this.level
|
val old = this.level
|
||||||
|
@ -3,7 +3,7 @@ package ru.dbotthepony.mc.otm.block.entity
|
|||||||
import net.minecraft.core.HolderLookup
|
import net.minecraft.core.HolderLookup
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.neoforged.neoforge.common.util.INBTSerializable
|
import net.neoforged.neoforge.common.util.INBTSerializable
|
||||||
import ru.dbotthepony.mc.otm.network.DelegateSyncher
|
import ru.dbotthepony.mc.otm.network.syncher.Syncher
|
||||||
import ru.dbotthepony.kommons.util.Listenable
|
import ru.dbotthepony.kommons.util.Listenable
|
||||||
import ru.dbotthepony.kommons.util.getValue
|
import ru.dbotthepony.kommons.util.getValue
|
||||||
import ru.dbotthepony.kommons.util.setValue
|
import ru.dbotthepony.kommons.util.setValue
|
||||||
@ -79,7 +79,7 @@ class RedstoneControl(private val valueChanges: (new: Boolean, old: Boolean) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SynchronizedRedstoneControl(
|
class SynchronizedRedstoneControl(
|
||||||
synchronizer: DelegateSyncher,
|
synchronizer: Syncher,
|
||||||
private val valueChanges: (new: Boolean, old: Boolean) -> Unit,
|
private val valueChanges: (new: Boolean, old: Boolean) -> Unit,
|
||||||
) : AbstractRedstoneControl() {
|
) : AbstractRedstoneControl() {
|
||||||
override var redstoneSetting: RedstoneSetting by synchronizer.enum(RedstoneSetting.LOW, setter = { access, value ->
|
override var redstoneSetting: RedstoneSetting by synchronizer.enum(RedstoneSetting.LOW, setter = { access, value ->
|
||||||
|
@ -27,7 +27,7 @@ import ru.dbotthepony.mc.otm.network.wrap
|
|||||||
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
||||||
|
|
||||||
class FluidTankBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.FLUID_TANK, blockPos, blockState) {
|
class FluidTankBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.FLUID_TANK, blockPos, blockState) {
|
||||||
val fluid = BlockMatteryFluidHandler(ItemsConfig::FLUID_TANK_CAPACITY, syncher.Slot(ListenableDelegate.SmartBox(FluidStack.EMPTY, setter = { access, value ->
|
val fluid = BlockMatteryFluidHandler(ItemsConfig::FLUID_TANK_CAPACITY, syncher.add(ListenableDelegate.SmartBox(FluidStack.EMPTY, setter = { access, value ->
|
||||||
access.accept(value)
|
access.accept(value)
|
||||||
level?.lightEngine?.checkBlock(blockPos)
|
level?.lightEngine?.checkBlock(blockPos)
|
||||||
|
|
||||||
|
@ -1,23 +1,22 @@
|
|||||||
package ru.dbotthepony.mc.otm.capability
|
package ru.dbotthepony.mc.otm.capability
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
||||||
import net.minecraft.core.HolderLookup
|
import net.minecraft.core.HolderLookup
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.minecraft.nbt.ListTag
|
|
||||||
import net.minecraft.nbt.NumericTag
|
|
||||||
import net.neoforged.neoforge.common.util.INBTSerializable
|
import net.neoforged.neoforge.common.util.INBTSerializable
|
||||||
import ru.dbotthepony.mc.otm.core.forValidRefs
|
import ru.dbotthepony.mc.otm.core.DecimalHistoryGraph
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
import ru.dbotthepony.mc.otm.core.nbt.map
|
import ru.dbotthepony.mc.otm.core.nbt.map
|
||||||
import ru.dbotthepony.mc.otm.core.nbt.set
|
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?> {
|
abstract class AbstractProfiledStorage<out P>(val parent: P) : INBTSerializable<CompoundTag?> {
|
||||||
private val historyReceiveInternal = ArrayList<Decimal>(HISTORY_SIZE)
|
val received = DecimalHistoryGraph(ticks = HISTORY_SIZE)
|
||||||
private val historyTransferInternal = ArrayList<Decimal>(HISTORY_SIZE)
|
val transferred = DecimalHistoryGraph(ticks = HISTORY_SIZE)
|
||||||
|
|
||||||
|
var receivedThisTick = Decimal.ZERO
|
||||||
|
private set
|
||||||
|
var transferredThisTick = Decimal.ZERO
|
||||||
|
private set
|
||||||
|
|
||||||
private var isTicking = false
|
private var isTicking = false
|
||||||
|
|
||||||
@ -28,31 +27,9 @@ abstract class AbstractProfiledStorage<out P>(val parent: P) : INBTSerializable<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
protected fun recordTransfer(value: Decimal, simulate: Boolean): Decimal {
|
||||||
if (!simulate) {
|
if (!simulate) {
|
||||||
thisTickTransfer += value
|
transferredThisTick += value
|
||||||
if (!value.isZero) startTicking()
|
if (!value.isZero) startTicking()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +38,7 @@ abstract class AbstractProfiledStorage<out P>(val parent: P) : INBTSerializable<
|
|||||||
|
|
||||||
protected fun recordReceive(value: Decimal, simulate: Boolean): Decimal {
|
protected fun recordReceive(value: Decimal, simulate: Boolean): Decimal {
|
||||||
if (!simulate) {
|
if (!simulate) {
|
||||||
thisTickReceive += value
|
receivedThisTick += value
|
||||||
if (!value.isZero) startTicking()
|
if (!value.isZero) startTicking()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,14 +46,13 @@ abstract class AbstractProfiledStorage<out P>(val parent: P) : INBTSerializable<
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun tick(): Boolean {
|
private fun tick(): Boolean {
|
||||||
tick = (tick + 1) % HISTORY_SIZE
|
received.add(receivedThisTick)
|
||||||
|
transferred.add(transferredThisTick)
|
||||||
|
|
||||||
historyReceiveInternal[tick] = thisTickReceive
|
isTicking = !receivedThisTick.isZero || !transferredThisTick.isZero || received.any { !it.isZero } || transferred.any { !it.isZero }
|
||||||
historyTransferInternal[tick] = thisTickTransfer
|
|
||||||
isTicking = !thisTickReceive.isZero || !thisTickTransfer.isZero || historyReceiveInternal.any { !it.isZero } || historyTransferInternal.any { !it.isZero }
|
|
||||||
|
|
||||||
thisTickReceive = Decimal.ZERO
|
transferredThisTick = Decimal.ZERO
|
||||||
thisTickTransfer = Decimal.ZERO
|
receivedThisTick = Decimal.ZERO
|
||||||
|
|
||||||
return isTicking
|
return isTicking
|
||||||
}
|
}
|
||||||
@ -84,48 +60,26 @@ abstract class AbstractProfiledStorage<out P>(val parent: P) : INBTSerializable<
|
|||||||
val savedata: INBTSerializable<CompoundTag?> = object : INBTSerializable<CompoundTag?> {
|
val savedata: INBTSerializable<CompoundTag?> = object : INBTSerializable<CompoundTag?> {
|
||||||
override fun serializeNBT(registry: HolderLookup.Provider): CompoundTag {
|
override fun serializeNBT(registry: HolderLookup.Provider): CompoundTag {
|
||||||
return CompoundTag().also { tag ->
|
return CompoundTag().also { tag ->
|
||||||
tag["historyReceive"] = ListTag().also {
|
tag["historyReceive"] = received.serializeNBT(registry)
|
||||||
for (value in historyReceiveInternal) {
|
tag["historyTransfer"] = transferred.serializeNBT(registry)
|
||||||
it.add(value.serializeNBT())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tag["historyTransfer"] = ListTag().also {
|
|
||||||
for (value in historyTransferInternal) {
|
|
||||||
it.add(value.serializeNBT())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tag["historyTick"] = tick
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deserializeNBT(registry: HolderLookup.Provider, nbt: CompoundTag?) {
|
override fun deserializeNBT(registry: HolderLookup.Provider, nbt: CompoundTag?) {
|
||||||
tick = 0
|
transferredThisTick = Decimal.ZERO
|
||||||
thisTickReceive = Decimal.ZERO
|
receivedThisTick = Decimal.ZERO
|
||||||
thisTickTransfer = Decimal.ZERO
|
|
||||||
|
|
||||||
for (i in 0 until HISTORY_SIZE) {
|
received.clear()
|
||||||
historyReceiveInternal[i] = Decimal.ZERO
|
transferred.clear()
|
||||||
historyTransferInternal[i] = Decimal.ZERO
|
|
||||||
}
|
|
||||||
|
|
||||||
nbt ?: return
|
nbt ?: return
|
||||||
|
|
||||||
nbt.map("historyTick") { it: NumericTag ->
|
nbt.map("historyReceive") { it: CompoundTag ->
|
||||||
tick = it.asInt
|
received.deserializeNBT(registry, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
nbt.map("historyReceive") { it: ListTag ->
|
nbt.map("historyTransfer") { it: CompoundTag ->
|
||||||
for (i in 0 until HISTORY_SIZE.coerceAtMost(it.size)) {
|
transferred.deserializeNBT(registry, it)
|
||||||
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])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
startTicking()
|
startTicking()
|
||||||
@ -150,14 +104,6 @@ abstract class AbstractProfiledStorage<out P>(val parent: P) : INBTSerializable<
|
|||||||
return tag
|
return tag
|
||||||
}
|
}
|
||||||
|
|
||||||
val weightedTransfer: Decimal get() {
|
|
||||||
return calcWeightedAverage(historyTransfer, tick)
|
|
||||||
}
|
|
||||||
|
|
||||||
val weightedReceive: Decimal get() {
|
|
||||||
return calcWeightedAverage(historyReceive, tick)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun deserializeNBT(registry: HolderLookup.Provider, nbt: CompoundTag?) {
|
override fun deserializeNBT(registry: HolderLookup.Provider, nbt: CompoundTag?) {
|
||||||
if (parent is INBTSerializable<*>) {
|
if (parent is INBTSerializable<*>) {
|
||||||
(parent as INBTSerializable<CompoundTag?>).deserializeNBT(registry, nbt)
|
(parent as INBTSerializable<CompoundTag?>).deserializeNBT(registry, nbt)
|
||||||
@ -167,48 +113,9 @@ abstract class AbstractProfiledStorage<out P>(val parent: P) : INBTSerializable<
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val HISTORY_SIZE = 20
|
const val HISTORY_SIZE = 20 * 5
|
||||||
private val storages = ObjectArrayList<AbstractProfiledStorage<*>>()
|
private val storages = ObjectArrayList<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
|
|
||||||
}
|
|
||||||
|
|
||||||
// после раздумий, наиболее корректное место для обновления состояния профилированных
|
// после раздумий, наиболее корректное место для обновления состояния профилированных
|
||||||
// хранилищ энергии и материи будет после всех остальных хуков на тики после тика сервера
|
// хранилищ энергии и материи будет после всех остальных хуков на тики после тика сервера
|
||||||
// разумеется, такое решение может несколько снизить производительность сервера,
|
// разумеется, такое решение может несколько снизить производительность сервера,
|
||||||
|
@ -59,7 +59,7 @@ import org.apache.logging.log4j.LogManager
|
|||||||
import org.joml.Vector4f
|
import org.joml.Vector4f
|
||||||
import ru.dbotthepony.kommons.collect.ListenableMap
|
import ru.dbotthepony.kommons.collect.ListenableMap
|
||||||
import ru.dbotthepony.kommons.collect.ListenableSet
|
import ru.dbotthepony.kommons.collect.ListenableSet
|
||||||
import ru.dbotthepony.mc.otm.network.DelegateSyncher
|
import ru.dbotthepony.mc.otm.network.syncher.Syncher
|
||||||
import ru.dbotthepony.kommons.util.ListenableDelegate
|
import ru.dbotthepony.kommons.util.ListenableDelegate
|
||||||
import ru.dbotthepony.kommons.util.getValue
|
import ru.dbotthepony.kommons.util.getValue
|
||||||
import ru.dbotthepony.kommons.util.setValue
|
import ru.dbotthepony.kommons.util.setValue
|
||||||
@ -80,6 +80,7 @@ import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
|
|||||||
import ru.dbotthepony.mc.otm.capability.energy.extractEnergyExact
|
import ru.dbotthepony.mc.otm.capability.energy.extractEnergyExact
|
||||||
import ru.dbotthepony.mc.otm.capability.energy.receiveEnergyExact
|
import ru.dbotthepony.mc.otm.capability.energy.receiveEnergyExact
|
||||||
import ru.dbotthepony.mc.otm.client.minecraft
|
import ru.dbotthepony.mc.otm.client.minecraft
|
||||||
|
import ru.dbotthepony.mc.otm.client.onceClient
|
||||||
import ru.dbotthepony.mc.otm.config.AndroidConfig
|
import ru.dbotthepony.mc.otm.config.AndroidConfig
|
||||||
import ru.dbotthepony.mc.otm.config.ExopackConfig
|
import ru.dbotthepony.mc.otm.config.ExopackConfig
|
||||||
import ru.dbotthepony.mc.otm.container.CombinedContainer
|
import ru.dbotthepony.mc.otm.container.CombinedContainer
|
||||||
@ -122,6 +123,7 @@ import ru.dbotthepony.mc.otm.triggers.ExopackGainedSmeltingTrigger
|
|||||||
import ru.dbotthepony.mc.otm.triggers.ExopackObtainedTrigger
|
import ru.dbotthepony.mc.otm.triggers.ExopackObtainedTrigger
|
||||||
import ru.dbotthepony.mc.otm.triggers.ExopackSlotsExpandedTrigger
|
import ru.dbotthepony.mc.otm.triggers.ExopackSlotsExpandedTrigger
|
||||||
import ru.dbotthepony.mc.otm.triggers.MatteryInventoryChangeTrigger
|
import ru.dbotthepony.mc.otm.triggers.MatteryInventoryChangeTrigger
|
||||||
|
import java.io.Closeable
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.stream.Stream
|
import java.util.stream.Stream
|
||||||
import kotlin.collections.ArrayDeque
|
import kotlin.collections.ArrayDeque
|
||||||
@ -182,7 +184,7 @@ class MatteryPlayer(val ply: Player) {
|
|||||||
* any differences in field order/types/etc between client and server
|
* any differences in field order/types/etc between client and server
|
||||||
* will break *everything*
|
* will break *everything*
|
||||||
*/
|
*/
|
||||||
val syncher = DelegateSyncher()
|
val syncher = Syncher()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remote for "this" player
|
* Remote for "this" player
|
||||||
@ -196,10 +198,10 @@ class MatteryPlayer(val ply: Player) {
|
|||||||
* any differences in field order/types/etc between client and server
|
* any differences in field order/types/etc between client and server
|
||||||
* will break *everything*
|
* will break *everything*
|
||||||
*/
|
*/
|
||||||
val publicSyncher = DelegateSyncher()
|
val publicSyncher = Syncher()
|
||||||
|
|
||||||
val publicSyncherRemote = publicSyncher.Remote()
|
val publicSyncherRemote = publicSyncher.Remote()
|
||||||
val remoteSynchers = Object2ObjectArrayMap<ServerPlayer, DelegateSyncher.Remote>()
|
val remoteSynchers = Object2ObjectArrayMap<ServerPlayer, Syncher.Remote>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For data to be stored to and loaded from NBT automatically
|
* For data to be stored to and loaded from NBT automatically
|
||||||
@ -242,8 +244,8 @@ class MatteryPlayer(val ply: Player) {
|
|||||||
exopackSlotModifier.recompute()
|
exopackSlotModifier.recompute()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val exopackSlotModifierMap = syncher.MapSlot(
|
private val exopackSlotModifierMap = syncher.map(
|
||||||
ListenableMap<UUID, Int>().also { it.addListener(Runnable { _recomputeModifiers() }) },
|
backing = ListenableMap<UUID, Int>().also { it.addListener(Runnable { _recomputeModifiers() }) },
|
||||||
keyCodec = StreamCodecs.UUID,
|
keyCodec = StreamCodecs.UUID,
|
||||||
valueCodec = StreamCodecs.VAR_INT,
|
valueCodec = StreamCodecs.VAR_INT,
|
||||||
)
|
)
|
||||||
@ -265,9 +267,9 @@ class MatteryPlayer(val ply: Player) {
|
|||||||
syncher.add(null, StreamCodecs.ITEM_TYPE.nullable())
|
syncher.add(null, StreamCodecs.ITEM_TYPE.nullable())
|
||||||
}
|
}
|
||||||
|
|
||||||
val slotsChargeFlag = syncher.SetSlot(
|
val slotsChargeFlag = syncher.set(
|
||||||
ListenableSet(IntAVLTreeSet()),
|
backing = ListenableSet(IntAVLTreeSet()),
|
||||||
StreamCodecs.VAR_INT,
|
codec = StreamCodecs.VAR_INT,
|
||||||
).delegate
|
).delegate
|
||||||
|
|
||||||
private fun slotChargeToDefault() {
|
private fun slotChargeToDefault() {
|
||||||
@ -282,13 +284,15 @@ class MatteryPlayer(val ply: Player) {
|
|||||||
slotChargeToDefault()
|
slotChargeToDefault()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var exopackContainerSynchedFilters: List<Closeable> = listOf()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exopack container, which actually store items inside Exopack
|
* Exopack container, which actually store items inside Exopack
|
||||||
*/
|
*/
|
||||||
var exopackContainer: MatteryContainer = PlayerMatteryContainer(0)
|
var exopackContainer: MatteryContainer = PlayerMatteryContainer(0)
|
||||||
private set(value) {
|
private set(value) {
|
||||||
_exoPackMenu = null
|
_exoPackMenu = null
|
||||||
field.removeFilterSynchronizer()
|
exopackContainerSynchedFilters.forEach { it.close() }
|
||||||
|
|
||||||
@Suppress("SENSELESS_COMPARISON") // false positive - fields of player can easily be nulls, despite annotations saying otherwise
|
@Suppress("SENSELESS_COMPARISON") // false positive - fields of player can easily be nulls, despite annotations saying otherwise
|
||||||
if (ply.containerMenu != null && (ply !is ServerPlayer || ply.connection != null)) {
|
if (ply.containerMenu != null && (ply !is ServerPlayer || ply.connection != null)) {
|
||||||
@ -303,7 +307,7 @@ class MatteryPlayer(val ply: Player) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
value.deserializeNBT(ply.level().registryAccess(), field.serializeNBT(ply.level().registryAccess()))
|
value.deserializeNBT(ply.level().registryAccess(), field.serializeNBT(ply.level().registryAccess()))
|
||||||
value.addFilterSynchronizer(syncher)
|
exopackContainerSynchedFilters = value.synchableFilters.map { syncher.add0(it) }
|
||||||
field = value
|
field = value
|
||||||
|
|
||||||
_combinedInventory = null
|
_combinedInventory = null
|
||||||
|
@ -8,7 +8,7 @@ import net.minecraft.world.item.ItemStack
|
|||||||
import net.minecraft.world.ticks.ContainerSingleItem
|
import net.minecraft.world.ticks.ContainerSingleItem
|
||||||
import net.neoforged.neoforge.capabilities.Capabilities
|
import net.neoforged.neoforge.capabilities.Capabilities
|
||||||
import net.neoforged.neoforge.common.util.INBTSerializable
|
import net.neoforged.neoforge.common.util.INBTSerializable
|
||||||
import ru.dbotthepony.mc.otm.network.DelegateSyncher
|
import ru.dbotthepony.mc.otm.network.syncher.Syncher
|
||||||
import ru.dbotthepony.kommons.util.getValue
|
import ru.dbotthepony.kommons.util.getValue
|
||||||
import ru.dbotthepony.kommons.util.setValue
|
import ru.dbotthepony.kommons.util.setValue
|
||||||
import ru.dbotthepony.mc.otm.capability.FlowDirection
|
import ru.dbotthepony.mc.otm.capability.FlowDirection
|
||||||
@ -23,7 +23,7 @@ import ru.dbotthepony.mc.otm.triggers.ExopackBatterySlotTrigger
|
|||||||
|
|
||||||
class BatteryBackedEnergyStorage(
|
class BatteryBackedEnergyStorage(
|
||||||
private val ply: Player,
|
private val ply: Player,
|
||||||
synchronizer: DelegateSyncher,
|
synchronizer: Syncher,
|
||||||
initialCharge: Decimal,
|
initialCharge: Decimal,
|
||||||
maxCharge: Decimal,
|
maxCharge: Decimal,
|
||||||
val isAndroid: Boolean,
|
val isAndroid: Boolean,
|
||||||
|
@ -43,8 +43,8 @@ object MatterStorageProvider : IBlockComponentProvider, IServerDataProvider<Bloc
|
|||||||
// profiledData.putDecimal("lastTickReceive", it.lastTickReceive)
|
// profiledData.putDecimal("lastTickReceive", it.lastTickReceive)
|
||||||
// profiledData.putDecimal("lastTickTransfer", it.lastTickTransfer)
|
// profiledData.putDecimal("lastTickTransfer", it.lastTickTransfer)
|
||||||
|
|
||||||
profiledData.putDecimal("weightedReceive", it.weightedReceive)
|
profiledData.putDecimal("weightedReceive", it.received.calcWeightedAverage())
|
||||||
profiledData.putDecimal("weightedTransfer", it.weightedTransfer)
|
profiledData.putDecimal("weightedTransfer", it.transferred.calcWeightedAverage())
|
||||||
|
|
||||||
matterData.put("profiledData", profiledData)
|
matterData.put("profiledData", profiledData)
|
||||||
}
|
}
|
||||||
|
@ -37,8 +37,8 @@ object MatteryEnergyProvider : IBlockComponentProvider, IServerDataProvider<Bloc
|
|||||||
// profiledData.putDecimal("lastTickReceive", it.lastTickReceive)
|
// profiledData.putDecimal("lastTickReceive", it.lastTickReceive)
|
||||||
// profiledData.putDecimal("lastTickTransfer", it.lastTickTransfer)
|
// profiledData.putDecimal("lastTickTransfer", it.lastTickTransfer)
|
||||||
|
|
||||||
profiledData.putDecimal("weightedReceive", it.weightedReceive)
|
profiledData.putDecimal("weightedReceive", it.received.calcWeightedAverage())
|
||||||
profiledData.putDecimal("weightedTransfer", it.weightedTransfer)
|
profiledData.putDecimal("weightedTransfer", it.transferred.calcWeightedAverage())
|
||||||
|
|
||||||
energyData.put("profiledData", profiledData)
|
energyData.put("profiledData", profiledData)
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import net.minecraft.nbt.CompoundTag
|
|||||||
import net.minecraft.nbt.ListTag
|
import net.minecraft.nbt.ListTag
|
||||||
import net.minecraft.nbt.NbtOps
|
import net.minecraft.nbt.NbtOps
|
||||||
import net.minecraft.nbt.Tag
|
import net.minecraft.nbt.Tag
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
import net.minecraft.world.Container
|
import net.minecraft.world.Container
|
||||||
import net.minecraft.world.entity.player.Player
|
import net.minecraft.world.entity.player.Player
|
||||||
import net.minecraft.world.entity.player.StackedContents
|
import net.minecraft.world.entity.player.StackedContents
|
||||||
@ -24,7 +25,8 @@ import net.minecraft.world.item.Items
|
|||||||
import net.neoforged.neoforge.common.util.INBTSerializable
|
import net.neoforged.neoforge.common.util.INBTSerializable
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import ru.dbotthepony.kommons.collect.ListenableMap
|
import ru.dbotthepony.kommons.collect.ListenableMap
|
||||||
import ru.dbotthepony.mc.otm.network.DelegateSyncher
|
import ru.dbotthepony.kommons.util.Delegate
|
||||||
|
import ru.dbotthepony.mc.otm.network.syncher.Syncher
|
||||||
import ru.dbotthepony.mc.otm.container.util.IContainerSlot
|
import ru.dbotthepony.mc.otm.container.util.IContainerSlot
|
||||||
import ru.dbotthepony.mc.otm.core.addSorted
|
import ru.dbotthepony.mc.otm.core.addSorted
|
||||||
import ru.dbotthepony.mc.otm.core.collect.any
|
import ru.dbotthepony.mc.otm.core.collect.any
|
||||||
@ -33,11 +35,15 @@ import ru.dbotthepony.mc.otm.core.collect.emptyIterator
|
|||||||
import ru.dbotthepony.mc.otm.core.collect.filter
|
import ru.dbotthepony.mc.otm.core.collect.filter
|
||||||
import ru.dbotthepony.mc.otm.core.collect.map
|
import ru.dbotthepony.mc.otm.core.collect.map
|
||||||
import ru.dbotthepony.mc.otm.core.collect.toList
|
import ru.dbotthepony.mc.otm.core.collect.toList
|
||||||
|
import ru.dbotthepony.mc.otm.core.immutableList
|
||||||
import ru.dbotthepony.mc.otm.core.isNotEmpty
|
import ru.dbotthepony.mc.otm.core.isNotEmpty
|
||||||
import ru.dbotthepony.mc.otm.core.nbt.set
|
import ru.dbotthepony.mc.otm.core.nbt.set
|
||||||
import ru.dbotthepony.mc.otm.core.registryName
|
import ru.dbotthepony.mc.otm.core.registryName
|
||||||
import ru.dbotthepony.mc.otm.data.minRange
|
import ru.dbotthepony.mc.otm.data.minRange
|
||||||
import ru.dbotthepony.mc.otm.network.StreamCodecs
|
import ru.dbotthepony.mc.otm.network.StreamCodecs
|
||||||
|
import ru.dbotthepony.mc.otm.network.syncher.IRemoteState
|
||||||
|
import ru.dbotthepony.mc.otm.network.syncher.ISynchable
|
||||||
|
import ru.dbotthepony.mc.otm.network.syncher.SynchableObservedDelegate
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
import java.util.function.Predicate
|
import java.util.function.Predicate
|
||||||
@ -80,7 +86,6 @@ open class MatteryContainer(var listener: ContainerListener, private val size: I
|
|||||||
|
|
||||||
private val trackedSlots: Array<ItemStack> = Array(size) { ItemStack.EMPTY }
|
private val trackedSlots: Array<ItemStack> = Array(size) { ItemStack.EMPTY }
|
||||||
private val filters: Array<Item?> = arrayOfNulls(size)
|
private val filters: Array<Item?> = arrayOfNulls(size)
|
||||||
private var filterSynchronizer: DelegateSyncher.MapSlot<Int, Item>? = null
|
|
||||||
|
|
||||||
var changeset = 0
|
var changeset = 0
|
||||||
private set
|
private set
|
||||||
@ -111,60 +116,18 @@ open class MatteryContainer(var listener: ContainerListener, private val size: I
|
|||||||
|
|
||||||
fun clearSlotFilters() {
|
fun clearSlotFilters() {
|
||||||
Arrays.fill(filters, null)
|
Arrays.fill(filters, null)
|
||||||
filterSynchronizer?.delegate?.clear()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeFilterSynchronizer() {
|
val synchableFilters by lazy {
|
||||||
filterSynchronizer?.remove()
|
immutableList<ISynchable> {
|
||||||
filterSynchronizer = null
|
for (i in 0 until size) {
|
||||||
}
|
accept(SynchableObservedDelegate(Delegate.Of({ filters[i] }, { filters[i] = it }), StreamCodecs.ITEM_TYPE_NULLABLE))
|
||||||
|
|
||||||
fun addFilterSynchronizer(synchronizer: DelegateSyncher): DelegateSyncher.MapSlot<Int, Item> {
|
|
||||||
check(filterSynchronizer?.delegate == null) { "Already has filter synchronizer" }
|
|
||||||
|
|
||||||
val field = synchronizer.MapSlot(
|
|
||||||
ListenableMap<Int, Item>(Int2ObjectOpenHashMap()),
|
|
||||||
keyCodec = StreamCodecs.VAR_INT,
|
|
||||||
valueCodec = StreamCodecs.ITEM_TYPE,
|
|
||||||
)
|
|
||||||
|
|
||||||
for ((i, filter) in filters.withIndex()) {
|
|
||||||
if (filter != null) {
|
|
||||||
field.delegate[i] = filter
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
field.addListener(object : ListenableMap.MapListener<Int, Item> {
|
|
||||||
override fun onClear() {
|
|
||||||
Arrays.fill(filters, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onValueAdded(key: Int, value: Item) {
|
|
||||||
filters[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onValueRemoved(key: Int, value: Item) {
|
|
||||||
filters[key] = null
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
filterSynchronizer = field
|
|
||||||
return field
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final override fun setSlotFilter(slot: Int, filter: Item?): Boolean {
|
final override fun setSlotFilter(slot: Int, filter: Item?): Boolean {
|
||||||
if (filters[slot] !== filter) {
|
filters[slot] = filter
|
||||||
filters[slot] = filter
|
|
||||||
|
|
||||||
filterSynchronizer?.delegate?.let {
|
|
||||||
if (filter == null) {
|
|
||||||
it.remove(slot)
|
|
||||||
} else {
|
|
||||||
it[slot] = filter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,8 +193,6 @@ open class MatteryContainer(var listener: ContainerListener, private val size: I
|
|||||||
nonEmptyFlags.clear()
|
nonEmptyFlags.clear()
|
||||||
nonEmptyIndices = IntArrayList()
|
nonEmptyIndices = IntArrayList()
|
||||||
|
|
||||||
filterSynchronizer?.delegate?.clear()
|
|
||||||
|
|
||||||
if (tag != null) {
|
if (tag != null) {
|
||||||
SerializedState.CODEC.parse(registries.createSerializationContext(NbtOps.INSTANCE), tag)
|
SerializedState.CODEC.parse(registries.createSerializationContext(NbtOps.INSTANCE), tag)
|
||||||
.resultOrPartial { LOGGER.error("Error deserializing container: $it") }
|
.resultOrPartial { LOGGER.error("Error deserializing container: $it") }
|
||||||
@ -258,11 +219,9 @@ open class MatteryContainer(var listener: ContainerListener, private val size: I
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val map = filterSynchronizer?.delegate
|
|
||||||
|
|
||||||
for ((item, index) in it.filters) {
|
for ((item, index) in it.filters) {
|
||||||
filters[index] = item
|
if (index in 0 until size)
|
||||||
map?.put(index, item)
|
filters[index] = item
|
||||||
}
|
}
|
||||||
|
|
||||||
setChanged()
|
setChanged()
|
||||||
|
@ -0,0 +1,200 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.core
|
||||||
|
|
||||||
|
import com.mojang.serialization.Codec
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||||
|
import net.minecraft.core.HolderLookup
|
||||||
|
import net.minecraft.nbt.CompoundTag
|
||||||
|
import net.minecraft.nbt.NbtOps
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
|
import net.neoforged.neoforge.common.util.INBTSerializable
|
||||||
|
import org.apache.logging.log4j.LogManager
|
||||||
|
import ru.dbotthepony.mc.otm.network.MatteryStreamCodec
|
||||||
|
import ru.dbotthepony.mc.otm.network.syncher.IRemoteState
|
||||||
|
import ru.dbotthepony.mc.otm.network.syncher.ISynchable
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList
|
||||||
|
|
||||||
|
abstract class AbstractHistoryGraph<V : Any>(
|
||||||
|
/**
|
||||||
|
* How many measurements one graph value contains
|
||||||
|
*/
|
||||||
|
val resolution: Int,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Limit on how many graph values to store
|
||||||
|
*/
|
||||||
|
val width: Int = 100,
|
||||||
|
) : Iterable<V>, INBTSerializable<CompoundTag>, ISynchable {
|
||||||
|
constructor(ticks: Int) : this(1, ticks)
|
||||||
|
|
||||||
|
init {
|
||||||
|
require(resolution >= 1) { "Invalid resolution: $resolution" }
|
||||||
|
require(width >= 1) { "Invalid width: $width" }
|
||||||
|
}
|
||||||
|
|
||||||
|
// can't believe i've become lazy enough to use codecs for this
|
||||||
|
private data class SData<V : Any>(val accumulated: List<V>, val values: List<V>)
|
||||||
|
|
||||||
|
protected abstract fun calculateAverage(input: List<V>): V
|
||||||
|
protected abstract fun sum(a: V, b: V): V
|
||||||
|
protected abstract fun identity(): V
|
||||||
|
|
||||||
|
abstract val codec: Codec<V>
|
||||||
|
abstract val streamCodec: MatteryStreamCodec<RegistryFriendlyByteBuf, V>
|
||||||
|
|
||||||
|
private val remotes = CopyOnWriteArrayList<RemoteState>()
|
||||||
|
|
||||||
|
private inner class RemoteState(val listener: Runnable) : IRemoteState {
|
||||||
|
init {
|
||||||
|
remotes.add(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var shouldFullyNetwork = false
|
||||||
|
private val networkValues = ArrayList<V>()
|
||||||
|
|
||||||
|
override fun write(stream: RegistryFriendlyByteBuf) {
|
||||||
|
stream.writeBoolean(shouldFullyNetwork)
|
||||||
|
|
||||||
|
if (shouldFullyNetwork) {
|
||||||
|
shouldFullyNetwork = false
|
||||||
|
stream.writeVarInt(values.size)
|
||||||
|
values.forEach { streamCodec.encode(stream, it) }
|
||||||
|
} else {
|
||||||
|
stream.writeVarInt(networkValues.size)
|
||||||
|
networkValues.forEach { streamCodec.encode(stream, it) }
|
||||||
|
networkValues.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun add(value: V) {
|
||||||
|
if (shouldFullyNetwork) {
|
||||||
|
return
|
||||||
|
} else if (networkValues.size + 1 >= (width / 2).coerceIn(20, 100)) {
|
||||||
|
shouldFullyNetwork = true
|
||||||
|
networkValues.clear()
|
||||||
|
listener.run()
|
||||||
|
} else {
|
||||||
|
networkValues.add(value)
|
||||||
|
listener.run()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invalidate() {
|
||||||
|
networkValues.clear()
|
||||||
|
shouldFullyNetwork = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
remotes.remove(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun read(stream: RegistryFriendlyByteBuf) {
|
||||||
|
if (stream.readBoolean()) {
|
||||||
|
values.clear()
|
||||||
|
|
||||||
|
for (i in 0 until stream.readVarInt()) {
|
||||||
|
values.add(streamCodec.decode(stream))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (i in 0 until stream.readVarInt()) {
|
||||||
|
values.addFirst(streamCodec.decode(stream))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createRemoteState(listener: Runnable): IRemoteState {
|
||||||
|
return RemoteState(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun calculateAverage(): V {
|
||||||
|
if (values.isEmpty()) {
|
||||||
|
return identity()
|
||||||
|
}
|
||||||
|
|
||||||
|
return calculateAverage(values)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val accumulator = ArrayList<V>(this.resolution)
|
||||||
|
private val values = ArrayDeque<V>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
values.fill(identity())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates sum of values provided by calls to [add] and writes it to graph history or graph value accumulator
|
||||||
|
*/
|
||||||
|
fun add(value: V) {
|
||||||
|
accumulator.add(value)
|
||||||
|
|
||||||
|
if (accumulator.size >= resolution) {
|
||||||
|
val calc = calculateAverage(accumulator)
|
||||||
|
values.addFirst(calc)
|
||||||
|
remotes.forEach { it.add(calc) }
|
||||||
|
accumulator.clear()
|
||||||
|
while (values.size >= width) values.removeLast()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val size: Int
|
||||||
|
get() = values.size
|
||||||
|
|
||||||
|
operator fun get(index: Int): V {
|
||||||
|
return values.getOrNull(index) ?: identity()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getRaw(index: Int): V {
|
||||||
|
return values[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun codec(): Codec<SData<V>> {
|
||||||
|
val list = Codec.list(codec)
|
||||||
|
|
||||||
|
return RecordCodecBuilder.create {
|
||||||
|
it.group(
|
||||||
|
list.fieldOf("accumulator").forGetter { it.accumulated },
|
||||||
|
list.fieldOf("values").forGetter { it.values },
|
||||||
|
).apply(it, ::SData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serializeNBT(registry: HolderLookup.Provider): CompoundTag {
|
||||||
|
return codec().encodeStart(registry.createSerializationContext(NbtOps.INSTANCE), SData(accumulator, values)).orThrow as CompoundTag
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserializeNBT(registry: HolderLookup.Provider, nbt: CompoundTag) {
|
||||||
|
this.accumulator.clear()
|
||||||
|
this.values.clear()
|
||||||
|
|
||||||
|
val (accumulator, values) = codec().decode(registry.createSerializationContext(NbtOps.INSTANCE), nbt)
|
||||||
|
.map { it.first }
|
||||||
|
.resultOrPartial { LOGGER.error("Errors during deserializing history graph: $it") }
|
||||||
|
.orElseGet { SData(listOf(), listOf()) }
|
||||||
|
|
||||||
|
this.accumulator.addAll(accumulator)
|
||||||
|
this.values.addAll(values)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clear() {
|
||||||
|
accumulator.clear()
|
||||||
|
values.fill(identity())
|
||||||
|
}
|
||||||
|
|
||||||
|
final override fun iterator(): Iterator<V> {
|
||||||
|
return object : Iterator<V> {
|
||||||
|
private var index = 0
|
||||||
|
|
||||||
|
override fun hasNext(): Boolean {
|
||||||
|
return index < width
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun next(): V {
|
||||||
|
return this@AbstractHistoryGraph[index++]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val LOGGER = LogManager.getLogger()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.core
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList
|
||||||
|
import com.mojang.serialization.Codec
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.reduce
|
||||||
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
|
import ru.dbotthepony.mc.otm.data.DecimalCodec
|
||||||
|
import ru.dbotthepony.mc.otm.network.MatteryStreamCodec
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
class DecimalHistoryGraph : AbstractHistoryGraph<Decimal> {
|
||||||
|
constructor(resolution: Int, width: Int) : super(resolution, width)
|
||||||
|
constructor(ticks: Int) : this(1, ticks)
|
||||||
|
|
||||||
|
override fun calculateAverage(input: List<Decimal>): Decimal {
|
||||||
|
return input.iterator().reduce(Decimal.ZERO, Decimal::plus) / Decimal(input.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun sum(a: Decimal, b: Decimal): Decimal {
|
||||||
|
return a + b
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun identity(): Decimal {
|
||||||
|
return Decimal.ZERO
|
||||||
|
}
|
||||||
|
|
||||||
|
override val codec: Codec<Decimal>
|
||||||
|
get() = DecimalCodec
|
||||||
|
override val streamCodec: MatteryStreamCodec<RegistryFriendlyByteBuf, Decimal>
|
||||||
|
get() = DecimalCodec.NETWORK
|
||||||
|
|
||||||
|
fun calcWeightedAverage(): Decimal {
|
||||||
|
return calcWeightedAverage(this::get)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val HISTORY_WEIGHTERS: ImmutableList<Decimal> = immutableList {
|
||||||
|
/*for (i in 0 until 20) {
|
||||||
|
accept(Decimal(0.5.pow(i + 1)))
|
||||||
|
}*/
|
||||||
|
|
||||||
|
val average = Decimal(1) / Decimal(20)
|
||||||
|
|
||||||
|
for (i in 0 until 20) {
|
||||||
|
accept(average)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun calcWeightedAverage(getter: (Int) -> Decimal): Decimal {
|
||||||
|
var result = Decimal.ZERO
|
||||||
|
|
||||||
|
for (i in 0 until 20) {
|
||||||
|
result += getter(i) * HISTORY_WEIGHTERS[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.core.collect
|
||||||
|
|
||||||
|
import net.minecraft.nbt.ListTag
|
||||||
|
import net.minecraft.nbt.Tag
|
||||||
|
import java.util.function.BiConsumer
|
||||||
|
import java.util.function.BinaryOperator
|
||||||
|
import java.util.function.Function
|
||||||
|
import java.util.function.Supplier
|
||||||
|
import java.util.stream.Collector
|
||||||
|
import java.util.stream.Collector.Characteristics
|
||||||
|
|
||||||
|
object ListTagCollector : Collector<Tag, ListTag, ListTag> {
|
||||||
|
override fun supplier(): Supplier<ListTag> {
|
||||||
|
return Supplier { ListTag() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun accumulator(): BiConsumer<ListTag, Tag> {
|
||||||
|
return BiConsumer { t, u -> t.add(u) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun combiner(): BinaryOperator<ListTag> {
|
||||||
|
return BinaryOperator { t, u ->
|
||||||
|
if (t.size >= u.size) {
|
||||||
|
t.addAll(u)
|
||||||
|
t
|
||||||
|
} else {
|
||||||
|
u.addAll(t)
|
||||||
|
u
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun finisher(): Function<ListTag, ListTag> {
|
||||||
|
return Function.identity()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val chars = setOf(Characteristics.IDENTITY_FINISH)
|
||||||
|
|
||||||
|
override fun characteristics(): Set<Characteristics> {
|
||||||
|
return chars
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,9 @@
|
|||||||
package ru.dbotthepony.mc.otm.core.util
|
package ru.dbotthepony.mc.otm.core.util
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.chars.CharArrayList
|
import it.unimi.dsi.fastutil.chars.CharArrayList
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList
|
|
||||||
import net.minecraft.ChatFormatting
|
import net.minecraft.ChatFormatting
|
||||||
import net.minecraft.network.chat.Component
|
import net.minecraft.network.chat.Component
|
||||||
import net.minecraft.network.chat.MutableComponent
|
import net.minecraft.network.chat.MutableComponent
|
||||||
import ru.dbotthepony.kommons.util.value
|
|
||||||
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage
|
|
||||||
import ru.dbotthepony.mc.otm.core.TextComponent
|
import ru.dbotthepony.mc.otm.core.TextComponent
|
||||||
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
@ -283,22 +280,20 @@ fun formatHistory(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (verbose.asBoolean) {
|
if (verbose.asBoolean) {
|
||||||
addLine(widget.lastTickReceive, widget.lastTickTransfer)
|
addLine(widget.receivedThisTick, widget.transferredThisTick)
|
||||||
|
|
||||||
val values = IntArrayList()
|
|
||||||
values.addAll(widget.tick downTo 0)
|
|
||||||
values.addAll(AbstractProfiledStorage.HISTORY_SIZE - 1 downTo widget.tick + 1)
|
|
||||||
|
|
||||||
for (i in values.intIterator()) {
|
|
||||||
addLine(widget.historyReceive[i].value, widget.historyTransfer[i].value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addLine(
|
addLine(
|
||||||
widget.weightedReceive,
|
widget.received.calcWeightedAverage(),
|
||||||
widget.weightedTransfer
|
widget.transferred.calcWeightedAverage()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (verbose.asBoolean) {
|
||||||
|
for (i in 0 until 20) {
|
||||||
|
addLine(widget.received[i], widget.transferred[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val maxWidthDeltaNumber = lines.maxOf { it.delta.formatted.length }
|
val maxWidthDeltaNumber = lines.maxOf { it.delta.formatted.length }
|
||||||
val maxWidthInNumber = lines.maxOf { it.incoming.formatted.length }
|
val maxWidthInNumber = lines.maxOf { it.incoming.formatted.length }
|
||||||
val maxWidthOutNumber = lines.maxOf { it.outgoing.formatted.length }
|
val maxWidthOutNumber = lines.maxOf { it.outgoing.formatted.length }
|
||||||
@ -310,7 +305,7 @@ fun formatHistory(
|
|||||||
fun Part.format(widthNumbers: Int, widthSi: Int): MutableComponent {
|
fun Part.format(widthNumbers: Int, widthSi: Int): MutableComponent {
|
||||||
return TranslatableComponent(
|
return TranslatableComponent(
|
||||||
prefix.formatLocaleKey,
|
prefix.formatLocaleKey,
|
||||||
resplice("-".repeat(max(widthNumbers - formatted.length, 0)) + formatted, decimals) + "-".repeat(max(widthSi - prefix.length, 0)),
|
resplice("_".repeat(max(widthNumbers - formatted.length, 0)) + formatted, decimals) + "_".repeat(max(widthSi - prefix.length, 0)),
|
||||||
suffix
|
suffix
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import net.minecraft.network.FriendlyByteBuf
|
|||||||
import net.minecraft.network.codec.StreamCodec
|
import net.minecraft.network.codec.StreamCodec
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal.Companion.fromByteArray
|
import ru.dbotthepony.mc.otm.core.math.Decimal.Companion.fromByteArray
|
||||||
|
import ru.dbotthepony.mc.otm.network.wrap
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
object DecimalCodec : Codec<Decimal> {
|
object DecimalCodec : Codec<Decimal> {
|
||||||
@ -21,7 +22,7 @@ object DecimalCodec : Codec<Decimal> {
|
|||||||
override fun encode(buf: FriendlyByteBuf, p_320396_: Decimal) {
|
override fun encode(buf: FriendlyByteBuf, p_320396_: Decimal) {
|
||||||
buf.writeByteArray(p_320396_.toByteArray())
|
buf.writeByteArray(p_320396_.toByteArray())
|
||||||
}
|
}
|
||||||
}
|
}.wrap()
|
||||||
|
|
||||||
override fun <T : Any> encode(input: Decimal, ops: DynamicOps<T>, prefix: T): DataResult<T> {
|
override fun <T : Any> encode(input: Decimal, ops: DynamicOps<T>, prefix: T): DataResult<T> {
|
||||||
if (ops === NbtOps.INSTANCE) {
|
if (ops === NbtOps.INSTANCE) {
|
||||||
|
@ -14,7 +14,6 @@ import net.minecraft.network.protocol.common.custom.CustomPacketPayload
|
|||||||
import net.minecraft.resources.ResourceLocation
|
import net.minecraft.resources.ResourceLocation
|
||||||
import net.minecraft.server.level.ServerPlayer
|
import net.minecraft.server.level.ServerPlayer
|
||||||
import net.minecraft.world.Container
|
import net.minecraft.world.Container
|
||||||
import net.minecraft.world.SimpleContainer
|
|
||||||
import net.minecraft.world.entity.player.Inventory
|
import net.minecraft.world.entity.player.Inventory
|
||||||
import net.minecraft.world.entity.player.Player
|
import net.minecraft.world.entity.player.Player
|
||||||
import net.minecraft.world.inventory.AbstractContainerMenu
|
import net.minecraft.world.inventory.AbstractContainerMenu
|
||||||
@ -32,26 +31,18 @@ import ru.dbotthepony.kommons.util.Delegate
|
|||||||
import ru.dbotthepony.kommons.util.getValue
|
import ru.dbotthepony.kommons.util.getValue
|
||||||
import ru.dbotthepony.kommons.util.setValue
|
import ru.dbotthepony.kommons.util.setValue
|
||||||
import ru.dbotthepony.mc.otm.OverdriveThatMatters
|
import ru.dbotthepony.mc.otm.OverdriveThatMatters
|
||||||
import ru.dbotthepony.mc.otm.capability.IMatteryUpgrade
|
|
||||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
|
||||||
import ru.dbotthepony.mc.otm.capability.UpgradeType
|
|
||||||
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
|
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
|
||||||
import ru.dbotthepony.mc.otm.capability.matteryPlayer
|
import ru.dbotthepony.mc.otm.capability.matteryPlayer
|
||||||
import ru.dbotthepony.mc.otm.compat.cos.cosmeticArmorSlots
|
import ru.dbotthepony.mc.otm.compat.cos.cosmeticArmorSlots
|
||||||
import ru.dbotthepony.mc.otm.compat.curios.curiosSlots
|
import ru.dbotthepony.mc.otm.compat.curios.curiosSlots
|
||||||
import ru.dbotthepony.mc.otm.compat.curios.isCurioSlot
|
import ru.dbotthepony.mc.otm.compat.curios.isCurioSlot
|
||||||
import ru.dbotthepony.mc.otm.container.IMatteryContainer
|
import ru.dbotthepony.mc.otm.container.IMatteryContainer
|
||||||
import ru.dbotthepony.mc.otm.container.UpgradeContainer
|
|
||||||
import ru.dbotthepony.mc.otm.container.computeSortedIndices
|
import ru.dbotthepony.mc.otm.container.computeSortedIndices
|
||||||
import ru.dbotthepony.mc.otm.container.sortWithIndices
|
import ru.dbotthepony.mc.otm.container.sortWithIndices
|
||||||
import ru.dbotthepony.mc.otm.core.ResourceLocation
|
import ru.dbotthepony.mc.otm.core.ResourceLocation
|
||||||
import ru.dbotthepony.mc.otm.core.collect.ConditionalEnumSet
|
|
||||||
import ru.dbotthepony.mc.otm.core.collect.ConditionalSet
|
import ru.dbotthepony.mc.otm.core.collect.ConditionalSet
|
||||||
import ru.dbotthepony.mc.otm.core.immutableList
|
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
import ru.dbotthepony.mc.otm.menu.input.InstantBooleanInput
|
|
||||||
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
|
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
|
||||||
import ru.dbotthepony.mc.otm.network.DelegateSyncher
|
|
||||||
import ru.dbotthepony.mc.otm.network.MatteryStreamCodec
|
import ru.dbotthepony.mc.otm.network.MatteryStreamCodec
|
||||||
import ru.dbotthepony.mc.otm.network.MenuDataPacket
|
import ru.dbotthepony.mc.otm.network.MenuDataPacket
|
||||||
import ru.dbotthepony.mc.otm.network.SetCarriedPacket
|
import ru.dbotthepony.mc.otm.network.SetCarriedPacket
|
||||||
@ -60,13 +51,12 @@ import ru.dbotthepony.mc.otm.network.decode
|
|||||||
import ru.dbotthepony.mc.otm.network.encode
|
import ru.dbotthepony.mc.otm.network.encode
|
||||||
import ru.dbotthepony.mc.otm.network.nullable
|
import ru.dbotthepony.mc.otm.network.nullable
|
||||||
import ru.dbotthepony.mc.otm.network.readByteListUnbounded
|
import ru.dbotthepony.mc.otm.network.readByteListUnbounded
|
||||||
|
import ru.dbotthepony.mc.otm.network.syncher.Syncher
|
||||||
import ru.dbotthepony.mc.otm.network.wrap
|
import ru.dbotthepony.mc.otm.network.wrap
|
||||||
import ru.dbotthepony.mc.otm.network.writeByteListUnbounded
|
import ru.dbotthepony.mc.otm.network.writeByteListUnbounded
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.function.BooleanSupplier
|
import java.util.function.BooleanSupplier
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
import java.util.function.DoubleSupplier
|
|
||||||
import java.util.function.IntSupplier
|
|
||||||
import java.util.function.Predicate
|
import java.util.function.Predicate
|
||||||
|
|
||||||
data class PlayerSlot<A : Slot, B : Slot>(val functional: A, val cosmetic: B? = null)
|
data class PlayerSlot<A : Slot, B : Slot>(val functional: A, val cosmetic: B? = null)
|
||||||
@ -85,7 +75,7 @@ abstract class MatteryMenu(
|
|||||||
/**
|
/**
|
||||||
* Server->Client synchronizer
|
* Server->Client synchronizer
|
||||||
*/
|
*/
|
||||||
val mSynchronizer = DelegateSyncher()
|
val mSynchronizer = Syncher()
|
||||||
val synchronizerRemote = mSynchronizer.Remote()
|
val synchronizerRemote = mSynchronizer.Remote()
|
||||||
val player: Player get() = inventory.player
|
val player: Player get() = inventory.player
|
||||||
|
|
||||||
@ -133,7 +123,7 @@ abstract class MatteryMenu(
|
|||||||
/**
|
/**
|
||||||
* Client->Server input
|
* Client->Server input
|
||||||
*/
|
*/
|
||||||
inner class PlayerInput<V>(val codec: MatteryStreamCodec<in RegistryFriendlyByteBuf, V>, allowSpectators: Boolean = false, val handler: (V) -> Unit) : Consumer<V>, Predicate<Player> {
|
inner class PlayerInput<V>(val codec: MatteryStreamCodec<RegistryFriendlyByteBuf, V>, allowSpectators: Boolean = false, val handler: (V) -> Unit) : Consumer<V>, Predicate<Player> {
|
||||||
val id = playerInputs.size
|
val id = playerInputs.size
|
||||||
var allowSpectators by mSynchronizer.boolean(allowSpectators)
|
var allowSpectators by mSynchronizer.boolean(allowSpectators)
|
||||||
|
|
||||||
@ -209,7 +199,7 @@ abstract class MatteryMenu(
|
|||||||
|
|
||||||
val exopackChargeSlots: List<MatterySlot> = Collections.unmodifiableList(_exopackChargeSlots)
|
val exopackChargeSlots: List<MatterySlot> = Collections.unmodifiableList(_exopackChargeSlots)
|
||||||
|
|
||||||
val exopackPowerLevel = ProfiledLevelGaugeWidget<ProfiledEnergyStorage<*>>(mSynchronizer)
|
val exopackPowerLevel = ProfiledLevelGaugeWidget<ProfiledEnergyStorage<*>>(mSynchronizer, player.matteryPlayer.exopackEnergy)
|
||||||
|
|
||||||
var sortInventoryInput: SortInput? = null
|
var sortInventoryInput: SortInput? = null
|
||||||
private set
|
private set
|
||||||
@ -326,8 +316,6 @@ abstract class MatteryMenu(
|
|||||||
|
|
||||||
for (i in 0 until mattery.exopackChargeSlots.containerSize)
|
for (i in 0 until mattery.exopackChargeSlots.containerSize)
|
||||||
_exopackChargeSlots.add(ChargeSlot(mattery.exopackChargeSlots, i).also { mapQuickMoveToExternal(it); mapQuickMoveToInventory(it); addSlot(it) })
|
_exopackChargeSlots.add(ChargeSlot(mattery.exopackChargeSlots, i).also { mapQuickMoveToExternal(it); mapQuickMoveToInventory(it); addSlot(it) })
|
||||||
|
|
||||||
exopackPowerLevel.with(mattery.exopackEnergy)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sortInventoryInput = SortInput(mattery.inventoryAndExopackNoHotbar, playerSortSettings)
|
sortInventoryInput = SortInput(mattery.inventoryAndExopackNoHotbar, playerSortSettings)
|
||||||
|
@ -48,7 +48,7 @@ class PainterMenu(
|
|||||||
val inputContainer = MatteryContainer(::rescan, 1)
|
val inputContainer = MatteryContainer(::rescan, 1)
|
||||||
val outputContainer = MatteryContainer(1)
|
val outputContainer = MatteryContainer(1)
|
||||||
private var lastRecipe: RecipeHolder<out AbstractPainterRecipe>? = null
|
private var lastRecipe: RecipeHolder<out AbstractPainterRecipe>? = null
|
||||||
var selectedRecipe by mSynchronizer.Slot(ListenableDelegate.Box(null), StreamCodecs.RESOURCE_LOCATION.nullable()).also { it.addListener(Runnable { rescan() }) }
|
var selectedRecipe by mSynchronizer.add(ListenableDelegate.Box(null), StreamCodecs.RESOURCE_LOCATION.nullable()).also { it.addListener(Runnable { rescan() }) }
|
||||||
|
|
||||||
val isBulk = BooleanInputWithFeedback(this, tile?.let { it::isBulk })
|
val isBulk = BooleanInputWithFeedback(this, tile?.let { it::isBulk })
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ class DriveViewerMenu(
|
|||||||
|
|
||||||
private val powered: PoweredVirtualComponent<ItemStorageStack>?
|
private val powered: PoweredVirtualComponent<ItemStorageStack>?
|
||||||
private var lastDrive: IMatteryDrive<ItemStorageStack>? = null
|
private var lastDrive: IMatteryDrive<ItemStorageStack>? = null
|
||||||
val profiledEnergy = ProfiledLevelGaugeWidget<ProfiledEnergyStorage<*>>(this, energyWidget)
|
val profiledEnergy = ProfiledLevelGaugeWidget<ProfiledEnergyStorage<*>>(this, tile?.energy, energyWidget)
|
||||||
val energyConfig = EnergyConfigPlayerInput(this, tile?.energyConfig)
|
val energyConfig = EnergyConfigPlayerInput(this, tile?.energyConfig)
|
||||||
|
|
||||||
val settings = object : DriveViewerBlockEntity.ISettings {
|
val settings = object : DriveViewerBlockEntity.ISettings {
|
||||||
@ -62,7 +62,6 @@ class DriveViewerMenu(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
if (tile != null) {
|
if (tile != null) {
|
||||||
profiledEnergy.with(tile.energy)
|
|
||||||
powered = PoweredVirtualComponent(StorageStack.ITEMS, tile::energy)
|
powered = PoweredVirtualComponent(StorageStack.ITEMS, tile::energy)
|
||||||
this.networkedItemView.component = powered
|
this.networkedItemView.component = powered
|
||||||
} else {
|
} else {
|
||||||
|
@ -2,7 +2,7 @@ package ru.dbotthepony.mc.otm.menu.widget
|
|||||||
|
|
||||||
import net.neoforged.neoforge.fluids.FluidStack
|
import net.neoforged.neoforge.fluids.FluidStack
|
||||||
import net.neoforged.neoforge.fluids.capability.IFluidHandler
|
import net.neoforged.neoforge.fluids.capability.IFluidHandler
|
||||||
import ru.dbotthepony.mc.otm.network.DelegateSyncher
|
import ru.dbotthepony.mc.otm.network.syncher.Syncher
|
||||||
import ru.dbotthepony.kommons.util.getValue
|
import ru.dbotthepony.kommons.util.getValue
|
||||||
import ru.dbotthepony.mc.otm.container.get
|
import ru.dbotthepony.mc.otm.container.get
|
||||||
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||||
@ -10,7 +10,7 @@ import ru.dbotthepony.mc.otm.network.wrap
|
|||||||
import java.util.function.IntSupplier
|
import java.util.function.IntSupplier
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
|
|
||||||
class FluidGaugeWidget(synchronizer: DelegateSyncher) {
|
class FluidGaugeWidget(synchronizer: Syncher) {
|
||||||
constructor(menu: MatteryMenu) : this(menu.mSynchronizer)
|
constructor(menu: MatteryMenu) : this(menu.mSynchronizer)
|
||||||
|
|
||||||
var maxCapacitySupplier = IntSupplier { 0 }
|
var maxCapacitySupplier = IntSupplier { 0 }
|
||||||
@ -27,7 +27,7 @@ class FluidGaugeWidget(synchronizer: DelegateSyncher) {
|
|||||||
return (fluid.amount.toFloat() / maxCapacity.toFloat()).coerceIn(0f, 1f)
|
return (fluid.amount.toFloat() / maxCapacity.toFloat()).coerceIn(0f, 1f)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(synchronizer: DelegateSyncher, fluid: IFluidHandler?, tank: Int = 0) : this(synchronizer) {
|
constructor(synchronizer: Syncher, fluid: IFluidHandler?, tank: Int = 0) : this(synchronizer) {
|
||||||
if (fluid != null) {
|
if (fluid != null) {
|
||||||
with(fluid, tank)
|
with(fluid, tank)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package ru.dbotthepony.mc.otm.menu.widget
|
package ru.dbotthepony.mc.otm.menu.widget
|
||||||
|
|
||||||
import ru.dbotthepony.mc.otm.network.DelegateSyncher
|
|
||||||
import ru.dbotthepony.kommons.util.getValue
|
import ru.dbotthepony.kommons.util.getValue
|
||||||
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
||||||
import ru.dbotthepony.mc.otm.capability.matter.IMatterStorage
|
import ru.dbotthepony.mc.otm.capability.matter.IMatterStorage
|
||||||
@ -8,9 +7,10 @@ import ru.dbotthepony.mc.otm.capability.matter.IPatternStorage
|
|||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||||
import ru.dbotthepony.mc.otm.network.StreamCodecs
|
import ru.dbotthepony.mc.otm.network.StreamCodecs
|
||||||
|
import ru.dbotthepony.mc.otm.network.syncher.Syncher
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
class LevelGaugeWidget(synchronizer: DelegateSyncher) {
|
class LevelGaugeWidget(synchronizer: Syncher) {
|
||||||
constructor(menu: MatteryMenu) : this(menu.mSynchronizer)
|
constructor(menu: MatteryMenu) : this(menu.mSynchronizer)
|
||||||
|
|
||||||
var levelProvider = { Decimal.ONE }
|
var levelProvider = { Decimal.ONE }
|
||||||
@ -56,7 +56,7 @@ class LevelGaugeWidget(synchronizer: DelegateSyncher) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
synchronizer: DelegateSyncher,
|
synchronizer: Syncher,
|
||||||
power: IMatteryEnergyStorage?
|
power: IMatteryEnergyStorage?
|
||||||
) : this(synchronizer) {
|
) : this(synchronizer) {
|
||||||
if (power != null) {
|
if (power != null) {
|
||||||
@ -65,7 +65,7 @@ class LevelGaugeWidget(synchronizer: DelegateSyncher) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
synchronizer: DelegateSyncher,
|
synchronizer: Syncher,
|
||||||
matter: IMatterStorage?
|
matter: IMatterStorage?
|
||||||
) : this(synchronizer) {
|
) : this(synchronizer) {
|
||||||
if (matter != null) {
|
if (matter != null) {
|
||||||
@ -74,7 +74,7 @@ class LevelGaugeWidget(synchronizer: DelegateSyncher) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
synchronizer: DelegateSyncher,
|
synchronizer: Syncher,
|
||||||
patterns: IPatternStorage?
|
patterns: IPatternStorage?
|
||||||
) : this(synchronizer) {
|
) : this(synchronizer) {
|
||||||
if (patterns != null) {
|
if (patterns != null) {
|
||||||
@ -83,7 +83,7 @@ class LevelGaugeWidget(synchronizer: DelegateSyncher) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
synchronizer: DelegateSyncher,
|
synchronizer: Syncher,
|
||||||
level: () -> Decimal,
|
level: () -> Decimal,
|
||||||
maxLevel: () -> Decimal,
|
maxLevel: () -> Decimal,
|
||||||
) : this(synchronizer) {
|
) : this(synchronizer) {
|
||||||
|
@ -1,64 +1,40 @@
|
|||||||
package ru.dbotthepony.mc.otm.menu.widget
|
package ru.dbotthepony.mc.otm.menu.widget
|
||||||
|
|
||||||
import ru.dbotthepony.mc.otm.network.DelegateSyncher
|
|
||||||
import ru.dbotthepony.kommons.util.getValue
|
import ru.dbotthepony.kommons.util.getValue
|
||||||
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage
|
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage
|
||||||
|
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage.Companion.HISTORY_SIZE
|
||||||
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
||||||
import ru.dbotthepony.mc.otm.capability.matter.IMatterStorage
|
import ru.dbotthepony.mc.otm.capability.matter.IMatterStorage
|
||||||
import ru.dbotthepony.mc.otm.core.collect.SupplierList
|
import ru.dbotthepony.mc.otm.core.DecimalHistoryGraph
|
||||||
import ru.dbotthepony.mc.otm.core.immutableList
|
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||||
import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu
|
|
||||||
import ru.dbotthepony.mc.otm.network.StreamCodecs
|
import ru.dbotthepony.mc.otm.network.StreamCodecs
|
||||||
import java.util.function.IntSupplier
|
import ru.dbotthepony.mc.otm.network.syncher.Syncher
|
||||||
|
|
||||||
class ProfiledLevelGaugeWidget<P : AbstractProfiledStorage<*>>(synchronizer: DelegateSyncher, val gauge: LevelGaugeWidget = LevelGaugeWidget(synchronizer)) {
|
class ProfiledLevelGaugeWidget<P : AbstractProfiledStorage<*>>(
|
||||||
var parent: P? = null
|
synchronizer: Syncher,
|
||||||
private set
|
val storage: P?,
|
||||||
|
val gauge: LevelGaugeWidget = LevelGaugeWidget(synchronizer)
|
||||||
|
) {
|
||||||
|
constructor(
|
||||||
|
menu: MatteryMenu,
|
||||||
|
storage: P?,
|
||||||
|
gauge: LevelGaugeWidget = LevelGaugeWidget(menu.mSynchronizer)
|
||||||
|
) : this(menu.mSynchronizer, storage, gauge)
|
||||||
|
|
||||||
val historyReceive = immutableList(AbstractProfiledStorage.HISTORY_SIZE) {
|
val received = storage?.received ?: DecimalHistoryGraph(ticks = HISTORY_SIZE)
|
||||||
synchronizer.computed({ parent?.historyReceive?.get(it) ?: Decimal.ZERO }, StreamCodecs.DECIMAL)
|
val transferred = storage?.transferred ?: DecimalHistoryGraph(ticks = HISTORY_SIZE)
|
||||||
}
|
|
||||||
|
|
||||||
val historyTransfer = immutableList(AbstractProfiledStorage.HISTORY_SIZE) {
|
val receivedThisTick by synchronizer.computed({ storage?.receivedThisTick ?: Decimal.ZERO }, StreamCodecs.DECIMAL)
|
||||||
synchronizer.computed({ parent?.historyTransfer?.get(it) ?: Decimal.ZERO }, StreamCodecs.DECIMAL)
|
val transferredThisTick by synchronizer.computed({ storage?.transferredThisTick ?: Decimal.ZERO }, StreamCodecs.DECIMAL)
|
||||||
}
|
|
||||||
|
|
||||||
val directHistoryReceive = SupplierList(historyReceive)
|
init {
|
||||||
val directHistoryTransfer = SupplierList(historyTransfer)
|
|
||||||
|
|
||||||
val tick by synchronizer.computedInt(IntSupplier { parent?.tick ?: 0 })
|
|
||||||
val lastTickReceive by synchronizer.computed({ parent?.lastTickReceive ?: Decimal.ZERO }, StreamCodecs.DECIMAL)
|
|
||||||
val lastTickTransfer by synchronizer.computed({ parent?.lastTickTransfer ?: Decimal.ZERO }, StreamCodecs.DECIMAL)
|
|
||||||
|
|
||||||
constructor(synchronizer: DelegateSyncher, 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)
|
if (storage is IMatterStorage)
|
||||||
gauge.with(storage)
|
gauge.with(storage)
|
||||||
else if (storage is IMatteryEnergyStorage)
|
else if (storage is IMatteryEnergyStorage)
|
||||||
gauge.with(storage)
|
gauge.with(storage)
|
||||||
|
|
||||||
parent = storage
|
synchronizer.add(received)
|
||||||
return this
|
synchronizer.add(transferred)
|
||||||
}
|
|
||||||
|
|
||||||
val weightedTransfer: Decimal get() {
|
|
||||||
return AbstractProfiledStorage.calcWeightedAverage(directHistoryTransfer, tick)
|
|
||||||
}
|
|
||||||
|
|
||||||
val weightedReceive: Decimal get() {
|
|
||||||
return AbstractProfiledStorage.calcWeightedAverage(directHistoryReceive, tick)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package ru.dbotthepony.mc.otm.menu.widget
|
package ru.dbotthepony.mc.otm.menu.widget
|
||||||
|
|
||||||
import ru.dbotthepony.mc.otm.network.DelegateSyncher
|
import ru.dbotthepony.mc.otm.network.syncher.Syncher
|
||||||
import ru.dbotthepony.kommons.util.getValue
|
import ru.dbotthepony.kommons.util.getValue
|
||||||
import ru.dbotthepony.mc.otm.block.entity.MachineJobEventLoop
|
import ru.dbotthepony.mc.otm.block.entity.MachineJobEventLoop
|
||||||
import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity
|
import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity
|
||||||
@ -9,7 +9,7 @@ import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
|||||||
import java.util.function.BooleanSupplier
|
import java.util.function.BooleanSupplier
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
class ProgressGaugeWidget(synchronizer: DelegateSyncher) {
|
class ProgressGaugeWidget(synchronizer: Syncher) {
|
||||||
constructor(menu: MatteryMenu) : this(menu.mSynchronizer)
|
constructor(menu: MatteryMenu) : this(menu.mSynchronizer)
|
||||||
|
|
||||||
var progressSupplier: FloatSupplier = FloatSupplier { 0f }
|
var progressSupplier: FloatSupplier = FloatSupplier { 0f }
|
||||||
@ -44,14 +44,14 @@ class ProgressGaugeWidget(synchronizer: DelegateSyncher) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
synchronizer: DelegateSyncher,
|
synchronizer: Syncher,
|
||||||
progress: FloatSupplier
|
progress: FloatSupplier
|
||||||
) : this(synchronizer) {
|
) : this(synchronizer) {
|
||||||
this.progressSupplier = progress
|
this.progressSupplier = progress
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
synchronizer: DelegateSyncher,
|
synchronizer: Syncher,
|
||||||
blockEntity: MatteryWorkerBlockEntity<*>?
|
blockEntity: MatteryWorkerBlockEntity<*>?
|
||||||
) : this(synchronizer) {
|
) : this(synchronizer) {
|
||||||
if (blockEntity != null) {
|
if (blockEntity != null) {
|
||||||
@ -60,7 +60,7 @@ class ProgressGaugeWidget(synchronizer: DelegateSyncher) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
synchronizer: DelegateSyncher,
|
synchronizer: Syncher,
|
||||||
job: MachineJobEventLoop<*>?
|
job: MachineJobEventLoop<*>?
|
||||||
) : this(synchronizer) {
|
) : this(synchronizer) {
|
||||||
if (job != null) {
|
if (job != null) {
|
||||||
|
@ -1,857 +0,0 @@
|
|||||||
package ru.dbotthepony.mc.otm.network
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBufAllocator
|
|
||||||
import it.unimi.dsi.fastutil.bytes.ByteArrayList
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectAVLTreeSet
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
|
|
||||||
import net.minecraft.core.RegistryAccess
|
|
||||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
|
||||||
import net.minecraft.world.item.ItemStack
|
|
||||||
import net.neoforged.neoforge.network.connection.ConnectionType
|
|
||||||
import ru.dbotthepony.kommons.collect.ListenableMap
|
|
||||||
import ru.dbotthepony.kommons.collect.ListenableSet
|
|
||||||
import ru.dbotthepony.kommons.util.Delegate
|
|
||||||
import ru.dbotthepony.kommons.util.DelegateGetter
|
|
||||||
import ru.dbotthepony.kommons.util.DelegateSetter
|
|
||||||
import ru.dbotthepony.kommons.util.KOptional
|
|
||||||
import ru.dbotthepony.kommons.util.Listenable
|
|
||||||
import ru.dbotthepony.kommons.util.ListenableDelegate
|
|
||||||
import ru.dbotthepony.kommons.util.Observer
|
|
||||||
import ru.dbotthepony.kommons.util.ValueObserver
|
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
|
||||||
import java.io.Closeable
|
|
||||||
import java.util.*
|
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
|
||||||
import java.util.concurrent.locks.ReentrantLock
|
|
||||||
import java.util.function.BooleanSupplier
|
|
||||||
import java.util.function.Consumer
|
|
||||||
import java.util.function.DoubleSupplier
|
|
||||||
import java.util.function.IntSupplier
|
|
||||||
import java.util.function.LongSupplier
|
|
||||||
import java.util.function.Supplier
|
|
||||||
import kotlin.concurrent.withLock
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Universal, one-to-many delegate/value synchronizer.
|
|
||||||
*
|
|
||||||
* Values and delegates can be attached using [add], or by constructing subclassed directly.
|
|
||||||
* Delta changes are tracked by [DelegateSyncher.Remote] instances.
|
|
||||||
*
|
|
||||||
* In general, this class is not meant to be _structurally_ concurrently mutated by different threads,
|
|
||||||
* to avoid structure corruption a lock is employed.
|
|
||||||
*
|
|
||||||
* Attached delegates can be safely mutated by multiple threads concurrently
|
|
||||||
* (attached [ListenableDelegate] can call [Listenable.addListener] callback from different threads).
|
|
||||||
*/
|
|
||||||
@Suppress("UNCHECKED_CAST", "UNUSED")
|
|
||||||
class DelegateSyncher : Observer {
|
|
||||||
private val lock = ReentrantLock()
|
|
||||||
private val slots = ArrayList<AbstractSlot?>()
|
|
||||||
private val gaps = IntAVLTreeSet()
|
|
||||||
private val observers = ArrayList<AbstractSlot>()
|
|
||||||
private val remotes = ArrayList<Remote>()
|
|
||||||
private var isRemote = false
|
|
||||||
|
|
||||||
override fun observe(): Boolean {
|
|
||||||
var any = false
|
|
||||||
observers.forEach { any = it.observe() || any }
|
|
||||||
return any
|
|
||||||
}
|
|
||||||
|
|
||||||
fun read(stream: RegistryFriendlyByteBuf) {
|
|
||||||
isRemote = true
|
|
||||||
|
|
||||||
var readID = stream.readVarInt()
|
|
||||||
|
|
||||||
while (readID > 0) {
|
|
||||||
val slot = slots.getOrNull(readID - 1) ?: throw IndexOutOfBoundsException("Unknown networked slot ${readID - 1}!")
|
|
||||||
slot.read(stream)
|
|
||||||
readID = stream.readVarInt()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun read(registry: RegistryAccess, bytes: ByteArrayList) {
|
|
||||||
return decodePayload(registry, bytes) {
|
|
||||||
read(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract inner class AbstractSlot : Closeable, Observer, Comparable<AbstractSlot> {
|
|
||||||
val id: Int
|
|
||||||
|
|
||||||
protected val isRemoved = AtomicBoolean()
|
|
||||||
|
|
||||||
fun remove() {
|
|
||||||
if (isRemoved.compareAndSet(false, true)) {
|
|
||||||
lock.withLock {
|
|
||||||
slots[id] = null
|
|
||||||
gaps.add(id)
|
|
||||||
|
|
||||||
remoteSlots.forEach {
|
|
||||||
it.remove()
|
|
||||||
}
|
|
||||||
|
|
||||||
remoteSlots.clear()
|
|
||||||
remove0()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun close() {
|
|
||||||
return remove()
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun compareTo(other: AbstractSlot): Int {
|
|
||||||
return id.compareTo(other.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract fun remove0()
|
|
||||||
internal abstract fun read(stream: RegistryFriendlyByteBuf)
|
|
||||||
internal abstract fun write(stream: RegistryFriendlyByteBuf)
|
|
||||||
|
|
||||||
internal val remoteSlots = CopyOnWriteArrayList<Remote.RemoteSlot>()
|
|
||||||
|
|
||||||
protected fun markDirty() {
|
|
||||||
if (!isRemote && !isRemoved.get()) {
|
|
||||||
remoteSlots.forEach {
|
|
||||||
it.markDirty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
lock.withLock {
|
|
||||||
if (gaps.isNotEmpty()) {
|
|
||||||
val gap = gaps.firstInt()
|
|
||||||
gaps.remove(gap)
|
|
||||||
id = gap
|
|
||||||
check(slots[id] == null) { "Expected slot $id to be empty" }
|
|
||||||
slots[id] = this
|
|
||||||
} else {
|
|
||||||
id = slots.size
|
|
||||||
slots.add(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
remotes.forEach {
|
|
||||||
it.addSlot(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class Slot<V>(val delegate: ListenableDelegate<V>, val codec: MatteryStreamCodec<in RegistryFriendlyByteBuf, V>) : AbstractSlot(), ListenableDelegate<V> {
|
|
||||||
constructor(delegate: Supplier<V>, codec: MatteryStreamCodec<in RegistryFriendlyByteBuf, V>) : this(ListenableDelegate.CustomShadow(delegate, codec::compare, codec::copy), codec)
|
|
||||||
|
|
||||||
private val l = delegate.addListener(Consumer {
|
|
||||||
markDirty()
|
|
||||||
listeners.accept(it)
|
|
||||||
})
|
|
||||||
|
|
||||||
init {
|
|
||||||
if (delegate is ListenableDelegate.AbstractShadow) {
|
|
||||||
observers.add(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val listeners = Listenable.Impl<V>()
|
|
||||||
|
|
||||||
override fun remove0() {
|
|
||||||
l.remove()
|
|
||||||
listeners.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun read(stream: RegistryFriendlyByteBuf) {
|
|
||||||
check(!isRemoved.get()) { "This network slot was removed" }
|
|
||||||
delegate.accept(codec.decode(stream))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun write(stream: RegistryFriendlyByteBuf) {
|
|
||||||
check(!isRemoved.get()) { "This network slot was removed" }
|
|
||||||
codec.encode(stream, delegate.get())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun get(): V {
|
|
||||||
check(!isRemoved.get()) { "This network slot was removed" }
|
|
||||||
return delegate.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun accept(t: V) {
|
|
||||||
check(!isRemoved.get()) { "This network slot was removed" }
|
|
||||||
delegate.accept(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addListener(listener: Consumer<V>): Listenable.L {
|
|
||||||
check(!isRemoved.get()) { "This network slot was removed" }
|
|
||||||
return listeners.addListener(listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun observe(): Boolean {
|
|
||||||
check(!isRemoved.get()) { "This network slot was removed" }
|
|
||||||
|
|
||||||
if (delegate is ListenableDelegate.AbstractShadow) {
|
|
||||||
return delegate.observe()
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class ObservedSlot<V>(val delegate: Delegate<V>, val codec: MatteryStreamCodec<in RegistryFriendlyByteBuf, V>) : AbstractSlot(), ListenableDelegate<V>, ValueObserver<V> {
|
|
||||||
private val listeners = Listenable.Impl<V>()
|
|
||||||
private var observed: Any? = Mark
|
|
||||||
private val observeLock = ReentrantLock()
|
|
||||||
|
|
||||||
init {
|
|
||||||
observers.add(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun remove0() {
|
|
||||||
listeners.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun read(stream: RegistryFriendlyByteBuf) {
|
|
||||||
check(!isRemoved.get()) { "This network slot was removed" }
|
|
||||||
val read = codec.decode(stream)
|
|
||||||
observed = codec.copy(read)
|
|
||||||
delegate.accept(read)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun write(stream: RegistryFriendlyByteBuf) {
|
|
||||||
check(!isRemoved.get()) { "This network slot was removed" }
|
|
||||||
codec.encode(stream, delegate.get())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun observe(): Boolean {
|
|
||||||
val get = delegate.get()
|
|
||||||
|
|
||||||
if (observed === Mark || !codec.compare(get, observed as V)) {
|
|
||||||
observeLock.withLock {
|
|
||||||
if (observed === Mark || !codec.compare(get, observed as V)) {
|
|
||||||
observed = codec.copy(get)
|
|
||||||
markDirty()
|
|
||||||
listeners.accept(get)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getAndObserve(): Pair<V, Boolean> {
|
|
||||||
val get = delegate.get()
|
|
||||||
|
|
||||||
if (observed === Mark || !codec.compare(get, observed as V)) {
|
|
||||||
observeLock.withLock {
|
|
||||||
if (observed === Mark || !codec.compare(get, observed as V)) {
|
|
||||||
observed = codec.copy(get)
|
|
||||||
markDirty()
|
|
||||||
listeners.accept(get)
|
|
||||||
}
|
|
||||||
|
|
||||||
return get to true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return get to false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun get(): V {
|
|
||||||
check(!isRemoved.get()) { "This network slot was removed" }
|
|
||||||
return delegate.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun accept(t: V) {
|
|
||||||
check(!isRemoved.get()) { "This network slot was removed" }
|
|
||||||
delegate.accept(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addListener(listener: Consumer<V>): Listenable.L {
|
|
||||||
check(!isRemoved.get()) { "This network slot was removed" }
|
|
||||||
return listeners.addListener(listener)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private object Mark
|
|
||||||
|
|
||||||
internal data class SetAction<V>(val value: KOptional<V>, val action: Int)
|
|
||||||
|
|
||||||
inner class SetSlot<V>(val delegate: ListenableSet<V>, val codec: MatteryStreamCodec<in RegistryFriendlyByteBuf, V>) : AbstractSlot() {
|
|
||||||
private val listener = object : ListenableSet.SetListener<V> {
|
|
||||||
override fun onClear() {
|
|
||||||
remoteSlots.forEach {
|
|
||||||
(it as Remote.RemoteSetSlot<V>).changelist.clear()
|
|
||||||
|
|
||||||
synchronized(it.changelistLock) {
|
|
||||||
it.changelist.add(SetAction(KOptional(), CLEAR))
|
|
||||||
}
|
|
||||||
|
|
||||||
listeners.forEach {
|
|
||||||
it.listener.onClear()
|
|
||||||
}
|
|
||||||
|
|
||||||
it.markDirty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onValueAdded(element: V) {
|
|
||||||
remoteSlots.forEach {
|
|
||||||
it as Remote.RemoteSetSlot<V>
|
|
||||||
|
|
||||||
synchronized(it.changelistLock) {
|
|
||||||
it.changelist.removeIf { it.value.valueEquals(element) }
|
|
||||||
it.changelist.add(SetAction(KOptional(codec.copy(element)), ADD))
|
|
||||||
}
|
|
||||||
|
|
||||||
listeners.forEach {
|
|
||||||
it.listener.onValueAdded(element)
|
|
||||||
}
|
|
||||||
|
|
||||||
it.markDirty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onValueRemoved(element: V) {
|
|
||||||
remoteSlots.forEach {
|
|
||||||
it as Remote.RemoteSetSlot<V>
|
|
||||||
|
|
||||||
synchronized(it.changelistLock) {
|
|
||||||
it.changelist.removeIf { it.value.valueEquals(element) }
|
|
||||||
it.changelist.add(SetAction(KOptional(codec.copy(element)), REMOVE))
|
|
||||||
}
|
|
||||||
|
|
||||||
listeners.forEach {
|
|
||||||
it.listener.onValueRemoved(element)
|
|
||||||
}
|
|
||||||
|
|
||||||
it.markDirty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val l = delegate.addListener(listener)
|
|
||||||
private val listeners = CopyOnWriteArrayList<Listener>()
|
|
||||||
|
|
||||||
override fun remove0() {
|
|
||||||
l.remove()
|
|
||||||
listeners.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class Listener(val listener: ListenableSet.SetListener<V>): Listenable.L {
|
|
||||||
private var isRemoved = false
|
|
||||||
|
|
||||||
init {
|
|
||||||
listeners.add(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun remove() {
|
|
||||||
if (!isRemoved) {
|
|
||||||
isRemoved = true
|
|
||||||
listeners.remove(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addListener(listener: ListenableSet.SetListener<V>): Listenable.L {
|
|
||||||
check(!isRemoved.get()) { "This network slot was removed" }
|
|
||||||
return Listener(listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addListener(listener: Runnable): Listenable.L {
|
|
||||||
check(!isRemoved.get()) { "This network slot was removed" }
|
|
||||||
return Listener(ListenableSet.RunnableAdapter(listener))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun read(stream: RegistryFriendlyByteBuf) {
|
|
||||||
check(!isRemoved.get()) { "This network slot was removed" }
|
|
||||||
var action = stream.readByte().toInt()
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
when (action) {
|
|
||||||
ADD -> delegate.add(codec.decode(stream))
|
|
||||||
REMOVE -> delegate.remove(codec.decode(stream))
|
|
||||||
CLEAR -> delegate.clear()
|
|
||||||
else -> break
|
|
||||||
}
|
|
||||||
|
|
||||||
action = stream.readByte().toInt()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun write(stream: RegistryFriendlyByteBuf) {
|
|
||||||
check(!isRemoved.get()) { "This network slot was removed" }
|
|
||||||
throw RuntimeException("unreachable code")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun observe(): Boolean {
|
|
||||||
check(!isRemoved.get()) { "This network slot was removed" }
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal data class MapAction<K, V>(val key: KOptional<K>, val value: KOptional<V>, val action: Int)
|
|
||||||
|
|
||||||
inner class MapSlot<K, V>(val delegate: ListenableMap<K, V>, val keyCodec: MatteryStreamCodec<in RegistryFriendlyByteBuf, K>, val valueCodec: MatteryStreamCodec<in RegistryFriendlyByteBuf, V>) : AbstractSlot() {
|
|
||||||
private val listener = object : ListenableMap.MapListener<K, V> {
|
|
||||||
override fun onClear() {
|
|
||||||
remoteSlots.forEach {
|
|
||||||
it as Remote.RemoteMapSlot<K, V>
|
|
||||||
|
|
||||||
synchronized(it.changelistLock) {
|
|
||||||
it.changelist.clear()
|
|
||||||
it.changelist.add(MapAction(KOptional(), KOptional(), CLEAR))
|
|
||||||
}
|
|
||||||
|
|
||||||
listeners.forEach {
|
|
||||||
it.listener.onClear()
|
|
||||||
}
|
|
||||||
|
|
||||||
it.markDirty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onValueAdded(key: K, value: V) {
|
|
||||||
remoteSlots.forEach {
|
|
||||||
it as Remote.RemoteMapSlot<K, V>
|
|
||||||
|
|
||||||
synchronized(it.changelistLock) {
|
|
||||||
it.changelist.removeIf { it.key.valueEquals(key) }
|
|
||||||
it.changelist.add(MapAction(KOptional(keyCodec.copy(key)), KOptional(valueCodec.copy(value)), ADD))
|
|
||||||
}
|
|
||||||
|
|
||||||
listeners.forEach {
|
|
||||||
it.listener.onValueAdded(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
it.markDirty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onValueRemoved(key: K, value: V) {
|
|
||||||
remoteSlots.forEach {
|
|
||||||
it as Remote.RemoteMapSlot<K, V>
|
|
||||||
|
|
||||||
synchronized(it.changelistLock) {
|
|
||||||
it.changelist.removeIf { it.key.valueEquals(key) }
|
|
||||||
it.changelist.add(MapAction(KOptional(keyCodec.copy(key)), KOptional(), REMOVE))
|
|
||||||
}
|
|
||||||
|
|
||||||
listeners.forEach {
|
|
||||||
it.listener.onValueRemoved(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
it.markDirty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val l = delegate.addListener(listener)
|
|
||||||
private val listeners = CopyOnWriteArrayList<Listener>()
|
|
||||||
|
|
||||||
override fun remove0() {
|
|
||||||
l.remove()
|
|
||||||
listeners.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class Listener(val listener: ListenableMap.MapListener<K, V>): Listenable.L {
|
|
||||||
private var isRemoved = false
|
|
||||||
|
|
||||||
init {
|
|
||||||
listeners.add(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun remove() {
|
|
||||||
if (!isRemoved) {
|
|
||||||
isRemoved = true
|
|
||||||
listeners.remove(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addListener(listener: ListenableMap.MapListener<K, V>): Listenable.L {
|
|
||||||
check(!isRemoved.get()) { "This network slot was removed" }
|
|
||||||
return Listener(listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addListener(listener: Runnable): Listenable.L {
|
|
||||||
check(!isRemoved.get()) { "This network slot was removed" }
|
|
||||||
return Listener(ListenableMap.RunnableAdapter(listener))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun read(stream: RegistryFriendlyByteBuf) {
|
|
||||||
check(!isRemoved.get()) { "This network slot was removed" }
|
|
||||||
var action = stream.readByte().toInt()
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
when (action) {
|
|
||||||
ADD -> delegate.put(keyCodec.decode(stream), valueCodec.decode(stream))
|
|
||||||
REMOVE -> delegate.remove(keyCodec.decode(stream))
|
|
||||||
CLEAR -> delegate.clear()
|
|
||||||
else -> break
|
|
||||||
}
|
|
||||||
|
|
||||||
action = stream.readByte().toInt()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun write(stream: RegistryFriendlyByteBuf) {
|
|
||||||
check(!isRemoved.get()) { "This network slot was removed" }
|
|
||||||
throw RuntimeException("unreachable code")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun observe(): Boolean {
|
|
||||||
check(!isRemoved.get()) { "This network slot was removed" }
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <V> add(value: V, codec: MatteryStreamCodec<in RegistryFriendlyByteBuf, V>, setter: DelegateSetter<V> = DelegateSetter.passthrough(), getter: DelegateGetter<V> = DelegateGetter.passthrough()): Slot<V> {
|
|
||||||
return Slot(ListenableDelegate.maskSmart(value, getter, setter), codec)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <V> add(delegate: ListenableDelegate<V>, codec: MatteryStreamCodec<in RegistryFriendlyByteBuf, V>): Slot<V> {
|
|
||||||
return Slot(delegate, codec)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <V> add(delegate: Delegate<V>, codec: MatteryStreamCodec<in RegistryFriendlyByteBuf, V>): ObservedSlot<V> {
|
|
||||||
return ObservedSlot(delegate, codec)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <V> add(delegate: Supplier<V>, codec: MatteryStreamCodec<in RegistryFriendlyByteBuf, V>): Slot<V> {
|
|
||||||
return computed(delegate, codec)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <E> add(delegate: ListenableSet<E>, codec: MatteryStreamCodec<in RegistryFriendlyByteBuf, E>): SetSlot<E> {
|
|
||||||
return SetSlot(delegate, codec)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <K, V> add(delegate: ListenableMap<K, V>, keyCodec: MatteryStreamCodec<in RegistryFriendlyByteBuf, K>, valueCodec: MatteryStreamCodec<in RegistryFriendlyByteBuf, V>): MapSlot<K, V> {
|
|
||||||
return MapSlot(delegate, keyCodec, valueCodec)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun computedByte(delegate: Supplier<Byte>) = Slot(ListenableDelegate.Shadow(delegate), StreamCodecs.BYTE)
|
|
||||||
fun computedShort(delegate: Supplier<Short>) = Slot(ListenableDelegate.Shadow(delegate), StreamCodecs.SHORT)
|
|
||||||
fun computedInt(delegate: Supplier<Int>) = Slot(ListenableDelegate.Shadow(delegate), StreamCodecs.INT)
|
|
||||||
fun computedLong(delegate: Supplier<Long>) = Slot(ListenableDelegate.Shadow(delegate), StreamCodecs.VAR_LONG)
|
|
||||||
fun computedFloat(delegate: Supplier<Float>) = Slot(ListenableDelegate.Shadow(delegate), StreamCodecs.FLOAT)
|
|
||||||
fun computedDouble(delegate: Supplier<Double>) = Slot(ListenableDelegate.Shadow(delegate), StreamCodecs.DOUBLE)
|
|
||||||
fun computedBoolean(delegate: Supplier<Boolean>) = Slot(ListenableDelegate.Shadow(delegate), StreamCodecs.BOOLEAN)
|
|
||||||
fun computedString(delegate: Supplier<String>) = Slot(ListenableDelegate.Shadow(delegate), StreamCodecs.STRING)
|
|
||||||
fun computedUUID(delegate: Supplier<UUID>) = Slot(ListenableDelegate.Shadow(delegate), StreamCodecs.UUID)
|
|
||||||
fun <V> computed(delegate: Supplier<V>, codec: MatteryStreamCodec<in RegistryFriendlyByteBuf, V>) = Slot(ListenableDelegate.CustomShadow(delegate, codec::compare, codec::copy), codec)
|
|
||||||
|
|
||||||
fun computedInt(delegate: IntSupplier) = Slot(ListenableDelegate.Shadow(delegate::getAsInt), StreamCodecs.VAR_INT)
|
|
||||||
fun computedLong(delegate: LongSupplier) = Slot(ListenableDelegate.Shadow(delegate::getAsLong), StreamCodecs.VAR_LONG)
|
|
||||||
fun computedDouble(delegate: DoubleSupplier) = Slot(ListenableDelegate.Shadow(delegate::getAsDouble), StreamCodecs.DOUBLE)
|
|
||||||
fun computedBoolean(delegate: BooleanSupplier) = Slot(ListenableDelegate.Shadow(delegate::getAsBoolean), StreamCodecs.BOOLEAN)
|
|
||||||
|
|
||||||
@JvmName("vbyte")
|
|
||||||
@JvmOverloads
|
|
||||||
fun byte(value: Byte = 0, setter: DelegateSetter<Byte> = DelegateSetter.passthrough(), getter: DelegateGetter<Byte> = DelegateGetter.passthrough()): Slot<Byte> {
|
|
||||||
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.BYTE)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("vshort")
|
|
||||||
@JvmOverloads
|
|
||||||
fun short(value: Short = 0, setter: DelegateSetter<Short> = DelegateSetter.passthrough(), getter: DelegateGetter<Short> = DelegateGetter.passthrough()): Slot<Short> {
|
|
||||||
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.SHORT)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("vint")
|
|
||||||
@JvmOverloads
|
|
||||||
fun int(value: Int = 0, setter: DelegateSetter<Int> = DelegateSetter.passthrough(), getter: DelegateGetter<Int> = DelegateGetter.passthrough()): Slot<Int> {
|
|
||||||
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.VAR_INT)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("vlong")
|
|
||||||
@JvmOverloads
|
|
||||||
fun long(value: Long = 0L, setter: DelegateSetter<Long> = DelegateSetter.passthrough(), getter: DelegateGetter<Long> = DelegateGetter.passthrough()): Slot<Long> {
|
|
||||||
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.VAR_LONG)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("vfloat")
|
|
||||||
@JvmOverloads
|
|
||||||
fun float(value: Float = 0f, setter: DelegateSetter<Float> = DelegateSetter.passthrough(), getter: DelegateGetter<Float> = DelegateGetter.passthrough()): Slot<Float> {
|
|
||||||
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.FLOAT)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("vdouble")
|
|
||||||
@JvmOverloads
|
|
||||||
fun double(value: Double = 0.0, setter: DelegateSetter<Double> = DelegateSetter.passthrough(), getter: DelegateGetter<Double> = DelegateGetter.passthrough()): Slot<Double> {
|
|
||||||
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.DOUBLE)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("vboolean")
|
|
||||||
@JvmOverloads
|
|
||||||
fun boolean(value: Boolean = false, setter: DelegateSetter<Boolean> = DelegateSetter.passthrough(), getter: DelegateGetter<Boolean> = DelegateGetter.passthrough()): Slot<Boolean> {
|
|
||||||
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.BOOLEAN)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmOverloads
|
|
||||||
fun string(value: String, setter: DelegateSetter<String> = DelegateSetter.passthrough(), getter: DelegateGetter<String> = DelegateGetter.passthrough()): Slot<String> {
|
|
||||||
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.STRING)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmOverloads
|
|
||||||
fun uuid(value: UUID, setter: DelegateSetter<UUID> = DelegateSetter.passthrough(), getter: DelegateGetter<UUID> = DelegateGetter.passthrough()): Slot<UUID> {
|
|
||||||
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.UUID)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmOverloads
|
|
||||||
fun <E> set(codec: MatteryStreamCodec<in RegistryFriendlyByteBuf, E>, backing: MutableSet<E> = ObjectOpenHashSet()): SetSlot<E> {
|
|
||||||
return add(ListenableSet(backing), codec)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmOverloads
|
|
||||||
fun <K, V> map(keyCodec: MatteryStreamCodec<in RegistryFriendlyByteBuf, K>, valueCodec: MatteryStreamCodec<in RegistryFriendlyByteBuf, V>, backing: MutableMap<K, V> = Object2ObjectOpenHashMap()): MapSlot<K, V> {
|
|
||||||
return add(ListenableMap(backing), keyCodec, valueCodec)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmOverloads
|
|
||||||
fun <E : Enum<E>> enum(value: E, setter: DelegateSetter<E> = DelegateSetter.passthrough(), getter: DelegateGetter<E> = DelegateGetter.passthrough()): Slot<E> {
|
|
||||||
return add(ListenableDelegate.maskSmart(value, getter, setter), MatteryStreamCodec.Enum(value::class.java))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun decimal(value: Decimal = Decimal.ZERO, setter: DelegateSetter<Decimal> = DelegateSetter.passthrough(), getter: DelegateGetter<Decimal> = DelegateGetter.passthrough()): DelegateSyncher.Slot<Decimal> {
|
|
||||||
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.DECIMAL)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun computedDecimal(delegate: Supplier<Decimal>): DelegateSyncher.Slot<Decimal> {
|
|
||||||
return add(delegate, StreamCodecs.DECIMAL)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun item(value: ItemStack = ItemStack.EMPTY, setter: DelegateSetter<ItemStack> = DelegateSetter.passthrough(), getter: DelegateGetter<ItemStack> = DelegateGetter.passthrough()): DelegateSyncher.Slot<ItemStack> {
|
|
||||||
return add(ListenableDelegate.maskSmart(value, getter, setter), ItemStack.OPTIONAL_STREAM_CODEC.wrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun computedItem(delegate: Supplier<ItemStack>): DelegateSyncher.Slot<ItemStack> {
|
|
||||||
return computed(delegate, ItemStack.OPTIONAL_STREAM_CODEC.wrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun observedItem(value: ItemStack = ItemStack.EMPTY, setter: DelegateSetter<ItemStack> = DelegateSetter.passthrough(), getter: DelegateGetter<ItemStack> = DelegateGetter.passthrough()): DelegateSyncher.ObservedSlot<ItemStack> {
|
|
||||||
return add(Delegate.maskSmart(value, getter, setter), ItemStack.OPTIONAL_STREAM_CODEC.wrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remotes must be [close]d when no longer in use, otherwise they will
|
|
||||||
* leak memory and decrease performance of syncher.
|
|
||||||
*
|
|
||||||
* To get changes to be networked to remote client, [write] should be called.
|
|
||||||
*/
|
|
||||||
inner class Remote : Closeable {
|
|
||||||
@Volatile
|
|
||||||
private var isRemoved = false
|
|
||||||
internal val dirty = ConcurrentLinkedQueue<RemoteSlot>()
|
|
||||||
private val remoteSlots = CopyOnWriteArrayList<RemoteSlot>()
|
|
||||||
|
|
||||||
internal open inner class RemoteSlot(val parent: AbstractSlot) : Comparable<RemoteSlot> {
|
|
||||||
private val isDirty = AtomicBoolean()
|
|
||||||
|
|
||||||
final override fun compareTo(other: RemoteSlot): Int {
|
|
||||||
return parent.compareTo(other.parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
remoteSlots.add(this)
|
|
||||||
dirty.add(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun markDirty() {
|
|
||||||
if (!isRemoved && isDirty.compareAndSet(false, true)) {
|
|
||||||
dirty.add(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun markClean() {
|
|
||||||
isDirty.set(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun invalidate() {
|
|
||||||
markDirty()
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun remove() {
|
|
||||||
isDirty.set(true)
|
|
||||||
remoteSlots.remove(this)
|
|
||||||
dirty.remove(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun write(stream: RegistryFriendlyByteBuf) {
|
|
||||||
parent.write(stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
return parent.hashCode()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
return this === other || other is Remote.RemoteSlot && other.parent == parent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal inner class RemoteSetSlot<V>(parent: SetSlot<V>) : RemoteSlot(parent) {
|
|
||||||
val changelist = ArrayList<SetAction<V>>()
|
|
||||||
val changelistLock = Any()
|
|
||||||
|
|
||||||
override fun remove() {
|
|
||||||
super.remove()
|
|
||||||
changelist.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun invalidate() {
|
|
||||||
synchronized(changelistLock) {
|
|
||||||
changelist.clear()
|
|
||||||
changelist.add(SetAction(KOptional(), CLEAR))
|
|
||||||
|
|
||||||
parent as SetSlot<V>
|
|
||||||
|
|
||||||
for (v in parent.delegate) {
|
|
||||||
changelist.add(SetAction(KOptional(parent.codec.copy(v)), ADD))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
super.invalidate()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun write(stream: RegistryFriendlyByteBuf) {
|
|
||||||
synchronized(changelistLock) {
|
|
||||||
for (change in changelist) {
|
|
||||||
stream.writeByte(change.action)
|
|
||||||
change.value.ifPresent { (parent as SetSlot<V>).codec.encode(stream, it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
changelist.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.writeByte(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal inner class RemoteMapSlot<K, V>(parent: MapSlot<K, V>) : RemoteSlot(parent) {
|
|
||||||
val changelist = ArrayList<MapAction<K, V>>()
|
|
||||||
val changelistLock = Any()
|
|
||||||
|
|
||||||
override fun remove() {
|
|
||||||
super.remove()
|
|
||||||
changelist.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun invalidate() {
|
|
||||||
synchronized(changelistLock) {
|
|
||||||
changelist.clear()
|
|
||||||
changelist.add(MapAction(KOptional(), KOptional(), CLEAR))
|
|
||||||
|
|
||||||
parent as MapSlot<K, V>
|
|
||||||
|
|
||||||
for ((k, v) in parent.delegate) {
|
|
||||||
changelist.add(MapAction(KOptional(parent.keyCodec.copy(k)), KOptional(parent.valueCodec.copy(v)), ADD))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
super.invalidate()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun write(stream: RegistryFriendlyByteBuf) {
|
|
||||||
synchronized(changelistLock) {
|
|
||||||
for (change in changelist) {
|
|
||||||
stream.writeByte(change.action)
|
|
||||||
change.key.ifPresent { (parent as MapSlot<K, V>).keyCodec.encode(stream, it) }
|
|
||||||
change.value.ifPresent { (parent as MapSlot<K, V>).valueCodec.encode(stream, it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
changelist.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.writeByte(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
lock.withLock {
|
|
||||||
slots.forEach {
|
|
||||||
if (it != null) {
|
|
||||||
addSlot(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
remotes.add(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun addSlot(slot: AbstractSlot) {
|
|
||||||
if (slot is SetSlot<*>) {
|
|
||||||
slot.remoteSlots.add(RemoteSetSlot(slot))
|
|
||||||
} else if (slot is MapSlot<*, *>) {
|
|
||||||
slot.remoteSlots.add(RemoteMapSlot(slot))
|
|
||||||
} else {
|
|
||||||
slot.remoteSlots.add(RemoteSlot(slot))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns null if this remote is clean.
|
|
||||||
*
|
|
||||||
* [DelegateSyncher.observe] is not called automatically for performance
|
|
||||||
* reasons, you must call it manually.
|
|
||||||
*/
|
|
||||||
fun write(registry: RegistryAccess): ByteArrayList? {
|
|
||||||
if (dirty.isNotEmpty()) {
|
|
||||||
val sorted = ObjectAVLTreeSet<RemoteSlot>()
|
|
||||||
var next = dirty.poll()
|
|
||||||
|
|
||||||
while (next != null) {
|
|
||||||
sorted.add(next)
|
|
||||||
next.markClean()
|
|
||||||
next = dirty.poll()
|
|
||||||
}
|
|
||||||
|
|
||||||
return encodePayload(registry) {
|
|
||||||
for (slot in sorted) {
|
|
||||||
it.writeVarInt(slot.parent.id + 1)
|
|
||||||
slot.write(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
it.writeByte(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marks all networked slots dirty
|
|
||||||
*/
|
|
||||||
fun invalidate() {
|
|
||||||
remoteSlots.forEach { it.invalidate() }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
if (!isRemoved) {
|
|
||||||
lock.withLock {
|
|
||||||
if (!isRemoved) {
|
|
||||||
remoteSlots.forEach {
|
|
||||||
it.remove()
|
|
||||||
it.parent.remoteSlots.remove(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
remotes.remove(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dirty.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val END = 0
|
|
||||||
private const val CLEAR = 1
|
|
||||||
private const val ADD = 2
|
|
||||||
private const val REMOVE = 3
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,7 +12,7 @@ import java.io.DataInputStream
|
|||||||
import java.io.DataOutputStream
|
import java.io.DataOutputStream
|
||||||
import java.util.NoSuchElementException
|
import java.util.NoSuchElementException
|
||||||
|
|
||||||
interface MatteryStreamCodec<S : ByteBuf, V> : StreamCodec<S, V> {
|
interface MatteryStreamCodec<in S : ByteBuf, V> : StreamCodec<@UnsafeVariance S, V> {
|
||||||
fun copy(value: V): V {
|
fun copy(value: V): V {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
@ -25,9 +25,9 @@ interface MatteryStreamCodec<S : ByteBuf, V> : StreamCodec<S, V> {
|
|||||||
override fun decode(stream: S): V
|
override fun decode(stream: S): V
|
||||||
override fun encode(stream: S, value: V)
|
override fun encode(stream: S, value: V)
|
||||||
|
|
||||||
class Wrapper<S : ByteBuf, V>(parent: StreamCodec<S, V>) : MatteryStreamCodec<S, V>, StreamCodec<S, V> by parent
|
class Wrapper<in S : ByteBuf, V>(parent: StreamCodec<S, V>) : MatteryStreamCodec<S, V>, StreamCodec<@UnsafeVariance S, V> by parent
|
||||||
|
|
||||||
class Nullable<S : ByteBuf, V>(val parent: MatteryStreamCodec<S, V>) : MatteryStreamCodec<S, V?> {
|
class Nullable<in S : ByteBuf, V>(val parent: MatteryStreamCodec<S, V>) : MatteryStreamCodec<S, V?> {
|
||||||
override fun decode(stream: S): V? {
|
override fun decode(stream: S): V? {
|
||||||
return if (!stream.readBoolean()) null else parent.decode(stream)
|
return if (!stream.readBoolean()) null else parent.decode(stream)
|
||||||
}
|
}
|
||||||
@ -52,7 +52,7 @@ interface MatteryStreamCodec<S : ByteBuf, V> : StreamCodec<S, V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Optional<S : ByteBuf, V : Any>(val parent: MatteryStreamCodec<S, V>) : MatteryStreamCodec<S, java.util.Optional<V>> {
|
class Optional<in S : ByteBuf, V : Any>(val parent: MatteryStreamCodec<S, V>) : MatteryStreamCodec<S, java.util.Optional<V>> {
|
||||||
override fun decode(stream: S): java.util.Optional<V> {
|
override fun decode(stream: S): java.util.Optional<V> {
|
||||||
return if (!stream.readBoolean()) java.util.Optional.empty() else java.util.Optional.of(parent.decode(stream))
|
return if (!stream.readBoolean()) java.util.Optional.empty() else java.util.Optional.of(parent.decode(stream))
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ interface MatteryStreamCodec<S : ByteBuf, V> : StreamCodec<S, V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Collection<S : FriendlyByteBuf, E, C : MutableCollection<E>>(val elementCodec: MatteryStreamCodec<in S, E>, val collectionFactory: (Int) -> C) : MatteryStreamCodec<S, C> {
|
class Collection<in S : FriendlyByteBuf, E, C : MutableCollection<E>>(val elementCodec: MatteryStreamCodec<S, E>, val collectionFactory: (Int) -> C) : MatteryStreamCodec<S, C> {
|
||||||
override fun decode(stream: S): C {
|
override fun decode(stream: S): C {
|
||||||
val size = stream.readVarInt()
|
val size = stream.readVarInt()
|
||||||
|
|
||||||
@ -106,7 +106,7 @@ interface MatteryStreamCodec<S : ByteBuf, V> : StreamCodec<S, V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Enum<S : FriendlyByteBuf, V : kotlin.Enum<V>>(clazz: Class<out V>) : MatteryStreamCodec<S, V> {
|
class Enum<in S : FriendlyByteBuf, V : kotlin.Enum<V>>(clazz: Class<out V>) : MatteryStreamCodec<S, V> {
|
||||||
val clazz = searchClass(clazz)
|
val clazz = searchClass(clazz)
|
||||||
val values: List<V> = listOf(*this.clazz.enumConstants!!)
|
val values: List<V> = listOf(*this.clazz.enumConstants!!)
|
||||||
val valuesMap = values.associateBy { it.name }
|
val valuesMap = values.associateBy { it.name }
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.network.syncher
|
||||||
|
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
|
import java.io.Closeable
|
||||||
|
|
||||||
|
interface IRemoteState : Closeable {
|
||||||
|
fun write(stream: RegistryFriendlyByteBuf)
|
||||||
|
|
||||||
|
fun invalidate()
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.network.syncher
|
||||||
|
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
|
import ru.dbotthepony.kommons.util.Observer
|
||||||
|
|
||||||
|
interface ISynchable : Observer {
|
||||||
|
fun read(stream: RegistryFriendlyByteBuf)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provided [listener] is to be considered thread-safe, and can be called at any time
|
||||||
|
*
|
||||||
|
* Created [IRemoteState] is implied to have [IRemoteState.invalidate] called implicitly internally
|
||||||
|
*/
|
||||||
|
fun createRemoteState(listener: Runnable): IRemoteState
|
||||||
|
|
||||||
|
val shouldBeObserved: Boolean
|
||||||
|
get() = false
|
||||||
|
|
||||||
|
override fun observe(): Boolean {
|
||||||
|
// no op
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.network.syncher
|
||||||
|
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
|
import ru.dbotthepony.kommons.util.Listenable
|
||||||
|
import ru.dbotthepony.kommons.util.ListenableDelegate
|
||||||
|
import ru.dbotthepony.kommons.util.Observer
|
||||||
|
import ru.dbotthepony.mc.otm.network.MatteryStreamCodec
|
||||||
|
import java.util.function.Consumer
|
||||||
|
import java.util.function.Supplier
|
||||||
|
|
||||||
|
class SynchableDelegate<V>(val delegate: ListenableDelegate<V>, val codec: MatteryStreamCodec<RegistryFriendlyByteBuf, V>) : ISynchable, ListenableDelegate<V>, Observer {
|
||||||
|
constructor(delegate: Supplier<V>, codec: MatteryStreamCodec<RegistryFriendlyByteBuf, V>) : this(ListenableDelegate.CustomShadow(delegate, codec::compare, codec::copy), codec)
|
||||||
|
|
||||||
|
private val listeners = Listenable.Impl<V>()
|
||||||
|
private val l = delegate.addListener(listeners)
|
||||||
|
|
||||||
|
override fun read(stream: RegistryFriendlyByteBuf) {
|
||||||
|
delegate.accept(codec.decode(stream))
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class Remote(listener: Runnable) : IRemoteState {
|
||||||
|
private val l = listeners.addListener(listener)
|
||||||
|
|
||||||
|
override fun write(stream: RegistryFriendlyByteBuf) {
|
||||||
|
codec.encode(stream, delegate.get())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invalidate() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
l.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createRemoteState(listener: Runnable): IRemoteState {
|
||||||
|
return Remote(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun accept(t: V) {
|
||||||
|
delegate.accept(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addListener(listener: Consumer<V>): Listenable.L {
|
||||||
|
return listeners.addListener(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun get(): V {
|
||||||
|
return delegate.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
override val shouldBeObserved: Boolean
|
||||||
|
get() = delegate is ListenableDelegate.AbstractShadow
|
||||||
|
|
||||||
|
override fun observe(): Boolean {
|
||||||
|
if (delegate is ListenableDelegate.AbstractShadow) {
|
||||||
|
return delegate.observe()
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,122 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.network.syncher
|
||||||
|
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
|
import ru.dbotthepony.kommons.collect.ListenableMap
|
||||||
|
import ru.dbotthepony.kommons.util.KOptional
|
||||||
|
import ru.dbotthepony.mc.otm.network.MatteryStreamCodec
|
||||||
|
import java.util.ArrayList
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList
|
||||||
|
|
||||||
|
class SynchableMap<K, V>(
|
||||||
|
val delegate: ListenableMap<K, V>,
|
||||||
|
val keyCodec: MatteryStreamCodec<RegistryFriendlyByteBuf, K>,
|
||||||
|
val valueCodec: MatteryStreamCodec<RegistryFriendlyByteBuf, V>
|
||||||
|
) : ISynchable {
|
||||||
|
override fun read(stream: RegistryFriendlyByteBuf) {
|
||||||
|
var action = stream.readByte().toInt()
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
when (action) {
|
||||||
|
ADD -> delegate.put(keyCodec.decode(stream), valueCodec.decode(stream))
|
||||||
|
REMOVE -> delegate.remove(keyCodec.decode(stream))
|
||||||
|
CLEAR -> delegate.clear()
|
||||||
|
else -> break
|
||||||
|
}
|
||||||
|
|
||||||
|
action = stream.readByte().toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val remoteSlots = CopyOnWriteArrayList<RemoteState>()
|
||||||
|
private data class MapAction<K, V>(val key: KOptional<K>, val value: KOptional<V>, val action: Int)
|
||||||
|
|
||||||
|
private inner class RemoteState(val listener: Runnable) : IRemoteState, ListenableMap.MapListener<K, V> {
|
||||||
|
private val changelist = ArrayList<MapAction<K, V>>()
|
||||||
|
private val changelistLock = Any()
|
||||||
|
private val l = delegate.addListener(this)
|
||||||
|
|
||||||
|
init {
|
||||||
|
remoteSlots.add(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun write(stream: RegistryFriendlyByteBuf) {
|
||||||
|
synchronized(changelistLock) {
|
||||||
|
for (change in changelist) {
|
||||||
|
stream.writeByte(change.action)
|
||||||
|
change.key.ifPresent { keyCodec.encode(stream, it) }
|
||||||
|
change.value.ifPresent { valueCodec.encode(stream, it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
changelist.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.writeByte(END)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invalidate() {
|
||||||
|
synchronized(changelistLock) {
|
||||||
|
changelist.clear()
|
||||||
|
changelist.add(MapAction(KOptional(), KOptional(), CLEAR))
|
||||||
|
|
||||||
|
for ((k, v) in delegate) {
|
||||||
|
changelist.add(MapAction(KOptional(keyCodec.copy(k)), KOptional(valueCodec.copy(v)), ADD))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
l.remove()
|
||||||
|
remoteSlots.remove(this)
|
||||||
|
|
||||||
|
synchronized(changelistLock) {
|
||||||
|
changelist.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClear() {
|
||||||
|
synchronized(changelistLock) {
|
||||||
|
changelist.clear()
|
||||||
|
changelist.add(MapAction(KOptional(), KOptional(), CLEAR))
|
||||||
|
}
|
||||||
|
|
||||||
|
listener.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onValueAdded(key: K, value: V) {
|
||||||
|
synchronized(changelistLock) {
|
||||||
|
changelist.removeIf { it.key.valueEquals(key) }
|
||||||
|
changelist.add(MapAction(KOptional(keyCodec.copy(key)), KOptional(valueCodec.copy(value)), ADD))
|
||||||
|
}
|
||||||
|
|
||||||
|
listener.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onValueRemoved(key: K, value: V) {
|
||||||
|
synchronized(changelistLock) {
|
||||||
|
changelist.removeIf { it.key.valueEquals(key) }
|
||||||
|
changelist.add(MapAction(KOptional(keyCodec.copy(key)), KOptional(), REMOVE))
|
||||||
|
}
|
||||||
|
|
||||||
|
listener.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createRemoteState(listener: Runnable): IRemoteState {
|
||||||
|
return RemoteState(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun observe(): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val END = 0
|
||||||
|
private const val CLEAR = 1
|
||||||
|
private const val ADD = 2
|
||||||
|
private const val REMOVE = 3
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.network.syncher
|
||||||
|
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
|
import ru.dbotthepony.kommons.util.Delegate
|
||||||
|
import ru.dbotthepony.kommons.util.Listenable
|
||||||
|
import ru.dbotthepony.kommons.util.ListenableDelegate
|
||||||
|
import ru.dbotthepony.kommons.util.ValueObserver
|
||||||
|
import ru.dbotthepony.mc.otm.network.MatteryStreamCodec
|
||||||
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
|
import java.util.function.Consumer
|
||||||
|
import kotlin.concurrent.withLock
|
||||||
|
|
||||||
|
class SynchableObservedDelegate<V>(val delegate: Delegate<V>, val codec: MatteryStreamCodec<RegistryFriendlyByteBuf, V>) : ISynchable, ListenableDelegate<V>, ValueObserver<V> {
|
||||||
|
private object Mark
|
||||||
|
|
||||||
|
private val listeners = Listenable.Impl<V>()
|
||||||
|
private var observed: Any? = Mark
|
||||||
|
private val observeLock = ReentrantLock()
|
||||||
|
|
||||||
|
override fun read(stream: RegistryFriendlyByteBuf) {
|
||||||
|
val read = codec.decode(stream)
|
||||||
|
observed = codec.copy(read)
|
||||||
|
delegate.accept(read)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun observe(): Boolean {
|
||||||
|
val get = delegate.get()
|
||||||
|
|
||||||
|
if (observed === Mark || !codec.compare(get, observed as V)) {
|
||||||
|
observeLock.withLock {
|
||||||
|
if (observed === Mark || !codec.compare(get, observed as V)) {
|
||||||
|
observed = codec.copy(get)
|
||||||
|
listeners.accept(get)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAndObserve(): Pair<V, Boolean> {
|
||||||
|
val get = delegate.get()
|
||||||
|
|
||||||
|
if (observed === Mark || !codec.compare(get, observed as V)) {
|
||||||
|
observeLock.withLock {
|
||||||
|
if (observed === Mark || !codec.compare(get, observed as V)) {
|
||||||
|
observed = codec.copy(get)
|
||||||
|
listeners.accept(get)
|
||||||
|
}
|
||||||
|
|
||||||
|
return get to true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return get to false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun get(): V {
|
||||||
|
return delegate.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun accept(t: V) {
|
||||||
|
delegate.accept(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val shouldBeObserved: Boolean
|
||||||
|
get() = true
|
||||||
|
|
||||||
|
override fun addListener(listener: Consumer<V>): Listenable.L {
|
||||||
|
return listeners.addListener(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class RemoteState(listener: Runnable) : IRemoteState {
|
||||||
|
private val l = listeners.addListener(listener)
|
||||||
|
|
||||||
|
override fun write(stream: RegistryFriendlyByteBuf) {
|
||||||
|
codec.encode(stream, delegate.get())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invalidate() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
l.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createRemoteState(listener: Runnable): IRemoteState {
|
||||||
|
return RemoteState(listener)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,119 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.network.syncher
|
||||||
|
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
|
import ru.dbotthepony.kommons.collect.ListenableSet
|
||||||
|
import ru.dbotthepony.kommons.util.KOptional
|
||||||
|
import ru.dbotthepony.kommons.util.Listenable
|
||||||
|
import ru.dbotthepony.mc.otm.network.MatteryStreamCodec
|
||||||
|
import java.util.ArrayList
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList
|
||||||
|
|
||||||
|
class SynchableSet<V>(val delegate: ListenableSet<V>, val codec: MatteryStreamCodec<RegistryFriendlyByteBuf, V>) : ISynchable {
|
||||||
|
private data class SetAction<V>(val value: KOptional<V>, val action: Int)
|
||||||
|
private val remoteSlots = CopyOnWriteArrayList<RemoteState>()
|
||||||
|
|
||||||
|
private inner class RemoteState(val listener: Runnable) : IRemoteState, ListenableSet.SetListener<V> {
|
||||||
|
private val changelist = ArrayList<SetAction<V>>()
|
||||||
|
private val changelistLock = Any()
|
||||||
|
private val l = delegate.addListener(this)
|
||||||
|
|
||||||
|
init {
|
||||||
|
remoteSlots.add(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun write(stream: RegistryFriendlyByteBuf) {
|
||||||
|
synchronized(changelistLock) {
|
||||||
|
for (change in changelist) {
|
||||||
|
stream.writeByte(change.action)
|
||||||
|
change.value.ifPresent { codec.encode(stream, it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
changelist.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.writeByte(END)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invalidate() {
|
||||||
|
synchronized(changelistLock) {
|
||||||
|
changelist.clear()
|
||||||
|
changelist.add(SetAction(KOptional(), CLEAR))
|
||||||
|
|
||||||
|
for (v in delegate) {
|
||||||
|
changelist.add(SetAction(KOptional(codec.copy(v)), ADD))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
remoteSlots.remove(this)
|
||||||
|
l.remove()
|
||||||
|
|
||||||
|
synchronized(changelistLock) {
|
||||||
|
changelist.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClear() {
|
||||||
|
changelist.clear()
|
||||||
|
|
||||||
|
synchronized(changelistLock) {
|
||||||
|
changelist.add(SetAction(KOptional(), CLEAR))
|
||||||
|
}
|
||||||
|
|
||||||
|
listener.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onValueAdded(element: V) {
|
||||||
|
synchronized(changelistLock) {
|
||||||
|
changelist.removeIf { it.value.valueEquals(element) }
|
||||||
|
changelist.add(SetAction(KOptional(codec.copy(element)), ADD))
|
||||||
|
}
|
||||||
|
|
||||||
|
listener.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onValueRemoved(element: V) {
|
||||||
|
synchronized(changelistLock) {
|
||||||
|
changelist.removeIf { it.value.valueEquals(element) }
|
||||||
|
changelist.add(SetAction(KOptional(codec.copy(element)), REMOVE))
|
||||||
|
}
|
||||||
|
|
||||||
|
listener.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createRemoteState(listener: Runnable): IRemoteState {
|
||||||
|
return RemoteState(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun observe(): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun read(stream: RegistryFriendlyByteBuf) {
|
||||||
|
var action = stream.readByte().toInt()
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
when (action) {
|
||||||
|
ADD -> delegate.add(codec.decode(stream))
|
||||||
|
REMOVE -> delegate.remove(codec.decode(stream))
|
||||||
|
CLEAR -> delegate.clear()
|
||||||
|
else -> break
|
||||||
|
}
|
||||||
|
|
||||||
|
action = stream.readByte().toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val END = 0
|
||||||
|
private const val CLEAR = 1
|
||||||
|
private const val ADD = 2
|
||||||
|
private const val REMOVE = 3
|
||||||
|
}
|
||||||
|
}
|
443
src/main/kotlin/ru/dbotthepony/mc/otm/network/syncher/Syncher.kt
Normal file
443
src/main/kotlin/ru/dbotthepony/mc/otm/network/syncher/Syncher.kt
Normal file
@ -0,0 +1,443 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.network.syncher
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.bytes.ByteArrayList
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
|
||||||
|
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet
|
||||||
|
import net.minecraft.core.RegistryAccess
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
|
import net.minecraft.world.item.ItemStack
|
||||||
|
import org.apache.logging.log4j.LogManager
|
||||||
|
import ru.dbotthepony.kommons.collect.ListenableMap
|
||||||
|
import ru.dbotthepony.kommons.collect.ListenableSet
|
||||||
|
import ru.dbotthepony.kommons.util.Delegate
|
||||||
|
import ru.dbotthepony.kommons.util.DelegateGetter
|
||||||
|
import ru.dbotthepony.kommons.util.DelegateSetter
|
||||||
|
import ru.dbotthepony.kommons.util.Listenable
|
||||||
|
import ru.dbotthepony.kommons.util.ListenableDelegate
|
||||||
|
import ru.dbotthepony.kommons.util.Observer
|
||||||
|
import ru.dbotthepony.mc.otm.OTM_CLEANER
|
||||||
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
|
import ru.dbotthepony.mc.otm.network.MatteryStreamCodec
|
||||||
|
import ru.dbotthepony.mc.otm.network.StreamCodecs
|
||||||
|
import ru.dbotthepony.mc.otm.network.decodePayload
|
||||||
|
import ru.dbotthepony.mc.otm.network.encodePayload
|
||||||
|
import ru.dbotthepony.mc.otm.network.wrap
|
||||||
|
import java.io.Closeable
|
||||||
|
import java.lang.ref.Cleaner.Cleanable
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
import java.util.*
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
|
import java.util.function.BooleanSupplier
|
||||||
|
import java.util.function.DoubleSupplier
|
||||||
|
import java.util.function.IntSupplier
|
||||||
|
import java.util.function.LongSupplier
|
||||||
|
import java.util.function.Supplier
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
import kotlin.concurrent.withLock
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Universal, one-to-many delegate/value synchronizer.
|
||||||
|
*
|
||||||
|
* Values and delegates can be attached using [add], or by constructing subclassed directly.
|
||||||
|
* Delta changes are tracked by [Syncher.Remote] instances.
|
||||||
|
*
|
||||||
|
* In general, this class is not meant to be _structurally_ concurrently mutated by different threads,
|
||||||
|
* to avoid structure corruption a lock is employed.
|
||||||
|
*
|
||||||
|
* Attached delegates can be safely mutated by multiple threads concurrently
|
||||||
|
* (attached [ListenableDelegate] can call [Listenable.addListener] callback from different threads).
|
||||||
|
*/
|
||||||
|
@Suppress("UNUSED")
|
||||||
|
class Syncher : Observer, ISynchable {
|
||||||
|
private val lock = ReentrantLock()
|
||||||
|
private val slots = ArrayList<Slot?>()
|
||||||
|
private val gaps = IntAVLTreeSet()
|
||||||
|
private val observers = ArrayList<Slot>()
|
||||||
|
private val remotes = ArrayList<Remote>()
|
||||||
|
private var isRemote = false
|
||||||
|
|
||||||
|
override fun observe(): Boolean {
|
||||||
|
var any = false
|
||||||
|
observers.forEach { any = it.observe() || any }
|
||||||
|
return any
|
||||||
|
}
|
||||||
|
|
||||||
|
override val shouldBeObserved: Boolean
|
||||||
|
get() = true
|
||||||
|
|
||||||
|
override fun createRemoteState(listener: Runnable): IRemoteState {
|
||||||
|
return Remote(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun read(stream: RegistryFriendlyByteBuf) {
|
||||||
|
isRemote = true
|
||||||
|
|
||||||
|
var readID = stream.readVarInt()
|
||||||
|
|
||||||
|
while (readID > 0) {
|
||||||
|
val slot = lock.withLock { slots.getOrNull(readID - 1) ?: throw IndexOutOfBoundsException("Unknown networked slot ${readID - 1}!") }
|
||||||
|
slot.synchable.read(stream)
|
||||||
|
readID = stream.readVarInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun read(registry: RegistryAccess, bytes: ByteArrayList) {
|
||||||
|
return decodePayload(registry, bytes) {
|
||||||
|
read(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal inner class Slot(val synchable: ISynchable) : Closeable, Observer {
|
||||||
|
val id: Int
|
||||||
|
|
||||||
|
private val isRemoved = AtomicBoolean()
|
||||||
|
val remoteSlots = CopyOnWriteArrayList<Remote.RemoteSlot>()
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
if (isRemoved.compareAndSet(false, true)) {
|
||||||
|
lock.withLock {
|
||||||
|
slots[id] = null
|
||||||
|
gaps.add(id)
|
||||||
|
|
||||||
|
observers.remove(this)
|
||||||
|
remoteSlots.forEach { it.remove() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun observe(): Boolean {
|
||||||
|
return synchable.observe()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun markDirty() {
|
||||||
|
if (!isRemote && !isRemoved.get()) {
|
||||||
|
remoteSlots.forEach {
|
||||||
|
it.markDirty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
lock.withLock {
|
||||||
|
if (synchable.shouldBeObserved) {
|
||||||
|
observers.add(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gaps.isNotEmpty()) {
|
||||||
|
val gap = gaps.firstInt()
|
||||||
|
gaps.remove(gap)
|
||||||
|
id = gap
|
||||||
|
check(slots[id] == null) { "Expected slot $id to be empty" }
|
||||||
|
slots[id] = this
|
||||||
|
} else {
|
||||||
|
id = slots.size
|
||||||
|
slots.add(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
remotes.forEach {
|
||||||
|
it.addSlot(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun add0(synchable: ISynchable): Closeable {
|
||||||
|
return Slot(synchable)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : ISynchable> add(synchable: T): T {
|
||||||
|
Slot(synchable)
|
||||||
|
return synchable
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <V> add(value: V, codec: MatteryStreamCodec<RegistryFriendlyByteBuf, V>, setter: DelegateSetter<V> = DelegateSetter.passthrough(), getter: DelegateGetter<V> = DelegateGetter.passthrough()): SynchableDelegate<V> {
|
||||||
|
return add(SynchableDelegate(ListenableDelegate.maskSmart(value, getter, setter), codec))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <V> add(delegate: ListenableDelegate<V>, codec: MatteryStreamCodec<RegistryFriendlyByteBuf, V>): SynchableDelegate<V> {
|
||||||
|
return add(SynchableDelegate(delegate, codec))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <V> add(delegate: Delegate<V>, codec: MatteryStreamCodec<RegistryFriendlyByteBuf, V>): SynchableObservedDelegate<V> {
|
||||||
|
return add(SynchableObservedDelegate(delegate, codec))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <V> add(delegate: Supplier<V>, codec: MatteryStreamCodec<RegistryFriendlyByteBuf, V>): SynchableDelegate<V> {
|
||||||
|
return computed(delegate, codec)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <E> add(delegate: ListenableSet<E>, codec: MatteryStreamCodec<RegistryFriendlyByteBuf, E>): SynchableSet<E> {
|
||||||
|
return add(SynchableSet(delegate, codec))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <K, V> add(delegate: ListenableMap<K, V>, keyCodec: MatteryStreamCodec<RegistryFriendlyByteBuf, K>, valueCodec: MatteryStreamCodec<RegistryFriendlyByteBuf, V>): SynchableMap<K, V> {
|
||||||
|
return add(SynchableMap(delegate, keyCodec, valueCodec))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun computedByte(delegate: Supplier<Byte>) = add(SynchableDelegate(ListenableDelegate.Shadow(delegate), StreamCodecs.BYTE))
|
||||||
|
fun computedShort(delegate: Supplier<Short>) = add(SynchableDelegate(ListenableDelegate.Shadow(delegate), StreamCodecs.SHORT))
|
||||||
|
fun computedInt(delegate: Supplier<Int>) = add(SynchableDelegate(ListenableDelegate.Shadow(delegate), StreamCodecs.INT))
|
||||||
|
fun computedLong(delegate: Supplier<Long>) = add(SynchableDelegate(ListenableDelegate.Shadow(delegate), StreamCodecs.VAR_LONG))
|
||||||
|
fun computedFloat(delegate: Supplier<Float>) = add(SynchableDelegate(ListenableDelegate.Shadow(delegate), StreamCodecs.FLOAT))
|
||||||
|
fun computedDouble(delegate: Supplier<Double>) = add(SynchableDelegate(ListenableDelegate.Shadow(delegate), StreamCodecs.DOUBLE))
|
||||||
|
fun computedBoolean(delegate: Supplier<Boolean>) = add(SynchableDelegate(ListenableDelegate.Shadow(delegate), StreamCodecs.BOOLEAN))
|
||||||
|
fun computedString(delegate: Supplier<String>) = add(SynchableDelegate(ListenableDelegate.Shadow(delegate), StreamCodecs.STRING))
|
||||||
|
fun computedUUID(delegate: Supplier<UUID>) = add(SynchableDelegate(ListenableDelegate.Shadow(delegate), StreamCodecs.UUID))
|
||||||
|
fun <V> computed(delegate: Supplier<V>, codec: MatteryStreamCodec<RegistryFriendlyByteBuf, V>) = add(SynchableDelegate(ListenableDelegate.CustomShadow(delegate, codec::compare, codec::copy), codec))
|
||||||
|
|
||||||
|
fun computedInt(delegate: IntSupplier) = add(SynchableDelegate(ListenableDelegate.Shadow(delegate::getAsInt), StreamCodecs.VAR_INT))
|
||||||
|
fun computedLong(delegate: LongSupplier) = add(SynchableDelegate(ListenableDelegate.Shadow(delegate::getAsLong), StreamCodecs.VAR_LONG))
|
||||||
|
fun computedDouble(delegate: DoubleSupplier) = add(SynchableDelegate(ListenableDelegate.Shadow(delegate::getAsDouble), StreamCodecs.DOUBLE))
|
||||||
|
fun computedBoolean(delegate: BooleanSupplier) = add(SynchableDelegate(ListenableDelegate.Shadow(delegate::getAsBoolean), StreamCodecs.BOOLEAN))
|
||||||
|
|
||||||
|
@JvmName("vbyte")
|
||||||
|
@JvmOverloads
|
||||||
|
fun byte(value: Byte = 0, setter: DelegateSetter<Byte> = DelegateSetter.passthrough(), getter: DelegateGetter<Byte> = DelegateGetter.passthrough()): SynchableDelegate<Byte> {
|
||||||
|
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.BYTE)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmName("vshort")
|
||||||
|
@JvmOverloads
|
||||||
|
fun short(value: Short = 0, setter: DelegateSetter<Short> = DelegateSetter.passthrough(), getter: DelegateGetter<Short> = DelegateGetter.passthrough()): SynchableDelegate<Short> {
|
||||||
|
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.SHORT)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmName("vint")
|
||||||
|
@JvmOverloads
|
||||||
|
fun int(value: Int = 0, setter: DelegateSetter<Int> = DelegateSetter.passthrough(), getter: DelegateGetter<Int> = DelegateGetter.passthrough()): SynchableDelegate<Int> {
|
||||||
|
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.VAR_INT)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmName("vlong")
|
||||||
|
@JvmOverloads
|
||||||
|
fun long(value: Long = 0L, setter: DelegateSetter<Long> = DelegateSetter.passthrough(), getter: DelegateGetter<Long> = DelegateGetter.passthrough()): SynchableDelegate<Long> {
|
||||||
|
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.VAR_LONG)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmName("vfloat")
|
||||||
|
@JvmOverloads
|
||||||
|
fun float(value: Float = 0f, setter: DelegateSetter<Float> = DelegateSetter.passthrough(), getter: DelegateGetter<Float> = DelegateGetter.passthrough()): SynchableDelegate<Float> {
|
||||||
|
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.FLOAT)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmName("vdouble")
|
||||||
|
@JvmOverloads
|
||||||
|
fun double(value: Double = 0.0, setter: DelegateSetter<Double> = DelegateSetter.passthrough(), getter: DelegateGetter<Double> = DelegateGetter.passthrough()): SynchableDelegate<Double> {
|
||||||
|
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.DOUBLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmName("vboolean")
|
||||||
|
@JvmOverloads
|
||||||
|
fun boolean(value: Boolean = false, setter: DelegateSetter<Boolean> = DelegateSetter.passthrough(), getter: DelegateGetter<Boolean> = DelegateGetter.passthrough()): SynchableDelegate<Boolean> {
|
||||||
|
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.BOOLEAN)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmOverloads
|
||||||
|
fun string(value: String, setter: DelegateSetter<String> = DelegateSetter.passthrough(), getter: DelegateGetter<String> = DelegateGetter.passthrough()): SynchableDelegate<String> {
|
||||||
|
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.STRING)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmOverloads
|
||||||
|
fun uuid(value: UUID, setter: DelegateSetter<UUID> = DelegateSetter.passthrough(), getter: DelegateGetter<UUID> = DelegateGetter.passthrough()): SynchableDelegate<UUID> {
|
||||||
|
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.UUID)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmOverloads
|
||||||
|
fun <E> set(codec: MatteryStreamCodec<RegistryFriendlyByteBuf, E>, backing: MutableSet<E> = ObjectOpenHashSet()): SynchableSet<E> {
|
||||||
|
return add(ListenableSet(backing), codec)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmOverloads
|
||||||
|
fun <K, V> map(keyCodec: MatteryStreamCodec<RegistryFriendlyByteBuf, K>, valueCodec: MatteryStreamCodec<RegistryFriendlyByteBuf, V>, backing: MutableMap<K, V> = Object2ObjectOpenHashMap()): SynchableMap<K, V> {
|
||||||
|
return add(ListenableMap(backing), keyCodec, valueCodec)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmOverloads
|
||||||
|
fun <E : Enum<E>> enum(value: E, setter: DelegateSetter<E> = DelegateSetter.passthrough(), getter: DelegateGetter<E> = DelegateGetter.passthrough()): SynchableDelegate<E> {
|
||||||
|
return add(ListenableDelegate.maskSmart(value, getter, setter), MatteryStreamCodec.Enum(value::class.java))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decimal(value: Decimal = Decimal.ZERO, setter: DelegateSetter<Decimal> = DelegateSetter.passthrough(), getter: DelegateGetter<Decimal> = DelegateGetter.passthrough()): SynchableDelegate<Decimal> {
|
||||||
|
return add(ListenableDelegate.maskSmart(value, getter, setter), StreamCodecs.DECIMAL)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun computedDecimal(delegate: Supplier<Decimal>): SynchableDelegate<Decimal> {
|
||||||
|
return add(delegate, StreamCodecs.DECIMAL)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun item(value: ItemStack = ItemStack.EMPTY, setter: DelegateSetter<ItemStack> = DelegateSetter.passthrough(), getter: DelegateGetter<ItemStack> = DelegateGetter.passthrough()): SynchableDelegate<ItemStack> {
|
||||||
|
return add(ListenableDelegate.maskSmart(value, getter, setter), ItemStack.OPTIONAL_STREAM_CODEC.wrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun computedItem(delegate: Supplier<ItemStack>): SynchableDelegate<ItemStack> {
|
||||||
|
return computed(delegate, ItemStack.OPTIONAL_STREAM_CODEC.wrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun observedItem(value: ItemStack = ItemStack.EMPTY, setter: DelegateSetter<ItemStack> = DelegateSetter.passthrough(), getter: DelegateGetter<ItemStack> = DelegateGetter.passthrough()): SynchableObservedDelegate<ItemStack> {
|
||||||
|
return add(Delegate.maskSmart(value, getter, setter), ItemStack.OPTIONAL_STREAM_CODEC.wrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remotes must be [close]d when no longer in use, otherwise they will
|
||||||
|
* leak memory and decrease performance of syncher.
|
||||||
|
*
|
||||||
|
* To get changes to be networked to remote client, [write] should be called.
|
||||||
|
*/
|
||||||
|
inner class Remote(private val listener: Runnable) : Closeable, IRemoteState {
|
||||||
|
constructor() : this(Runnable { })
|
||||||
|
|
||||||
|
@Volatile
|
||||||
|
private var isRemoved = false
|
||||||
|
internal val dirty = ConcurrentLinkedQueue<RemoteSlot>()
|
||||||
|
private val remoteSlots = CopyOnWriteArrayList<RemoteSlot>()
|
||||||
|
|
||||||
|
internal inner class RemoteSlot(val slot: Slot) {
|
||||||
|
private val isDirty = AtomicBoolean()
|
||||||
|
|
||||||
|
init {
|
||||||
|
remoteSlots.add(this)
|
||||||
|
dirty.add(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun markDirty() {
|
||||||
|
if (!isRemoved && isDirty.compareAndSet(false, true)) {
|
||||||
|
dirty.add(this)
|
||||||
|
listener.run()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val state: IRemoteState
|
||||||
|
private val cleanable: Cleanable
|
||||||
|
|
||||||
|
init {
|
||||||
|
val weakSelf = WeakReference(this)
|
||||||
|
val state = slot.synchable.createRemoteState(Runnable {
|
||||||
|
weakSelf.get()?.markDirty()
|
||||||
|
})
|
||||||
|
|
||||||
|
// !!!
|
||||||
|
// hOLY SHIT
|
||||||
|
val weakState = WeakReference(state)
|
||||||
|
cleanable = OTM_CLEANER.register(this) { weakState.get()?.close() }
|
||||||
|
this.state = state
|
||||||
|
}
|
||||||
|
|
||||||
|
fun markClean() {
|
||||||
|
isDirty.set(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun invalidate() {
|
||||||
|
state.invalidate()
|
||||||
|
markDirty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun write(stream: RegistryFriendlyByteBuf) {
|
||||||
|
state.write(stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun remove() {
|
||||||
|
isDirty.set(true)
|
||||||
|
remoteSlots.remove(this)
|
||||||
|
dirty.remove(this)
|
||||||
|
cleanable.clean()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return slot.hashCode()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return this === other || other is RemoteSlot && other.slot == slot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
lock.withLock {
|
||||||
|
slots.forEach {
|
||||||
|
if (it != null) {
|
||||||
|
addSlot(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remotes.add(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun addSlot(slot: Slot) {
|
||||||
|
slot.remoteSlots.add(RemoteSlot(slot))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns null if this remote is clean.
|
||||||
|
*
|
||||||
|
* [Syncher.observe] is not called automatically for performance
|
||||||
|
* reasons, you must call it manually.
|
||||||
|
*/
|
||||||
|
fun write(registry: RegistryAccess): ByteArrayList? {
|
||||||
|
if (dirty.isNotEmpty()) {
|
||||||
|
return encodePayload(registry) {
|
||||||
|
write(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks all networked slots dirty
|
||||||
|
*/
|
||||||
|
override fun invalidate() {
|
||||||
|
remoteSlots.forEach { it.invalidate() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun write(stream: RegistryFriendlyByteBuf) {
|
||||||
|
val seen = ReferenceOpenHashSet<RemoteSlot>()
|
||||||
|
val sorted = ArrayList<RemoteSlot>()
|
||||||
|
var next = dirty.poll()
|
||||||
|
|
||||||
|
while (next != null) {
|
||||||
|
val status = seen.add(next)
|
||||||
|
next.markClean()
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
sorted.add(next)
|
||||||
|
}
|
||||||
|
|
||||||
|
next = dirty.poll()
|
||||||
|
}
|
||||||
|
|
||||||
|
for (slot in sorted) {
|
||||||
|
stream.writeVarInt(slot.slot.id + 1)
|
||||||
|
slot.write(stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.writeByte(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
if (!isRemoved) {
|
||||||
|
lock.withLock {
|
||||||
|
if (!isRemoved) {
|
||||||
|
remoteSlots.forEach {
|
||||||
|
it.remove()
|
||||||
|
it.slot.remoteSlots.remove(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
remotes.remove(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dirty.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val LOGGER = LogManager.getLogger()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user