diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/network/synchronizer/FieldSynchronizer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/network/synchronizer/FieldSynchronizer.kt index 76a7c5ae5..0c5a96766 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/network/synchronizer/FieldSynchronizer.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/network/synchronizer/FieldSynchronizer.kt @@ -48,7 +48,7 @@ import kotlin.reflect.KProperty0 * Universal, one-to-many value synchronizer, allowing to synchronize values from server to client * anywhere, where input/output streams are supported */ -@Suppress("unused") +@Suppress("unused", "BlockingMethodInNonBlockingContext") class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCallback: Boolean) { constructor() : this(Runnable {}, false) constructor(callback: Runnable) : this(callback, false) @@ -485,6 +485,8 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa final override var isRemoved = false private set + protected var isDirty = false + override fun remove() { if (isRemoved) return @@ -508,6 +510,12 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa check(!isRemoved) { "Field was removed" } endpoint.addDirtyField(this) } + + override fun markDirty() { + check(!isRemoved) { "Field was removed" } + notifyEndpoints(this@AbstractField) + isDirty = true + } } /** @@ -528,8 +536,6 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa } } - private var isDirty = false - private val access = object : FieldAccess { override fun read(): V { return field @@ -582,12 +588,6 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa this.field = value } - override fun markDirty() { - check(!isRemoved) { "Field was removed" } - notifyEndpoints(this@Field) - isDirty = true - } - override fun write(stream: DataOutputStream, endpoint: Endpoint) { check(!isRemoved) { "Field was removed" } codec.write(stream, field) @@ -609,19 +609,11 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa } } - abstract inner class PrimitiveField() : AbstractField() { - protected var isDirty = false - + abstract inner class PrimitiveField : AbstractField() { override fun observe(): Boolean { check(!isRemoved) { "Field was removed" } return isDirty } - - override fun markDirty() { - check(!isRemoved) { "Field was removed" } - notifyEndpoints(this) - isDirty = true - } } /** @@ -1022,7 +1014,6 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa ) : AbstractField(), IField { private var remote: V? = null private var clientValue: V? = null - private var isDirty = false init { observers.add(this) @@ -1042,12 +1033,6 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa return isDirty } - override fun markDirty() { - check(!isRemoved) { "Field was removed" } - notifyEndpoints(this) - isDirty = true - } - override val value: V get() = clientValue ?: getter.invoke() @@ -1075,7 +1060,6 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa private var isRemoteSet = false private var clientValue: Float = 0f private var isClientValue = false - private var isDirty = false override val property = object : IFloatProperty { override fun getValue(thisRef: Any?, property: KProperty<*>): Float { @@ -1133,7 +1117,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property")) override fun getValue(thisRef: Any?, property: KProperty<*>): Float { - return super.getValue(thisRef, property) + return float } } @@ -1147,7 +1131,6 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa private var isRemoteSet = false private var clientValue: Double = 0.0 private var isClientValue = false - private var isDirty = false override val property = object : IDoubleProperty { override fun getValue(thisRef: Any?, property: KProperty<*>): Double { @@ -1174,12 +1157,6 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa return isDirty } - override fun markDirty() { - check(!isRemoved) { "Field was removed" } - notifyEndpoints(this) - isDirty = true - } - override val double: Double get() { if (isClientValue) { @@ -1205,7 +1182,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property")) override fun getValue(thisRef: Any?, property: KProperty<*>): Double { - return super.getValue(thisRef, property) + return double } } @@ -1219,7 +1196,6 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa private var isRemoteSet = false protected var clientValue: Int = 0 protected var isClientValue = false - protected var isDirty = false final override val property = object : IIntProperty { override fun getValue(thisRef: Any?, property: KProperty<*>): Int { @@ -1246,12 +1222,6 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa return isDirty } - final override fun markDirty() { - check(!isRemoved) { "Field was removed" } - notifyEndpoints(this) - isDirty = true - } - final override val int: Int get() { if (isClientValue) { @@ -1263,7 +1233,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property")) override fun getValue(thisRef: Any?, property: KProperty<*>): Int { - return super.getValue(thisRef, property) + return int } } @@ -1319,7 +1289,6 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa private var isRemoteSet = false protected var clientValue: Long = 0L protected var isClientValue = false - protected var isDirty = false final override val property = object : ILongProperty { override fun getValue(thisRef: Any?, property: KProperty<*>): Long { @@ -1346,12 +1315,6 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa return isDirty } - final override fun markDirty() { - check(!isRemoved) { "Field was removed" } - notifyEndpoints(this) - isDirty = true - } - final override val long: Long get() { if (isClientValue) { @@ -1363,7 +1326,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property")) override fun getValue(thisRef: Any?, property: KProperty<*>): Long { - return super.getValue(thisRef, property) + return long } } @@ -1419,7 +1382,6 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa private var isRemoteSet = false private var clientValue: Boolean = false private var isClientValue = false - private var isDirty = false override val property = object : IBooleanProperty { override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean { @@ -1446,12 +1408,6 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa return isDirty } - override fun markDirty() { - check(!isRemoved) { "Field was removed" } - notifyEndpoints(this) - isDirty = true - } - override val boolean: Boolean get() { if (isClientValue) { @@ -1477,7 +1433,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property")) override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean { - return super.getValue(thisRef, property) + return boolean } } @@ -1508,8 +1464,6 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa remote = codec.copy(value) } - private var isDirty = false - init { observers.add(this) } @@ -1528,12 +1482,6 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa return isDirty } - override fun markDirty() { - check(!isRemoved) { "Field was removed" } - notifyEndpoints(this) - isDirty = true - } - override fun write(stream: DataOutputStream, endpoint: Endpoint) { check(!isRemoved) { "Field was removed" } codec.write(stream, value) @@ -1552,7 +1500,6 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa private val callback: ((changes: Collection>) -> Unit)? = null, ) : AbstractField>() { private var isRemote = false - private var isDirty = false private fun pushBacklog(element: E, action: (DataOutputStream) -> Unit) { check(!isRemote) { "Field marked as remote" } @@ -1585,12 +1532,6 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa super.remove() } - override fun markDirty() { - check(!isRemoved) { "Field was removed" } - notifyEndpoints(this) - isDirty = true - } - override fun markDirty(endpoint: Endpoint) { super.markDirty(endpoint) @@ -1610,9 +1551,11 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa if (!isRemote) { markDirty() + val copy = codec.copy(element) + pushBacklog(element) { it.write(ChangesetAction.ADD.ordinal + 1) - codec.write(it, element) + codec.write(it, copy) } } @@ -1668,6 +1611,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa if (!isRemote) { markDirty() + @Suppress("unchecked_cast") pushBacklog(lastElement as E) { it.write(ChangesetAction.REMOVE.ordinal + 1) codec.write(it, lastElement as E) @@ -1683,9 +1627,11 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa if (!isRemote) { markDirty() + val copy = codec.copy(element) + pushBacklog(element) { it.write(ChangesetAction.REMOVE.ordinal + 1) - codec.write(it, element) + codec.write(it, copy) } } @@ -1790,19 +1736,11 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa private val keyCodec: IStreamCodec, private val valueCodec: IStreamCodec, private val backingMap: MutableMap, - private val observingBackingMap: MutableMap? = null, private val callback: ((changes: Collection>) -> Unit)? = null, ) : AbstractField>(), IField> { - private var isDirty = false private var sentAllValues = false private var isRemote = false - init { - if (observingBackingMap != null) { - observers.add(this) - } - } - private fun pushBacklog(key: Any?, value: (DataOutputStream) -> Unit) { val pair = key to value @@ -1820,62 +1758,15 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa } } - private fun clearBacklog() { - forEachEndpoint { - it.getMapBacklog(this).clear() - } - } - override fun observe(): Boolean { - check(!isRemoved) { "Field was removed" } - - if (isRemote) { - return false - } - - val observingBackingMap = observingBackingMap - - if (observingBackingMap != null) { - for ((key, value) in backingMap) { - val remoteValue = observingBackingMap[key] ?: throw ConcurrentModificationException("Backing map of $this was modified externally, or $value missed a modification") - - if (!valueCodec.compare(value, remoteValue)) { - val valueCopy = valueCodec.copy(value) - - pushBacklog(key) { - it.write(ChangesetAction.ADD.ordinal + 1) - keyCodec.write(it, key) - valueCodec.write(it, valueCopy) - } - - observingBackingMap[key] = valueCopy - - if (!isDirty) { - notifyEndpoints(this) - isDirty = true - } - } - } - } - - return isDirty + return false } override fun markDirty() { check(!isRemoved) { "Field was removed" } - if (isRemote) { + if (isRemote || endpoints.isEmpty()) return - } - - if (endpoints.isEmpty()) { - val observingBackingMap = observingBackingMap ?: return - - for ((key, value) in backingMap) - observingBackingMap[key] = valueCodec.copy(value) - - return - } isDirty = true val backlogs = LinkedList Unit>>>() @@ -1900,17 +1791,14 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa for (backlog in backlogs) { backlog.add(key to action) } - - observingBackingMap?.put(key, valueCopy) } } override fun markDirty(endpoint: Endpoint) { check(!isRemoved) { "Field was removed" } - if (isRemote) { + if (isRemote) return - } val backlog = endpoint.getMapBacklog(this) @@ -1928,69 +1816,49 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa } } + private fun lmarkDirty() { + if (!isDirty) { + notifyEndpoints(this@Map) + isDirty = true + } + } + override val value: MutableMap = object : ProxiedMap(backingMap) { override fun onClear() { - if (isRemote) { + if (isRemote || endpoints.isEmpty()) return - } - - if (endpoints.isEmpty()) { - return - } - - observingBackingMap?.clear() forEachEndpoint { endpoint -> - endpoint.getMapBacklog(this@Map).also { + endpoint.getMapBacklog(this@Map).let { it.clear() it.add(null to ClearBacklogEntry) } } - if (!isDirty) { - notifyEndpoints(this@Map) - isDirty = true - } - + lmarkDirty() hasChanges = true } override fun onValueAdded(key: K, value: V) { - if (isRemote) { + if (isRemote || endpoints.isEmpty()) return - } - - if (endpoints.isEmpty()) { - return - } + val keyCopy = keyCodec.copy(key) val valueCopy = valueCodec.copy(value) pushBacklog(key) { @Suppress("BlockingMethodInNonBlockingContext") // false positive it.write(ChangesetAction.ADD.ordinal + 1) - keyCodec.write(it, key) + keyCodec.write(it, keyCopy) valueCodec.write(it, valueCopy) } - observingBackingMap?.put(key, valueCopy) - - if (!isDirty) { - notifyEndpoints(this@Map) - isDirty = true - } - - hasChanges = true + lmarkDirty() } override fun onValueRemoved(key: K, value: V) { - if (isRemote) { + if (isRemote || endpoints.isEmpty()) return - } - - if (endpoints.isEmpty()) { - return - } val keyCopy = keyCodec.copy(key) @@ -2000,14 +1868,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa keyCodec.write(it, keyCopy) } - observingBackingMap?.remove(key) - - if (!isDirty) { - notifyEndpoints(this@Map) - isDirty = true - } - - hasChanges = true + lmarkDirty() } } @@ -2029,7 +1890,6 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa if (!isRemote) { isRemote = true forEachEndpoint { it.removeMapBacklog(this) } - observingBackingMap?.clear() } isDirty = false