Observing tick list, computed fields in Field Synchronizer
This commit is contained in:
parent
f586f12b90
commit
93711aacc1
@ -144,6 +144,7 @@ public final class OverdriveThatMatters {
|
|||||||
EVENT_BUS.addListener(EventPriority.NORMAL, SynchronizedBlockEntity.Companion::onWatch);
|
EVENT_BUS.addListener(EventPriority.NORMAL, SynchronizedBlockEntity.Companion::onWatch);
|
||||||
EVENT_BUS.addListener(EventPriority.NORMAL, SynchronizedBlockEntity.Companion::onForget);
|
EVENT_BUS.addListener(EventPriority.NORMAL, SynchronizedBlockEntity.Companion::onForget);
|
||||||
EVENT_BUS.addListener(EventPriority.NORMAL, SynchronizedBlockEntity.Companion::playerDisconnected);
|
EVENT_BUS.addListener(EventPriority.NORMAL, SynchronizedBlockEntity.Companion::playerDisconnected);
|
||||||
|
EVENT_BUS.addListener(EventPriority.LOWEST, SynchronizedBlockEntity.Companion::postLevelTick);
|
||||||
|
|
||||||
EVENT_BUS.addListener(EventPriority.NORMAL, EnderTeleporterFeature.Companion::onEntityDeath);
|
EVENT_BUS.addListener(EventPriority.NORMAL, EnderTeleporterFeature.Companion::onEntityDeath);
|
||||||
|
|
||||||
|
@ -2,8 +2,6 @@ package ru.dbotthepony.mc.otm.block.entity
|
|||||||
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectAVLTreeMap
|
import it.unimi.dsi.fastutil.longs.Long2ObjectAVLTreeMap
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectFunction
|
import it.unimi.dsi.fastutil.longs.Long2ObjectFunction
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap
|
|
||||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
|
|
||||||
import net.minecraft.core.BlockPos
|
import net.minecraft.core.BlockPos
|
||||||
import net.minecraft.server.level.ServerLevel
|
import net.minecraft.server.level.ServerLevel
|
||||||
import net.minecraft.server.level.ServerPlayer
|
import net.minecraft.server.level.ServerPlayer
|
||||||
@ -13,93 +11,17 @@ import net.minecraft.world.level.block.entity.BlockEntity
|
|||||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||||
import net.minecraft.world.level.block.state.BlockState
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
import net.minecraftforge.event.TickEvent
|
import net.minecraftforge.event.TickEvent
|
||||||
|
import net.minecraftforge.event.TickEvent.LevelTickEvent
|
||||||
import net.minecraftforge.event.entity.player.PlayerEvent
|
import net.minecraftforge.event.entity.player.PlayerEvent
|
||||||
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerChangedDimensionEvent
|
|
||||||
import net.minecraftforge.event.level.ChunkWatchEvent
|
import net.minecraftforge.event.level.ChunkWatchEvent
|
||||||
import net.minecraftforge.event.server.ServerStartingEvent
|
|
||||||
import net.minecraftforge.event.server.ServerStoppingEvent
|
import net.minecraftforge.event.server.ServerStoppingEvent
|
||||||
import org.apache.logging.log4j.LogManager
|
|
||||||
import ru.dbotthepony.mc.otm.NULLABLE_MINECRAFT_SERVER
|
|
||||||
import ru.dbotthepony.mc.otm.SERVER_IS_LIVE
|
|
||||||
import ru.dbotthepony.mc.otm.core.component1
|
|
||||||
import ru.dbotthepony.mc.otm.core.component2
|
|
||||||
import ru.dbotthepony.mc.otm.core.component3
|
|
||||||
import ru.dbotthepony.mc.otm.core.position
|
|
||||||
import ru.dbotthepony.mc.otm.network.BlockEntitySyncPacket
|
import ru.dbotthepony.mc.otm.network.BlockEntitySyncPacket
|
||||||
import ru.dbotthepony.mc.otm.network.FieldSynchronizer
|
import ru.dbotthepony.mc.otm.network.FieldSynchronizer
|
||||||
import ru.dbotthepony.mc.otm.network.WorldNetworkChannel
|
import ru.dbotthepony.mc.otm.network.WorldNetworkChannel
|
||||||
import ru.dbotthepony.mc.otm.onceServer
|
import ru.dbotthepony.mc.otm.onceServer
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.util.LinkedList
|
|
||||||
import java.util.WeakHashMap
|
import java.util.WeakHashMap
|
||||||
|
|
||||||
private data class Subscribers(
|
|
||||||
val blockEntities: ArrayList<WeakReference<SynchronizedBlockEntity>> = ArrayList(0),
|
|
||||||
val players: ArrayList<ServerPlayer> = ArrayList(1),
|
|
||||||
val level: WeakReference<Level>,
|
|
||||||
val chunkPos: Long,
|
|
||||||
var changeset: Int = 0
|
|
||||||
) {
|
|
||||||
fun cleanUpBlocks() {
|
|
||||||
val listIterator = blockEntities.listIterator()
|
|
||||||
|
|
||||||
for (block in listIterator) {
|
|
||||||
if (block.get() == null) {
|
|
||||||
listIterator.remove()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun subscribe(blockEntity: SynchronizedBlockEntity) {
|
|
||||||
val iterator = blockEntities.listIterator()
|
|
||||||
|
|
||||||
for (value in iterator) {
|
|
||||||
val ref = value.get()
|
|
||||||
|
|
||||||
if (ref === blockEntity) {
|
|
||||||
return
|
|
||||||
} else if (ref == null) {
|
|
||||||
iterator.remove()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
blockEntities.add(WeakReference(blockEntity))
|
|
||||||
changeset++
|
|
||||||
|
|
||||||
onceServer {
|
|
||||||
blockEntity.synchronizeToPlayers()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun unsubscribe(blockEntity: SynchronizedBlockEntity): Boolean {
|
|
||||||
val listIterator = blockEntities.listIterator()
|
|
||||||
|
|
||||||
for (value in listIterator) {
|
|
||||||
if (value.get() === blockEntity) {
|
|
||||||
listIterator.remove()
|
|
||||||
changeset++
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (players.isNotEmpty()) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanUpBlocks()
|
|
||||||
return blockEntities.isEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
val isEmpty: Boolean get() {
|
|
||||||
if (!players.isEmpty()) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanUpBlocks()
|
|
||||||
return blockEntities.isEmpty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class SynchronizedBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: BlockPos, p_155230_: BlockState) : BlockEntity(p_155228_, p_155229_, p_155230_) {
|
abstract class SynchronizedBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: BlockPos, p_155230_: BlockState) : BlockEntity(p_155228_, p_155229_, p_155230_) {
|
||||||
val synchronizer = FieldSynchronizer {
|
val synchronizer = FieldSynchronizer {
|
||||||
if (!isRemoved && level?.isClientSide == false) {
|
if (!isRemoved && level?.isClientSide == false) {
|
||||||
@ -136,7 +58,7 @@ abstract class SynchronizedBlockEntity(p_155228_: BlockEntityType<*>, p_155229_:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var _subCache: Subscribers? = null
|
private var _subCache: ChunkSubscribers? = null
|
||||||
|
|
||||||
private fun unsubscribe() {
|
private fun unsubscribe() {
|
||||||
val subCache = _subCache ?: return
|
val subCache = _subCache ?: return
|
||||||
@ -152,11 +74,15 @@ abstract class SynchronizedBlockEntity(p_155228_: BlockEntityType<*>, p_155229_:
|
|||||||
lastSubscriptionChangeset = -1
|
lastSubscriptionChangeset = -1
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun subscribe(): Subscribers {
|
private fun subscribe(): ChunkSubscribers {
|
||||||
val level = level
|
val level = level
|
||||||
check(level is ServerLevel) { "Invalid realm" }
|
check(level is ServerLevel) { "Invalid realm" }
|
||||||
unsubscribe()
|
unsubscribe()
|
||||||
val subs = playerMap.computeIfAbsent(level) { Long2ObjectAVLTreeMap() }.computeIfAbsent(ChunkPos(blockPos).toLong(), Long2ObjectFunction { Subscribers(level = WeakReference(level), chunkPos = ChunkPos(blockPos).toLong()) })
|
|
||||||
|
val subs = playerMap
|
||||||
|
.computeIfAbsent(level) { Long2ObjectAVLTreeMap() }
|
||||||
|
.computeIfAbsent(ChunkPos(blockPos).toLong(), Long2ObjectFunction { ChunkSubscribers(level = WeakReference(level), chunkPos = ChunkPos(blockPos).toLong()) })
|
||||||
|
|
||||||
subs.subscribe(this)
|
subs.subscribe(this)
|
||||||
_subCache = subs
|
_subCache = subs
|
||||||
return subs
|
return subs
|
||||||
@ -199,6 +125,124 @@ abstract class SynchronizedBlockEntity(p_155228_: BlockEntityType<*>, p_155229_:
|
|||||||
synchronizer.markClean()
|
synchronizer.markClean()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private data class ChunkSubscribers(
|
||||||
|
val blockEntities: ArrayList<WeakReference<SynchronizedBlockEntity>> = ArrayList(0),
|
||||||
|
val players: ArrayList<ServerPlayer> = ArrayList(1),
|
||||||
|
val observingBlockEntities: ArrayList<WeakReference<SynchronizedBlockEntity>> = ArrayList(0),
|
||||||
|
val level: WeakReference<ServerLevel>,
|
||||||
|
val chunkPos: Long,
|
||||||
|
var changeset: Int = 0
|
||||||
|
) {
|
||||||
|
fun cleanUpBlocks() {
|
||||||
|
val listIterator = blockEntities.listIterator()
|
||||||
|
|
||||||
|
for (block in listIterator) {
|
||||||
|
if (block.get() == null) {
|
||||||
|
listIterator.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val hasObservers: Boolean get() {
|
||||||
|
val listIterator = blockEntities.listIterator()
|
||||||
|
|
||||||
|
for (value in listIterator) {
|
||||||
|
val ref = value.get()
|
||||||
|
|
||||||
|
if (ref == null) {
|
||||||
|
listIterator.remove()
|
||||||
|
} else if (ref.synchronizer.hasObservers) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun subscribe(blockEntity: SynchronizedBlockEntity) {
|
||||||
|
val iterator = blockEntities.listIterator()
|
||||||
|
|
||||||
|
for (value in iterator) {
|
||||||
|
val ref = value.get()
|
||||||
|
|
||||||
|
if (ref === blockEntity) {
|
||||||
|
return
|
||||||
|
} else if (ref == null) {
|
||||||
|
iterator.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val bref = WeakReference(blockEntity)
|
||||||
|
blockEntities.add(bref)
|
||||||
|
changeset++
|
||||||
|
|
||||||
|
onceServer {
|
||||||
|
blockEntity.synchronizeToPlayers()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blockEntity.synchronizer.hasObservers) {
|
||||||
|
observingBlockEntities.add(bref)
|
||||||
|
val listing = tickingMap.computeIfAbsent(level.get() ?: throw NullPointerException("Level got GCd!")) { ArrayList(1) }
|
||||||
|
val tickerIterator = listing.listIterator()
|
||||||
|
|
||||||
|
for (value in tickerIterator) {
|
||||||
|
val ref = value.get()
|
||||||
|
|
||||||
|
if (ref === this) {
|
||||||
|
return
|
||||||
|
} else if (ref == null) {
|
||||||
|
tickerIterator.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
listing.add(WeakReference(this))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unsubscribe(blockEntity: SynchronizedBlockEntity): Boolean {
|
||||||
|
val listIterator = blockEntities.listIterator()
|
||||||
|
|
||||||
|
for (value in listIterator) {
|
||||||
|
if (value.get() === blockEntity) {
|
||||||
|
listIterator.remove()
|
||||||
|
changeset++
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blockEntity.synchronizer.hasObservers) {
|
||||||
|
val tickerIterator = observingBlockEntities.listIterator()
|
||||||
|
|
||||||
|
for (value in tickerIterator) {
|
||||||
|
val ref = value.get()
|
||||||
|
|
||||||
|
if (ref === blockEntity) {
|
||||||
|
tickerIterator.remove()
|
||||||
|
break
|
||||||
|
} else if (ref == null) {
|
||||||
|
tickerIterator.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (players.isNotEmpty()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanUpBlocks()
|
||||||
|
return blockEntities.isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
val isEmpty: Boolean get() {
|
||||||
|
if (players.isNotEmpty()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanUpBlocks()
|
||||||
|
return blockEntities.isEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Why track player-tracked chunks?
|
* Why track player-tracked chunks?
|
||||||
*
|
*
|
||||||
@ -214,14 +258,55 @@ abstract class SynchronizedBlockEntity(p_155228_: BlockEntityType<*>, p_155229_:
|
|||||||
* [net.minecraft.server.level.ChunkMap.getPlayers], which is not something we want
|
* [net.minecraft.server.level.ChunkMap.getPlayers], which is not something we want
|
||||||
*/
|
*/
|
||||||
companion object {
|
companion object {
|
||||||
private val playerMap = WeakHashMap<ServerLevel, Long2ObjectAVLTreeMap<Subscribers>>()
|
private val playerMap = WeakHashMap<ServerLevel, Long2ObjectAVLTreeMap<ChunkSubscribers>>()
|
||||||
|
private val tickingMap = WeakHashMap<ServerLevel, ArrayList<WeakReference<ChunkSubscribers>>>()
|
||||||
|
|
||||||
fun onServerStopping(event: ServerStoppingEvent) {
|
fun onServerStopping(event: ServerStoppingEvent) {
|
||||||
playerMap.clear()
|
playerMap.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun postLevelTick(event: LevelTickEvent) {
|
||||||
|
if (event.phase == TickEvent.Phase.END) {
|
||||||
|
val level = event.level as? ServerLevel ?: return
|
||||||
|
val listing = tickingMap[level] ?: return
|
||||||
|
val listIterator = listing.listIterator()
|
||||||
|
var hitAnyObservers = false
|
||||||
|
|
||||||
|
for (value in listIterator) {
|
||||||
|
val ref = value.get()
|
||||||
|
|
||||||
|
if (ref == null) {
|
||||||
|
listIterator.remove()
|
||||||
|
} else {
|
||||||
|
var hitObservers = false
|
||||||
|
val blockIterator = ref.observingBlockEntities.listIterator()
|
||||||
|
|
||||||
|
for (bvalue in blockIterator) {
|
||||||
|
val bref = bvalue.get()
|
||||||
|
|
||||||
|
if (bref == null) {
|
||||||
|
blockIterator.remove()
|
||||||
|
} else {
|
||||||
|
hitObservers = true
|
||||||
|
hitAnyObservers = true
|
||||||
|
bref.synchronizeToPlayers()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hitObservers) {
|
||||||
|
listIterator.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hitAnyObservers) {
|
||||||
|
tickingMap.remove(level)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun onWatch(event: ChunkWatchEvent.Watch) {
|
fun onWatch(event: ChunkWatchEvent.Watch) {
|
||||||
playerMap.computeIfAbsent(event.level) { Long2ObjectAVLTreeMap() }.computeIfAbsent(event.pos.toLong(), Long2ObjectFunction { Subscribers(level = WeakReference(event.level), chunkPos = event.pos.toLong()) }).let {
|
playerMap.computeIfAbsent(event.level) { Long2ObjectAVLTreeMap() }.computeIfAbsent(event.pos.toLong(), Long2ObjectFunction { ChunkSubscribers(level = WeakReference(event.level), chunkPos = event.pos.toLong()) }).let {
|
||||||
val (blocks, players) = it
|
val (blocks, players) = it
|
||||||
|
|
||||||
if (event.player !in players) {
|
if (event.player !in players) {
|
||||||
|
@ -12,7 +12,7 @@ import java.io.InputStream
|
|||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.function.Consumer
|
import java.util.function.Supplier
|
||||||
import kotlin.ConcurrentModificationException
|
import kotlin.ConcurrentModificationException
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.collections.HashMap
|
import kotlin.collections.HashMap
|
||||||
@ -20,6 +20,7 @@ import kotlin.properties.ReadOnlyProperty
|
|||||||
import kotlin.properties.ReadWriteProperty
|
import kotlin.properties.ReadWriteProperty
|
||||||
import kotlin.reflect.KMutableProperty0
|
import kotlin.reflect.KMutableProperty0
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
import kotlin.reflect.KProperty0
|
||||||
|
|
||||||
interface FieldAccess<V> {
|
interface FieldAccess<V> {
|
||||||
fun read(): V
|
fun read(): V
|
||||||
@ -35,7 +36,7 @@ fun interface FieldSetter<V> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sealed interface IField<V> : ReadOnlyProperty<Any, V> {
|
sealed interface IField<V> : ReadOnlyProperty<Any, V> {
|
||||||
fun observe()
|
fun observe(): Boolean
|
||||||
fun markDirty()
|
fun markDirty()
|
||||||
fun markDirty(endpoint: FieldSynchronizer.Endpoint)
|
fun markDirty(endpoint: FieldSynchronizer.Endpoint)
|
||||||
val value: V
|
val value: V
|
||||||
@ -70,6 +71,7 @@ enum class MapAction {
|
|||||||
CLEAR, ADD, REMOVE
|
CLEAR, ADD, REMOVE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCallback: Boolean) {
|
class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCallback: Boolean) {
|
||||||
constructor() : this(Runnable {}, false)
|
constructor() : this(Runnable {}, false)
|
||||||
constructor(callback: Runnable) : this(callback, false)
|
constructor(callback: Runnable) : this(callback, false)
|
||||||
@ -77,6 +79,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
private val fields = ArrayList<IField<*>>(0)
|
private val fields = ArrayList<IField<*>>(0)
|
||||||
private val observers = ArrayList<IField<*>>(0)
|
private val observers = ArrayList<IField<*>>(0)
|
||||||
|
|
||||||
|
val hasObservers: Boolean get() = observers.isNotEmpty()
|
||||||
val isEmpty: Boolean get() = fields.isEmpty()
|
val isEmpty: Boolean get() = fields.isEmpty()
|
||||||
val isNotEmpty: Boolean get() = fields.isNotEmpty()
|
val isNotEmpty: Boolean get() = fields.isNotEmpty()
|
||||||
|
|
||||||
@ -99,6 +102,54 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
hasChanges = false
|
hasChanges = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun byte(getter: () -> Byte) = ComputedField(getter, ByteValueCodec)
|
||||||
|
fun bool(getter: () -> Boolean) = ComputedField(getter, BooleanValueCodec)
|
||||||
|
fun short(getter: () -> Short) = ComputedField(getter, ShortValueCodec)
|
||||||
|
fun long(getter: () -> Long) = ComputedField(getter, VarLongValueCodec)
|
||||||
|
fun fixedLong(getter: () -> Long) = ComputedField(getter, LongValueCodec)
|
||||||
|
fun float(getter: () -> Float) = ComputedField(getter, FloatValueCodec)
|
||||||
|
fun double(getter: () -> Double) = ComputedField(getter, DoubleValueCodec)
|
||||||
|
fun uuid(getter: () -> UUID) = ComputedField(getter, UUIDValueCodec)
|
||||||
|
fun int(getter: () -> Int) = ComputedField(getter, VarIntValueCodec)
|
||||||
|
fun fixedInt(getter: () -> Int) = ComputedField(getter, IntValueCodec)
|
||||||
|
fun fraction(getter: () -> ImpreciseFraction) = ComputedField(getter, ImpreciseFractionValueCodec)
|
||||||
|
fun bigDecimal(getter: () -> BigDecimal) = ComputedField(getter, BigDecimalValueCodec)
|
||||||
|
fun item(getter: () -> ItemStack) = ComputedField(getter, ItemStackValueCodec)
|
||||||
|
|
||||||
|
fun byte(getter: KProperty0<Byte>) = ComputedField(getter, ByteValueCodec)
|
||||||
|
fun bool(getter: KProperty0<Boolean>) = ComputedField(getter, BooleanValueCodec)
|
||||||
|
fun short(getter: KProperty0<Short>) = ComputedField(getter, ShortValueCodec)
|
||||||
|
fun long(getter: KProperty0<Long>) = ComputedField(getter, VarLongValueCodec)
|
||||||
|
fun fixedLong(getter: KProperty0<Long>) = ComputedField(getter, LongValueCodec)
|
||||||
|
fun float(getter: KProperty0<Float>) = ComputedField(getter, FloatValueCodec)
|
||||||
|
fun double(getter: KProperty0<Double>) = ComputedField(getter, DoubleValueCodec)
|
||||||
|
fun uuid(getter: KProperty0<UUID>) = ComputedField(getter, UUIDValueCodec)
|
||||||
|
fun int(getter: KProperty0<Int>) = ComputedField(getter, VarIntValueCodec)
|
||||||
|
fun fixedInt(getter: KProperty0<Int>) = ComputedField(getter, IntValueCodec)
|
||||||
|
fun fraction(getter: KProperty0<ImpreciseFraction>) = ComputedField(getter, ImpreciseFractionValueCodec)
|
||||||
|
fun bigDecimal(getter: KProperty0<BigDecimal>) = ComputedField(getter, BigDecimalValueCodec)
|
||||||
|
fun item(getter: KProperty0<ItemStack>) = ComputedField(getter, ItemStackValueCodec)
|
||||||
|
|
||||||
|
fun byte(getter: Supplier<Byte>) = ComputedField(getter::get, ByteValueCodec)
|
||||||
|
fun bool(getter: Supplier<Boolean>) = ComputedField(getter::get, BooleanValueCodec)
|
||||||
|
fun short(getter: Supplier<Short>) = ComputedField(getter::get, ShortValueCodec)
|
||||||
|
fun long(getter: Supplier<Long>) = ComputedField(getter::get, VarLongValueCodec)
|
||||||
|
fun fixedLong(getter: Supplier<Long>) = ComputedField(getter::get, LongValueCodec)
|
||||||
|
fun float(getter: Supplier<Float>) = ComputedField(getter::get, FloatValueCodec)
|
||||||
|
fun double(getter: Supplier<Double>) = ComputedField(getter::get, DoubleValueCodec)
|
||||||
|
fun uuid(getter: Supplier<UUID>) = ComputedField(getter::get, UUIDValueCodec)
|
||||||
|
fun int(getter: Supplier<Int>) = ComputedField(getter::get, VarIntValueCodec)
|
||||||
|
fun fixedInt(getter: Supplier<Int>) = ComputedField(getter::get, IntValueCodec)
|
||||||
|
fun fraction(getter: Supplier<ImpreciseFraction>) = ComputedField(getter::get, ImpreciseFractionValueCodec)
|
||||||
|
fun bigDecimal(getter: Supplier<BigDecimal>) = ComputedField(getter::get, BigDecimalValueCodec)
|
||||||
|
fun item(getter: Supplier<ItemStack>) = ComputedField(getter::get, ItemStackValueCodec)
|
||||||
|
|
||||||
|
fun <T : Enum<T>> enum(type: Class<T>, getter: () -> T) = ComputedField(getter, EnumValueCodec(type))
|
||||||
|
inline fun <reified T : Enum<T>> enum(noinline getter: () -> T) = ComputedField(getter, EnumValueCodec(T::class.java))
|
||||||
|
|
||||||
|
fun <T : Enum<T>> enum(type: Class<T>, getter: KProperty0<T>) = ComputedField(getter, EnumValueCodec(type))
|
||||||
|
inline fun <reified T : Enum<T>> enum(getter: KProperty0<T>) = ComputedField(getter, EnumValueCodec(T::class.java))
|
||||||
|
|
||||||
fun byte(
|
fun byte(
|
||||||
value: Byte = 0,
|
value: Byte = 0,
|
||||||
getter: FieldGetter<Byte>? = null,
|
getter: FieldGetter<Byte>? = null,
|
||||||
@ -216,8 +267,9 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
value: ItemStack = ItemStack.EMPTY,
|
value: ItemStack = ItemStack.EMPTY,
|
||||||
getter: FieldGetter<ItemStack>? = null,
|
getter: FieldGetter<ItemStack>? = null,
|
||||||
setter: FieldSetter<ItemStack>? = null,
|
setter: FieldSetter<ItemStack>? = null,
|
||||||
|
observe: Boolean = true
|
||||||
): Field<ItemStack> {
|
): Field<ItemStack> {
|
||||||
return Field(value, ItemStackValueCodec, getter, setter, isObserver = true)
|
return Field(value, ItemStackValueCodec, getter, setter, isObserver = observe)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun item(
|
fun item(
|
||||||
@ -421,11 +473,13 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun observe() {
|
override fun observe(): Boolean {
|
||||||
if (!isDirty && !codec.compare(remote, field)) {
|
if (!isDirty && !codec.compare(remote, field)) {
|
||||||
notifyEndpoints(this@Field)
|
notifyEndpoints(this@Field)
|
||||||
isDirty = true
|
isDirty = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return isDirty
|
||||||
}
|
}
|
||||||
|
|
||||||
override var value: V
|
override var value: V
|
||||||
@ -489,8 +543,59 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inner class ComputedField<V>(
|
||||||
|
private val getter: () -> V,
|
||||||
|
private val codec: IStreamCodec<V>,
|
||||||
|
) : IField<V> {
|
||||||
|
private var remote: V? = null
|
||||||
|
private var clientValue: V? = null
|
||||||
|
private var isDirty = false
|
||||||
|
|
||||||
|
val id = fields.size + 1
|
||||||
|
|
||||||
|
init {
|
||||||
|
fields.add(this)
|
||||||
|
observers.add(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun observe(): Boolean {
|
||||||
|
if (!isDirty && (remote == null || !codec.compare(remote ?: throw ConcurrentModificationException(), value))) {
|
||||||
|
notifyEndpoints(this)
|
||||||
|
isDirty = true
|
||||||
|
remote = codec.copy(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return isDirty
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun markDirty() {
|
||||||
|
if (!isDirty && clientValue == null) {
|
||||||
|
notifyEndpoints(this)
|
||||||
|
isDirty = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun markDirty(endpoint: Endpoint) {
|
||||||
|
endpoint.addDirtyField(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val value: V
|
||||||
|
get() = clientValue ?: getter.invoke()
|
||||||
|
|
||||||
|
override fun write(stream: DataOutputStream, endpoint: Endpoint) {
|
||||||
|
stream.write(id)
|
||||||
|
val value = value
|
||||||
|
codec.write(stream, value)
|
||||||
|
isDirty = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun read(stream: DataInputStream) {
|
||||||
|
clientValue = codec.read(stream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inner class ObservedField<V> : IMutableField<V> {
|
inner class ObservedField<V> : IMutableField<V> {
|
||||||
private val codec: StreamCodec<V>
|
private val codec: IStreamCodec<V>
|
||||||
private val getter: () -> V
|
private val getter: () -> V
|
||||||
private val setter: (V) -> Unit
|
private val setter: (V) -> Unit
|
||||||
|
|
||||||
@ -500,18 +605,18 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
get() = getter.invoke()
|
get() = getter.invoke()
|
||||||
set(value) { setter.invoke(value) }
|
set(value) { setter.invoke(value) }
|
||||||
|
|
||||||
constructor(field: KMutableProperty0<V>, dispatcher: StreamCodec<V>) {
|
constructor(field: KMutableProperty0<V>, codec: IStreamCodec<V>) {
|
||||||
this.codec = dispatcher
|
this.codec = codec
|
||||||
getter = field::get
|
getter = field::get
|
||||||
setter = field::set
|
setter = field::set
|
||||||
remote = dispatcher.copy(value)
|
remote = codec.copy(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(getter: () -> V, setter: (V) -> Unit, dispatcher: StreamCodec<V>) {
|
constructor(getter: () -> V, setter: (V) -> Unit, codec: IStreamCodec<V>) {
|
||||||
this.codec = dispatcher
|
this.codec = codec
|
||||||
this.getter = getter
|
this.getter = getter
|
||||||
this.setter = setter
|
this.setter = setter
|
||||||
remote = dispatcher.copy(value)
|
remote = codec.copy(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var isDirty = false
|
private var isDirty = false
|
||||||
@ -523,12 +628,14 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
observers.add(this)
|
observers.add(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun observe() {
|
override fun observe(): Boolean {
|
||||||
if (!isDirty && !codec.compare(remote, value)) {
|
if (!isDirty && !codec.compare(remote, value)) {
|
||||||
notifyEndpoints(this)
|
notifyEndpoints(this)
|
||||||
isDirty = true
|
isDirty = true
|
||||||
remote = codec.copy(value)
|
remote = codec.copy(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return isDirty
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun markDirty() {
|
override fun markDirty() {
|
||||||
@ -601,9 +708,9 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun observe() {
|
override fun observe(): Boolean {
|
||||||
if (isRemote) {
|
if (isRemote) {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
val observingBackingMap = observingBackingMap
|
val observingBackingMap = observingBackingMap
|
||||||
@ -630,6 +737,8 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return isDirty
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun markDirty() {
|
override fun markDirty() {
|
||||||
@ -844,12 +953,16 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun observe() {
|
fun observe(): Boolean {
|
||||||
|
var changes = false
|
||||||
|
|
||||||
if (observers.isNotEmpty()) {
|
if (observers.isNotEmpty()) {
|
||||||
for (field in observers) {
|
for (field in observers) {
|
||||||
field.observe()
|
changes = field.observe() || changes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return changes
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user