From 3c797b9131b299c5b7a87002298002f1c287365a Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Thu, 8 Feb 2024 19:59:14 +0700 Subject: [PATCH] Slice and splice delegates, listenables, syncheddelegates --- gradle.properties | 2 +- .../kommons/gson/EitherTypeAdapter.kt | 2 +- .../kommons/gson/KOptionalTypeAdapter.kt | 2 +- .../ru/dbotthepony/kommons/io/VectorsIO.kt | 33 +- .../ru/dbotthepony/kommons/matrix/Matrix2d.kt | 2 +- .../ru/dbotthepony/kommons/matrix/Matrix2f.kt | 2 +- .../ru/dbotthepony/kommons/matrix/Matrix3d.kt | 4 +- .../ru/dbotthepony/kommons/matrix/Matrix3f.kt | 4 +- .../ru/dbotthepony/kommons/matrix/Matrix4d.kt | 6 +- .../ru/dbotthepony/kommons/matrix/Matrix4f.kt | 6 +- .../ru/dbotthepony/kommons/util/AABB.kt | 1 - .../ru/dbotthepony/kommons/util/AABBi.kt | 1 - .../ru/dbotthepony/kommons/vector/Vector2d.kt | 6 +- .../ru/dbotthepony/kommons/vector/Vector2f.kt | 6 +- .../ru/dbotthepony/kommons/vector/Vector2i.kt | 6 +- .../ru/dbotthepony/kommons/vector/Vector3d.kt | 12 +- .../ru/dbotthepony/kommons/vector/Vector3f.kt | 12 +- .../ru/dbotthepony/kommons/vector/Vector3i.kt | 12 +- .../ru/dbotthepony/kommons/vector/Vector4d.kt | 19 +- .../ru/dbotthepony/kommons/vector/Vector4f.kt | 18 +- .../ru/dbotthepony/kommons/vector/Vector4i.kt | 19 +- .../kommons/collect/CollectionUtils.kt | 2 +- .../dbotthepony/kommons/core/GetterSetter.kt | 149 -- .../kommons/event/IBooleanSubscriptable.kt | 43 - .../kommons/event/IDoubleSubcriptable.kt | 43 - .../kommons/event/IFloatSubcriptable.kt | 43 - .../kommons/event/IIntSubcriptable.kt | 43 - .../kommons/event/ILongSubcriptable.kt | 43 - .../kommons/event/ISubscriptable.kt | 60 - .../kommons/event/IUnitSubscriptable.kt | 41 - .../{networking => io}/ChangesetAction.kt | 2 +- .../kommons/io/CollectionStreamCodec.kt | 34 - .../ru/dbotthepony/kommons/io/DecimalIO.kt | 2 +- .../dbotthepony/kommons/io/EnumValueCodec.kt | 48 - .../ru/dbotthepony/kommons/io/IStreamCodec.kt | 28 - .../{networking => io}/MapChangeset.kt | 2 +- .../kommons/io/OutputStreamUtils.kt | 36 +- .../{networking => io}/SetChangeset.kt | 2 +- .../ru/dbotthepony/kommons/io/StreamCodec.kt | 175 ++ .../ru/dbotthepony/kommons/io/StreamCodecs.kt | 77 - .../kommons/io/SynchedDelegates.kt | 945 +++++++ .../ru/dbotthepony/kommons/math/Intersect.kt | 8 +- .../ru/dbotthepony/kommons/math/RGBAColor.kt | 2 +- .../kommons/networking/FieldAccess.kt | 41 - .../kommons/networking/FieldGetter.kt | 50 - .../kommons/networking/FieldSetter.kt | 50 - .../kommons/networking/FieldSynchronizer.kt | 2175 ----------------- .../dbotthepony/kommons/networking/Fields.kt | 173 -- .../kommons/networking/MutableFields.kt | 165 -- .../ru/dbotthepony/kommons/util/Delegate.kt | 64 + .../kommons/util/DelegateGetter.kt | 17 + .../kommons/util/DelegateSetter.kt | 17 + .../kommons/{core => util}/Either.kt | 2 +- .../dbotthepony/kommons/util/FloatSupplier.kt | 12 - .../kommons/{core => util}/KOptional.kt | 2 +- .../ru/dbotthepony/kommons/util/Listenable.kt | 100 + .../kommons/util/ListenableDelegate.kt | 177 ++ .../ru/dbotthepony/kommons/util/Observer.kt | 11 + .../kommons/{core => util}/Struct.kt | 2 +- .../dbotthepony/kommons/util/ValueObserver.kt | 157 ++ 60 files changed, 1787 insertions(+), 3431 deletions(-) delete mode 100644 src/main/kotlin/ru/dbotthepony/kommons/core/GetterSetter.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/kommons/event/IBooleanSubscriptable.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/kommons/event/IDoubleSubcriptable.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/kommons/event/IFloatSubcriptable.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/kommons/event/IIntSubcriptable.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/kommons/event/ILongSubcriptable.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/kommons/event/ISubscriptable.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/kommons/event/IUnitSubscriptable.kt rename src/main/kotlin/ru/dbotthepony/kommons/{networking => io}/ChangesetAction.kt (55%) delete mode 100644 src/main/kotlin/ru/dbotthepony/kommons/io/CollectionStreamCodec.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/kommons/io/EnumValueCodec.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/kommons/io/IStreamCodec.kt rename src/main/kotlin/ru/dbotthepony/kommons/{networking => io}/MapChangeset.kt (93%) rename src/main/kotlin/ru/dbotthepony/kommons/{networking => io}/SetChangeset.kt (92%) create mode 100644 src/main/kotlin/ru/dbotthepony/kommons/io/StreamCodec.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/kommons/io/StreamCodecs.kt create mode 100644 src/main/kotlin/ru/dbotthepony/kommons/io/SynchedDelegates.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/kommons/networking/FieldAccess.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/kommons/networking/FieldGetter.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/kommons/networking/FieldSetter.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/kommons/networking/FieldSynchronizer.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/kommons/networking/Fields.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/kommons/networking/MutableFields.kt create mode 100644 src/main/kotlin/ru/dbotthepony/kommons/util/Delegate.kt create mode 100644 src/main/kotlin/ru/dbotthepony/kommons/util/DelegateGetter.kt create mode 100644 src/main/kotlin/ru/dbotthepony/kommons/util/DelegateSetter.kt rename src/main/kotlin/ru/dbotthepony/kommons/{core => util}/Either.kt (97%) delete mode 100644 src/main/kotlin/ru/dbotthepony/kommons/util/FloatSupplier.kt rename src/main/kotlin/ru/dbotthepony/kommons/{core => util}/KOptional.kt (98%) create mode 100644 src/main/kotlin/ru/dbotthepony/kommons/util/Listenable.kt create mode 100644 src/main/kotlin/ru/dbotthepony/kommons/util/ListenableDelegate.kt create mode 100644 src/main/kotlin/ru/dbotthepony/kommons/util/Observer.kt rename src/main/kotlin/ru/dbotthepony/kommons/{core => util}/Struct.kt (97%) create mode 100644 src/main/kotlin/ru/dbotthepony/kommons/util/ValueObserver.kt diff --git a/gradle.properties b/gradle.properties index 3b93f1b..5f93cb8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ kotlin.code.style=official specifyKotlinAsDependency=false projectGroup=ru.dbotthepony.kommons -projectVersion=1.7.9 +projectVersion=2.0.0 guavaDepVersion=33.0.0 gsonDepVersion=2.8.9 diff --git a/gson/src/main/kotlin/ru/dbotthepony/kommons/gson/EitherTypeAdapter.kt b/gson/src/main/kotlin/ru/dbotthepony/kommons/gson/EitherTypeAdapter.kt index 8e77cff..4ebc812 100644 --- a/gson/src/main/kotlin/ru/dbotthepony/kommons/gson/EitherTypeAdapter.kt +++ b/gson/src/main/kotlin/ru/dbotthepony/kommons/gson/EitherTypeAdapter.kt @@ -10,7 +10,7 @@ import com.google.gson.reflect.TypeToken import com.google.gson.stream.JsonReader import com.google.gson.stream.JsonToken import com.google.gson.stream.JsonWriter -import ru.dbotthepony.kommons.core.Either +import ru.dbotthepony.kommons.util.Either import java.lang.reflect.ParameterizedType object EitherTypeAdapter : TypeAdapterFactory { diff --git a/gson/src/main/kotlin/ru/dbotthepony/kommons/gson/KOptionalTypeAdapter.kt b/gson/src/main/kotlin/ru/dbotthepony/kommons/gson/KOptionalTypeAdapter.kt index 6340290..9684c57 100644 --- a/gson/src/main/kotlin/ru/dbotthepony/kommons/gson/KOptionalTypeAdapter.kt +++ b/gson/src/main/kotlin/ru/dbotthepony/kommons/gson/KOptionalTypeAdapter.kt @@ -7,7 +7,7 @@ import com.google.gson.TypeAdapterFactory import com.google.gson.reflect.TypeToken import com.google.gson.stream.JsonReader import com.google.gson.stream.JsonWriter -import ru.dbotthepony.kommons.core.KOptional +import ru.dbotthepony.kommons.util.KOptional import java.lang.reflect.ParameterizedType @Suppress("DEPRECATION") diff --git a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/io/VectorsIO.kt b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/io/VectorsIO.kt index 336f6b2..8ed5354 100644 --- a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/io/VectorsIO.kt +++ b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/io/VectorsIO.kt @@ -1,5 +1,8 @@ package ru.dbotthepony.kommons.io +import ru.dbotthepony.kommons.util.DelegateGetter +import ru.dbotthepony.kommons.util.DelegateSetter +import ru.dbotthepony.kommons.util.ListenableDelegate import ru.dbotthepony.kommons.vector.Vector2d import ru.dbotthepony.kommons.vector.Vector2f import ru.dbotthepony.kommons.vector.Vector2i @@ -37,14 +40,26 @@ fun InputStream.readVector4i() = read4(::Vector4i) { it.readInt() } fun InputStream.readVector4d() = read4(::Vector4d) { it.readDouble() } fun InputStream.readVector4f() = read4(::Vector4f) { it.readFloat() } -val Vector2iCodec = StreamCodec(DataInputStream::readVector2i, DataOutputStream::writeStruct2i) -val Vector2dCodec = StreamCodec(DataInputStream::readVector2d, DataOutputStream::writeStruct2d) -val Vector2fCodec = StreamCodec(DataInputStream::readVector2f, DataOutputStream::writeStruct2f) +val Vector2iCodec = StreamCodec.Impl(DataInputStream::readVector2i, DataOutputStream::writeStruct2i) +val Vector2dCodec = StreamCodec.Impl(DataInputStream::readVector2d, DataOutputStream::writeStruct2d) +val Vector2fCodec = StreamCodec.Impl(DataInputStream::readVector2f, DataOutputStream::writeStruct2f) -val Vector3iCodec = StreamCodec(DataInputStream::readVector3i, DataOutputStream::writeStruct3i) -val Vector3dCodec = StreamCodec(DataInputStream::readVector3d, DataOutputStream::writeStruct3d) -val Vector3fCodec = StreamCodec(DataInputStream::readVector3f, DataOutputStream::writeStruct3f) +val Vector3iCodec = StreamCodec.Impl(DataInputStream::readVector3i, DataOutputStream::writeStruct3i) +val Vector3dCodec = StreamCodec.Impl(DataInputStream::readVector3d, DataOutputStream::writeStruct3d) +val Vector3fCodec = StreamCodec.Impl(DataInputStream::readVector3f, DataOutputStream::writeStruct3f) -val Vector4iCodec = StreamCodec(DataInputStream::readVector4i, DataOutputStream::writeStruct4i) -val Vector4dCodec = StreamCodec(DataInputStream::readVector4d, DataOutputStream::writeStruct4d) -val Vector4fCodec = StreamCodec(DataInputStream::readVector4f, DataOutputStream::writeStruct4f) +val Vector4iCodec = StreamCodec.Impl(DataInputStream::readVector4i, DataOutputStream::writeStruct4i) +val Vector4dCodec = StreamCodec.Impl(DataInputStream::readVector4d, DataOutputStream::writeStruct4d) +val Vector4fCodec = StreamCodec.Impl(DataInputStream::readVector4f, DataOutputStream::writeStruct4f) + +fun SynchedDelegates.vec2i(value: Vector2i, getter: DelegateGetter = DelegateGetter.passthrough(), setter: DelegateSetter = DelegateSetter.passthrough()): ListenableDelegate = RegularValue(value, Vector2iCodec, getter, setter) +fun SynchedDelegates.vec2d(value: Vector2d, getter: DelegateGetter = DelegateGetter.passthrough(), setter: DelegateSetter = DelegateSetter.passthrough()): ListenableDelegate = RegularValue(value, Vector2dCodec, getter, setter) +fun SynchedDelegates.vec2f(value: Vector2f, getter: DelegateGetter = DelegateGetter.passthrough(), setter: DelegateSetter = DelegateSetter.passthrough()): ListenableDelegate = RegularValue(value, Vector2fCodec, getter, setter) + +fun SynchedDelegates.vec3i(value: Vector3i, getter: DelegateGetter = DelegateGetter.passthrough(), setter: DelegateSetter = DelegateSetter.passthrough()): ListenableDelegate = RegularValue(value, Vector3iCodec, getter, setter) +fun SynchedDelegates.vec3d(value: Vector3d, getter: DelegateGetter = DelegateGetter.passthrough(), setter: DelegateSetter = DelegateSetter.passthrough()): ListenableDelegate = RegularValue(value, Vector3dCodec, getter, setter) +fun SynchedDelegates.vec3f(value: Vector3f, getter: DelegateGetter = DelegateGetter.passthrough(), setter: DelegateSetter = DelegateSetter.passthrough()): ListenableDelegate = RegularValue(value, Vector3fCodec, getter, setter) + +fun SynchedDelegates.vec4i(value: Vector4i, getter: DelegateGetter = DelegateGetter.passthrough(), setter: DelegateSetter = DelegateSetter.passthrough()): ListenableDelegate = RegularValue(value, Vector4iCodec, getter, setter) +fun SynchedDelegates.vec4d(value: Vector4d, getter: DelegateGetter = DelegateGetter.passthrough(), setter: DelegateSetter = DelegateSetter.passthrough()): ListenableDelegate = RegularValue(value, Vector4dCodec, getter, setter) +fun SynchedDelegates.vec4f(value: Vector4f, getter: DelegateGetter = DelegateGetter.passthrough(), setter: DelegateSetter = DelegateSetter.passthrough()): ListenableDelegate = RegularValue(value, Vector4fCodec, getter, setter) diff --git a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/matrix/Matrix2d.kt b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/matrix/Matrix2d.kt index 7cd1fdb..5764f18 100644 --- a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/matrix/Matrix2d.kt +++ b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/matrix/Matrix2d.kt @@ -2,7 +2,7 @@ package ru.dbotthepony.kommons.matrix import ru.dbotthepony.kommons.arrays.Double2DArray import ru.dbotthepony.kommons.arrays.mulMatrixComponents -import ru.dbotthepony.kommons.core.IStruct2d +import ru.dbotthepony.kommons.util.IStruct2d import ru.dbotthepony.kommons.vector.Vector2d import java.nio.ByteBuffer import java.nio.DoubleBuffer diff --git a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/matrix/Matrix2f.kt b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/matrix/Matrix2f.kt index 1eac46b..3d0b7b3 100644 --- a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/matrix/Matrix2f.kt +++ b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/matrix/Matrix2f.kt @@ -2,7 +2,7 @@ package ru.dbotthepony.kommons.matrix import ru.dbotthepony.kommons.arrays.Float2DArray import ru.dbotthepony.kommons.arrays.mulMatrixComponents -import ru.dbotthepony.kommons.core.IStruct2f +import ru.dbotthepony.kommons.util.IStruct2f import ru.dbotthepony.kommons.vector.Vector2f import java.nio.ByteBuffer import java.nio.FloatBuffer diff --git a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/matrix/Matrix3d.kt b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/matrix/Matrix3d.kt index 00f933e..d88d448 100644 --- a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/matrix/Matrix3d.kt +++ b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/matrix/Matrix3d.kt @@ -2,8 +2,8 @@ package ru.dbotthepony.kommons.matrix import ru.dbotthepony.kommons.arrays.Double2DArray import ru.dbotthepony.kommons.arrays.mulMatrixComponents -import ru.dbotthepony.kommons.core.IStruct2d -import ru.dbotthepony.kommons.core.IStruct3d +import ru.dbotthepony.kommons.util.IStruct2d +import ru.dbotthepony.kommons.util.IStruct3d import ru.dbotthepony.kommons.vector.Vector2d import ru.dbotthepony.kommons.vector.Vector3d import java.nio.ByteBuffer diff --git a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/matrix/Matrix3f.kt b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/matrix/Matrix3f.kt index da05b15..f32e59b 100644 --- a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/matrix/Matrix3f.kt +++ b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/matrix/Matrix3f.kt @@ -2,8 +2,8 @@ package ru.dbotthepony.kommons.matrix import ru.dbotthepony.kommons.arrays.Float2DArray import ru.dbotthepony.kommons.arrays.mulMatrixComponents -import ru.dbotthepony.kommons.core.IStruct2f -import ru.dbotthepony.kommons.core.IStruct3f +import ru.dbotthepony.kommons.util.IStruct2f +import ru.dbotthepony.kommons.util.IStruct3f import ru.dbotthepony.kommons.vector.Vector2f import ru.dbotthepony.kommons.vector.Vector3f import java.nio.ByteBuffer diff --git a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/matrix/Matrix4d.kt b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/matrix/Matrix4d.kt index 34ac787..fd49214 100644 --- a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/matrix/Matrix4d.kt +++ b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/matrix/Matrix4d.kt @@ -2,9 +2,9 @@ package ru.dbotthepony.kommons.matrix import ru.dbotthepony.kommons.arrays.Double2DArray import ru.dbotthepony.kommons.arrays.mulMatrixComponents -import ru.dbotthepony.kommons.core.IStruct2d -import ru.dbotthepony.kommons.core.IStruct3d -import ru.dbotthepony.kommons.core.IStruct4d +import ru.dbotthepony.kommons.util.IStruct2d +import ru.dbotthepony.kommons.util.IStruct3d +import ru.dbotthepony.kommons.util.IStruct4d import ru.dbotthepony.kommons.vector.Vector2d import ru.dbotthepony.kommons.vector.Vector3d import ru.dbotthepony.kommons.vector.Vector4d diff --git a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/matrix/Matrix4f.kt b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/matrix/Matrix4f.kt index 3f34f64..6f1d32e 100644 --- a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/matrix/Matrix4f.kt +++ b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/matrix/Matrix4f.kt @@ -2,9 +2,9 @@ package ru.dbotthepony.kommons.matrix import ru.dbotthepony.kommons.arrays.Float2DArray import ru.dbotthepony.kommons.arrays.mulMatrixComponents -import ru.dbotthepony.kommons.core.IStruct2f -import ru.dbotthepony.kommons.core.IStruct3f -import ru.dbotthepony.kommons.core.IStruct4f +import ru.dbotthepony.kommons.util.IStruct2f +import ru.dbotthepony.kommons.util.IStruct3f +import ru.dbotthepony.kommons.util.IStruct4f import ru.dbotthepony.kommons.vector.Vector2f import ru.dbotthepony.kommons.vector.Vector3f import ru.dbotthepony.kommons.vector.Vector4f diff --git a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/util/AABB.kt b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/util/AABB.kt index f5fb92d..6b74d0e 100644 --- a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/util/AABB.kt +++ b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/util/AABB.kt @@ -3,7 +3,6 @@ package ru.dbotthepony.kommons.util -import ru.dbotthepony.kommons.core.IStruct2d import ru.dbotthepony.kommons.math.intersectRectangles import ru.dbotthepony.kommons.math.rectangleContainsRectangle import ru.dbotthepony.kommons.vector.Vector2d diff --git a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/util/AABBi.kt b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/util/AABBi.kt index 5987ff4..ff8edbd 100644 --- a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/util/AABBi.kt +++ b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/util/AABBi.kt @@ -1,6 +1,5 @@ package ru.dbotthepony.kommons.util -import ru.dbotthepony.kommons.core.IStruct2i import ru.dbotthepony.kommons.math.intersectRectangles import ru.dbotthepony.kommons.math.rectangleContainsRectangle import ru.dbotthepony.kommons.vector.Vector2i diff --git a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector2d.kt b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector2d.kt index b388f71..a77a303 100644 --- a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector2d.kt +++ b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector2d.kt @@ -1,8 +1,8 @@ package ru.dbotthepony.kommons.vector -import ru.dbotthepony.kommons.core.IStruct2d -import ru.dbotthepony.kommons.core.IStruct2f -import ru.dbotthepony.kommons.core.IStruct2i +import ru.dbotthepony.kommons.util.IStruct2d +import ru.dbotthepony.kommons.util.IStruct2f +import ru.dbotthepony.kommons.util.IStruct2i import ru.dbotthepony.kommons.matrix.Matrix2d import ru.dbotthepony.kommons.matrix.Matrix3d import ru.dbotthepony.kommons.matrix.Matrix4d diff --git a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector2f.kt b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector2f.kt index 86490bb..afe580c 100644 --- a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector2f.kt +++ b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector2f.kt @@ -1,8 +1,8 @@ package ru.dbotthepony.kommons.vector -import ru.dbotthepony.kommons.core.IStruct2d -import ru.dbotthepony.kommons.core.IStruct2f -import ru.dbotthepony.kommons.core.IStruct2i +import ru.dbotthepony.kommons.util.IStruct2d +import ru.dbotthepony.kommons.util.IStruct2f +import ru.dbotthepony.kommons.util.IStruct2i import ru.dbotthepony.kommons.matrix.Matrix2f import ru.dbotthepony.kommons.matrix.Matrix3f import ru.dbotthepony.kommons.matrix.Matrix4f diff --git a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector2i.kt b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector2i.kt index 2236ae0..719191a 100644 --- a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector2i.kt +++ b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector2i.kt @@ -1,8 +1,8 @@ package ru.dbotthepony.kommons.vector -import ru.dbotthepony.kommons.core.IStruct2d -import ru.dbotthepony.kommons.core.IStruct2f -import ru.dbotthepony.kommons.core.IStruct2i +import ru.dbotthepony.kommons.util.IStruct2d +import ru.dbotthepony.kommons.util.IStruct2f +import ru.dbotthepony.kommons.util.IStruct2i import kotlin.math.sqrt data class Vector2i( diff --git a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector3d.kt b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector3d.kt index 271c0aa..7a5bb9c 100644 --- a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector3d.kt +++ b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector3d.kt @@ -1,11 +1,11 @@ package ru.dbotthepony.kommons.vector -import ru.dbotthepony.kommons.core.IStruct2d -import ru.dbotthepony.kommons.core.IStruct2f -import ru.dbotthepony.kommons.core.IStruct2i -import ru.dbotthepony.kommons.core.IStruct3d -import ru.dbotthepony.kommons.core.IStruct3f -import ru.dbotthepony.kommons.core.IStruct3i +import ru.dbotthepony.kommons.util.IStruct2d +import ru.dbotthepony.kommons.util.IStruct2f +import ru.dbotthepony.kommons.util.IStruct2i +import ru.dbotthepony.kommons.util.IStruct3d +import ru.dbotthepony.kommons.util.IStruct3f +import ru.dbotthepony.kommons.util.IStruct3i import ru.dbotthepony.kommons.matrix.Matrix3d import ru.dbotthepony.kommons.matrix.Matrix4d import ru.dbotthepony.kommons.matrix.Matrix4dStack diff --git a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector3f.kt b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector3f.kt index 121b1a1..0446933 100644 --- a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector3f.kt +++ b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector3f.kt @@ -1,11 +1,11 @@ package ru.dbotthepony.kommons.vector -import ru.dbotthepony.kommons.core.IStruct2d -import ru.dbotthepony.kommons.core.IStruct2f -import ru.dbotthepony.kommons.core.IStruct2i -import ru.dbotthepony.kommons.core.IStruct3d -import ru.dbotthepony.kommons.core.IStruct3f -import ru.dbotthepony.kommons.core.IStruct3i +import ru.dbotthepony.kommons.util.IStruct2d +import ru.dbotthepony.kommons.util.IStruct2f +import ru.dbotthepony.kommons.util.IStruct2i +import ru.dbotthepony.kommons.util.IStruct3d +import ru.dbotthepony.kommons.util.IStruct3f +import ru.dbotthepony.kommons.util.IStruct3i import ru.dbotthepony.kommons.matrix.Matrix3f import ru.dbotthepony.kommons.matrix.Matrix4f import ru.dbotthepony.kommons.matrix.Matrix4fStack diff --git a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector3i.kt b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector3i.kt index beb71ee..242f810 100644 --- a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector3i.kt +++ b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector3i.kt @@ -1,11 +1,11 @@ package ru.dbotthepony.kommons.vector -import ru.dbotthepony.kommons.core.IStruct2d -import ru.dbotthepony.kommons.core.IStruct2f -import ru.dbotthepony.kommons.core.IStruct2i -import ru.dbotthepony.kommons.core.IStruct3d -import ru.dbotthepony.kommons.core.IStruct3f -import ru.dbotthepony.kommons.core.IStruct3i +import ru.dbotthepony.kommons.util.IStruct2d +import ru.dbotthepony.kommons.util.IStruct2f +import ru.dbotthepony.kommons.util.IStruct2i +import ru.dbotthepony.kommons.util.IStruct3d +import ru.dbotthepony.kommons.util.IStruct3f +import ru.dbotthepony.kommons.util.IStruct3i import kotlin.math.sqrt data class Vector3i( diff --git a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector4d.kt b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector4d.kt index 0527877..6576d9b 100644 --- a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector4d.kt +++ b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector4d.kt @@ -1,15 +1,14 @@ package ru.dbotthepony.kommons.vector -import ru.dbotthepony.kommons.vector.Vector4f -import ru.dbotthepony.kommons.core.IStruct2d -import ru.dbotthepony.kommons.core.IStruct2f -import ru.dbotthepony.kommons.core.IStruct2i -import ru.dbotthepony.kommons.core.IStruct3d -import ru.dbotthepony.kommons.core.IStruct3f -import ru.dbotthepony.kommons.core.IStruct3i -import ru.dbotthepony.kommons.core.IStruct4d -import ru.dbotthepony.kommons.core.IStruct4f -import ru.dbotthepony.kommons.core.IStruct4i +import ru.dbotthepony.kommons.util.IStruct2d +import ru.dbotthepony.kommons.util.IStruct2f +import ru.dbotthepony.kommons.util.IStruct2i +import ru.dbotthepony.kommons.util.IStruct3d +import ru.dbotthepony.kommons.util.IStruct3f +import ru.dbotthepony.kommons.util.IStruct3i +import ru.dbotthepony.kommons.util.IStruct4d +import ru.dbotthepony.kommons.util.IStruct4f +import ru.dbotthepony.kommons.util.IStruct4i import ru.dbotthepony.kommons.matrix.Matrix4d import ru.dbotthepony.kommons.matrix.Matrix4dStack import kotlin.math.absoluteValue diff --git a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector4f.kt b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector4f.kt index d074f1b..e3b8b4a 100644 --- a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector4f.kt +++ b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector4f.kt @@ -1,14 +1,14 @@ package ru.dbotthepony.kommons.vector -import ru.dbotthepony.kommons.core.IStruct2d -import ru.dbotthepony.kommons.core.IStruct2f -import ru.dbotthepony.kommons.core.IStruct2i -import ru.dbotthepony.kommons.core.IStruct3d -import ru.dbotthepony.kommons.core.IStruct3f -import ru.dbotthepony.kommons.core.IStruct3i -import ru.dbotthepony.kommons.core.IStruct4d -import ru.dbotthepony.kommons.core.IStruct4f -import ru.dbotthepony.kommons.core.IStruct4i +import ru.dbotthepony.kommons.util.IStruct2d +import ru.dbotthepony.kommons.util.IStruct2f +import ru.dbotthepony.kommons.util.IStruct2i +import ru.dbotthepony.kommons.util.IStruct3d +import ru.dbotthepony.kommons.util.IStruct3f +import ru.dbotthepony.kommons.util.IStruct3i +import ru.dbotthepony.kommons.util.IStruct4d +import ru.dbotthepony.kommons.util.IStruct4f +import ru.dbotthepony.kommons.util.IStruct4i import ru.dbotthepony.kommons.matrix.Matrix4f import ru.dbotthepony.kommons.matrix.Matrix4fStack import kotlin.math.absoluteValue diff --git a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector4i.kt b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector4i.kt index 27fdfe6..70c3781 100644 --- a/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector4i.kt +++ b/linear-algebra/src/main/kotlin/ru/dbotthepony/kommons/vector/Vector4i.kt @@ -1,15 +1,14 @@ package ru.dbotthepony.kommons.vector -import ru.dbotthepony.kommons.vector.Vector4f -import ru.dbotthepony.kommons.core.IStruct2d -import ru.dbotthepony.kommons.core.IStruct2f -import ru.dbotthepony.kommons.core.IStruct2i -import ru.dbotthepony.kommons.core.IStruct3d -import ru.dbotthepony.kommons.core.IStruct3f -import ru.dbotthepony.kommons.core.IStruct3i -import ru.dbotthepony.kommons.core.IStruct4d -import ru.dbotthepony.kommons.core.IStruct4f -import ru.dbotthepony.kommons.core.IStruct4i +import ru.dbotthepony.kommons.util.IStruct2d +import ru.dbotthepony.kommons.util.IStruct2f +import ru.dbotthepony.kommons.util.IStruct2i +import ru.dbotthepony.kommons.util.IStruct3d +import ru.dbotthepony.kommons.util.IStruct3f +import ru.dbotthepony.kommons.util.IStruct3i +import ru.dbotthepony.kommons.util.IStruct4d +import ru.dbotthepony.kommons.util.IStruct4f +import ru.dbotthepony.kommons.util.IStruct4i import kotlin.math.sqrt data class Vector4i( diff --git a/src/main/kotlin/ru/dbotthepony/kommons/collect/CollectionUtils.kt b/src/main/kotlin/ru/dbotthepony/kommons/collect/CollectionUtils.kt index fda0424..0a4a4b4 100644 --- a/src/main/kotlin/ru/dbotthepony/kommons/collect/CollectionUtils.kt +++ b/src/main/kotlin/ru/dbotthepony/kommons/collect/CollectionUtils.kt @@ -1,6 +1,6 @@ package ru.dbotthepony.kommons.collect -import ru.dbotthepony.kommons.core.KOptional +import ru.dbotthepony.kommons.util.KOptional import java.lang.ref.Reference import java.util.* import java.util.concurrent.CompletableFuture diff --git a/src/main/kotlin/ru/dbotthepony/kommons/core/GetterSetter.kt b/src/main/kotlin/ru/dbotthepony/kommons/core/GetterSetter.kt deleted file mode 100644 index ebf43ae..0000000 --- a/src/main/kotlin/ru/dbotthepony/kommons/core/GetterSetter.kt +++ /dev/null @@ -1,149 +0,0 @@ -package ru.dbotthepony.kommons.core - -import ru.dbotthepony.kommons.event.ISubscriptable -import java.util.function.Consumer -import java.util.function.Supplier -import kotlin.properties.ReadWriteProperty -import kotlin.reflect.KMutableProperty0 -import kotlin.reflect.KProperty - -inline var GetterSetter.value: V - get() = get() - set(value) { accept(value) } - -interface GetterSetter : Supplier, Consumer, ReadWriteProperty { - override fun setValue(thisRef: Any?, property: KProperty<*>, value: V) { - accept(value) - } - - override fun getValue(thisRef: Any?, property: KProperty<*>): V { - return get() - } - - operator fun invoke(): V { - return get() - } - - operator fun invoke(value: V) { - accept(value) - } - - fun asGetterOnly(): GetterSetter { - val self = this - - return object : GetterSetter { - override fun get(): V { - return self.get() - } - - override fun accept(t: V) { - } - } - } - - fun watch(watch: (old: V, new: V) -> Unit): GetterSetter { - val self = this - - return object : GetterSetter by self { - override fun accept(t: V) { - val old = get() - self.accept(t) - watch.invoke(old, t) - } - } - } - - companion object { - fun of(getter: Supplier, setter: Consumer): GetterSetter { - return object : GetterSetter { - override fun get(): V { - return getter.get() - } - - override fun accept(t: V) { - setter.accept(t) - } - } - } - - fun of(getter: () -> V, setter: (V) -> Unit): GetterSetter { - return object : GetterSetter { - override fun get(): V { - return getter.invoke() - } - - override fun accept(t: V) { - setter.invoke(t) - } - } - } - - fun of(property: KMutableProperty0): GetterSetter { - return object : GetterSetter { - override fun get(): V { - return property.get() - } - - override fun accept(t: V) { - property.set(t) - } - } - } - - fun box(value: V): SentientGetterSetter { - return object : SentientGetterSetter { - private val subs = ISubscriptable.Impl() - - override fun addListener(listener: Consumer): ISubscriptable.L { - return subs.addListener(listener) - } - - private var value = value - - override fun get(): V { - return value - } - - override fun accept(t: V) { - this.value = t - subs.accept(t) - } - } - } - } -} - -interface SentientGetterSetter : GetterSetter, ISubscriptable { - override fun watch(watch: (old: V, new: V) -> Unit): SentientGetterSetter { - val self = this - - return object : SentientGetterSetter by self { - override fun accept(t: V) { - val old = get() - self.accept(t) - watch.invoke(old, t) - } - } - } - -} - -operator fun Supplier.getValue(thisRef: Any?, property: KProperty<*>): T { - return get() -} - -operator fun Consumer.setValue(thisRef: Any?, property: KProperty<*>, value: T) { - accept(value) -} - -fun KMutableProperty0.asGetterSetter(watch: ((old: V, new: V) -> Unit)? = null): GetterSetter { - return GetterSetter.of(this).let { - if (watch != null) { - it.watch(watch) - } else { - it - } - } -} - -fun KMutableProperty0.asGetterOnly() = GetterSetter.of(Supplier { this.get() }, Consumer { /* do nothing */ }) diff --git a/src/main/kotlin/ru/dbotthepony/kommons/event/IBooleanSubscriptable.kt b/src/main/kotlin/ru/dbotthepony/kommons/event/IBooleanSubscriptable.kt deleted file mode 100644 index cdaf114..0000000 --- a/src/main/kotlin/ru/dbotthepony/kommons/event/IBooleanSubscriptable.kt +++ /dev/null @@ -1,43 +0,0 @@ -package ru.dbotthepony.kommons.event - -import it.unimi.dsi.fastutil.booleans.BooleanConsumer -import java.util.function.Consumer - -interface IBooleanSubscriptable : ISubscriptable { - @Deprecated("Use type specific listener") - override fun addListener(listener: Consumer): ISubscriptable.L { - return addListener(listener::accept) - } - - fun addListener(listener: BooleanConsumer): ISubscriptable.L - - class Impl : IBooleanSubscriptable, Consumer, BooleanConsumer { - private inner class L(val callback: BooleanConsumer) : ISubscriptable.L { - private var isRemoved = false - - init { - subscribers.add(this) - } - - override fun remove() { - if (!isRemoved) { - isRemoved = true - queue.add(this) - } - } - } - - private val subscribers = ArrayList(0) - private val queue = ArrayList(0) - - override fun addListener(listener: BooleanConsumer): ISubscriptable.L { - return L(listener) - } - - override fun accept(t: Boolean) { - queue.forEach { subscribers.remove(it) } - queue.clear() - subscribers.forEach { it.callback.accept(t) } - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/ru/dbotthepony/kommons/event/IDoubleSubcriptable.kt b/src/main/kotlin/ru/dbotthepony/kommons/event/IDoubleSubcriptable.kt deleted file mode 100644 index 641b0a1..0000000 --- a/src/main/kotlin/ru/dbotthepony/kommons/event/IDoubleSubcriptable.kt +++ /dev/null @@ -1,43 +0,0 @@ -package ru.dbotthepony.kommons.event - -import java.util.function.Consumer -import java.util.function.DoubleConsumer - -interface IDoubleSubcriptable : ISubscriptable { - @Deprecated("Use type specific listener") - override fun addListener(listener: Consumer): ISubscriptable.L { - return addListener(DoubleConsumer { listener.accept(it) }) - } - - fun addListener(listener: DoubleConsumer): ISubscriptable.L - - class Impl : IDoubleSubcriptable, Consumer, DoubleConsumer { - private inner class L(val callback: DoubleConsumer) : ISubscriptable.L { - private var isRemoved = false - - init { - subscribers.add(this) - } - - override fun remove() { - if (!isRemoved) { - isRemoved = true - queue.add(this) - } - } - } - - private val subscribers = ArrayList(0) - private val queue = ArrayList(0) - - override fun addListener(listener: DoubleConsumer): ISubscriptable.L { - return L(listener) - } - - override fun accept(t: Double) { - queue.forEach { subscribers.remove(it) } - queue.clear() - subscribers.forEach { it.callback.accept(t) } - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/ru/dbotthepony/kommons/event/IFloatSubcriptable.kt b/src/main/kotlin/ru/dbotthepony/kommons/event/IFloatSubcriptable.kt deleted file mode 100644 index 412bd1a..0000000 --- a/src/main/kotlin/ru/dbotthepony/kommons/event/IFloatSubcriptable.kt +++ /dev/null @@ -1,43 +0,0 @@ -package ru.dbotthepony.kommons.event - -import it.unimi.dsi.fastutil.floats.FloatConsumer -import java.util.function.Consumer - -interface IFloatSubcriptable : ISubscriptable { - @Deprecated("Use type specific listener") - override fun addListener(listener: Consumer): ISubscriptable.L { - return addListener(listener::accept) - } - - fun addListener(listener: FloatConsumer): ISubscriptable.L - - class Impl : IFloatSubcriptable, Consumer, FloatConsumer { - private inner class L(val callback: FloatConsumer) : ISubscriptable.L { - private var isRemoved = false - - init { - subscribers.add(this) - } - - override fun remove() { - if (!isRemoved) { - isRemoved = true - queue.add(this) - } - } - } - - private val subscribers = ArrayList(0) - private val queue = ArrayList(0) - - override fun addListener(listener: FloatConsumer): ISubscriptable.L { - return L(listener) - } - - override fun accept(t: Float) { - queue.forEach { subscribers.remove(it) } - queue.clear() - subscribers.forEach { it.callback.accept(t) } - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/ru/dbotthepony/kommons/event/IIntSubcriptable.kt b/src/main/kotlin/ru/dbotthepony/kommons/event/IIntSubcriptable.kt deleted file mode 100644 index 8ddeecf..0000000 --- a/src/main/kotlin/ru/dbotthepony/kommons/event/IIntSubcriptable.kt +++ /dev/null @@ -1,43 +0,0 @@ -package ru.dbotthepony.kommons.event - -import java.util.function.Consumer -import java.util.function.IntConsumer - -interface IIntSubcriptable : ISubscriptable { - @Deprecated("Use type specific listener") - override fun addListener(listener: Consumer): ISubscriptable.L { - return addListener(IntConsumer { listener.accept(it) }) - } - - fun addListener(listener: IntConsumer): ISubscriptable.L - - class Impl : IIntSubcriptable, Consumer, IntConsumer { - private inner class L(val callback: IntConsumer) : ISubscriptable.L { - private var isRemoved = false - - init { - subscribers.add(this) - } - - override fun remove() { - if (!isRemoved) { - isRemoved = true - queue.add(this) - } - } - } - - private val subscribers = ArrayList(0) - private val queue = ArrayList(0) - - override fun addListener(listener: IntConsumer): ISubscriptable.L { - return L(listener) - } - - override fun accept(t: Int) { - queue.forEach { subscribers.remove(it) } - queue.clear() - subscribers.forEach { it.callback.accept(t) } - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/ru/dbotthepony/kommons/event/ILongSubcriptable.kt b/src/main/kotlin/ru/dbotthepony/kommons/event/ILongSubcriptable.kt deleted file mode 100644 index 8c41aa5..0000000 --- a/src/main/kotlin/ru/dbotthepony/kommons/event/ILongSubcriptable.kt +++ /dev/null @@ -1,43 +0,0 @@ -package ru.dbotthepony.kommons.event - -import java.util.function.Consumer -import java.util.function.LongConsumer - -interface ILongSubcriptable : ISubscriptable { - @Deprecated("Use type specific listener") - override fun addListener(listener: Consumer): ISubscriptable.L { - return addListener(LongConsumer { listener.accept(it) }) - } - - fun addListener(listener: LongConsumer): ISubscriptable.L - - class Impl : ILongSubcriptable, Consumer, LongConsumer { - private inner class L(val callback: LongConsumer) : ISubscriptable.L { - private var isRemoved = false - - init { - subscribers.add(this) - } - - override fun remove() { - if (!isRemoved) { - isRemoved = true - queue.add(this) - } - } - } - - private val subscribers = ArrayList(0) - private val queue = ArrayList(0) - - override fun addListener(listener: LongConsumer): ISubscriptable.L { - return L(listener) - } - - override fun accept(t: Long) { - queue.forEach { subscribers.remove(it) } - queue.clear() - subscribers.forEach { it.callback.accept(t) } - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/ru/dbotthepony/kommons/event/ISubscriptable.kt b/src/main/kotlin/ru/dbotthepony/kommons/event/ISubscriptable.kt deleted file mode 100644 index cbb4e14..0000000 --- a/src/main/kotlin/ru/dbotthepony/kommons/event/ISubscriptable.kt +++ /dev/null @@ -1,60 +0,0 @@ -package ru.dbotthepony.kommons.event - -import java.util.function.Consumer - -interface ISubscriptable { - /** - * Listener token, allows to remove listener from subscriber list - */ - fun interface L { - /** - * Removes this listener - */ - fun remove() - } - - fun addListener(listener: Consumer): L - - class Impl : ISubscriptable, Consumer { - private inner class L(val callback: Consumer) : ISubscriptable.L { - private var isRemoved = false - - init { - subscribers.add(this) - } - - override fun remove() { - if (!isRemoved) { - isRemoved = true - queue.add(this) - } - } - } - - private val subscribers = ArrayList(0) - private val queue = ArrayList(0) - - override fun addListener(listener: Consumer): ISubscriptable.L { - return L(listener) - } - - override fun accept(t: V) { - queue.forEach { subscribers.remove(it) } - queue.clear() - subscribers.forEach { it.callback.accept(t) } - } - } - - companion object : ISubscriptable, L { - @Suppress("unchecked_cast") - fun empty(): ISubscriptable { - return this as ISubscriptable - } - - override fun remove() {} - - override fun addListener(listener: Consumer): L { - return this - } - } -} diff --git a/src/main/kotlin/ru/dbotthepony/kommons/event/IUnitSubscriptable.kt b/src/main/kotlin/ru/dbotthepony/kommons/event/IUnitSubscriptable.kt deleted file mode 100644 index f6402e0..0000000 --- a/src/main/kotlin/ru/dbotthepony/kommons/event/IUnitSubscriptable.kt +++ /dev/null @@ -1,41 +0,0 @@ -package ru.dbotthepony.kommons.event - -import java.util.function.Consumer - -interface IUnitSubscriptable : ISubscriptable { - fun addListener(listener: Runnable): ISubscriptable.L - - override fun addListener(listener: Consumer): ISubscriptable.L { - return addListener(Runnable { listener.accept(Unit) }) - } - - class Impl : IUnitSubscriptable, Runnable { - private inner class L(val callback: Runnable) : ISubscriptable.L { - private var isRemoved = false - - init { - subscribers.add(this) - } - - override fun remove() { - if (!isRemoved) { - isRemoved = true - queue.add(this) - } - } - } - - private val subscribers = ArrayList(0) - private val queue = ArrayList(0) - - override fun addListener(listener: Runnable): ISubscriptable.L { - return L(listener) - } - - override fun run() { - queue.forEach { subscribers.remove(it) } - queue.clear() - subscribers.forEach { it.callback.run() } - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/ru/dbotthepony/kommons/networking/ChangesetAction.kt b/src/main/kotlin/ru/dbotthepony/kommons/io/ChangesetAction.kt similarity index 55% rename from src/main/kotlin/ru/dbotthepony/kommons/networking/ChangesetAction.kt rename to src/main/kotlin/ru/dbotthepony/kommons/io/ChangesetAction.kt index 341ce4e..20c3952 100644 --- a/src/main/kotlin/ru/dbotthepony/kommons/networking/ChangesetAction.kt +++ b/src/main/kotlin/ru/dbotthepony/kommons/io/ChangesetAction.kt @@ -1,4 +1,4 @@ -package ru.dbotthepony.kommons.networking +package ru.dbotthepony.kommons.io enum class ChangesetAction { CLEAR, ADD, REMOVE diff --git a/src/main/kotlin/ru/dbotthepony/kommons/io/CollectionStreamCodec.kt b/src/main/kotlin/ru/dbotthepony/kommons/io/CollectionStreamCodec.kt deleted file mode 100644 index 2cca67a..0000000 --- a/src/main/kotlin/ru/dbotthepony/kommons/io/CollectionStreamCodec.kt +++ /dev/null @@ -1,34 +0,0 @@ -package ru.dbotthepony.kommons.io - -import java.io.DataInputStream -import java.io.DataOutputStream - -class CollectionStreamCodec>(val elementCodec: IStreamCodec, val collectionFactory: (Int) -> C) : - IStreamCodec { - override fun read(stream: DataInputStream): C { - val size = stream.readVarInt() - - if (size <= 0) { - return collectionFactory.invoke(0) - } - - val collection = collectionFactory.invoke(size) - - for (i in 0 until size) { - collection.add(elementCodec.read(stream)) - } - - return collection - } - - override fun write(stream: DataOutputStream, value: C) { - stream.writeVarInt(value.size) - value.forEach { elementCodec.write(stream, it) } - } - - override fun copy(value: C): C { - val new = collectionFactory.invoke(value.size) - value.forEach { new.add(elementCodec.copy(it)) } - return new - } -} \ No newline at end of file diff --git a/src/main/kotlin/ru/dbotthepony/kommons/io/DecimalIO.kt b/src/main/kotlin/ru/dbotthepony/kommons/io/DecimalIO.kt index 0cca8a0..452cf82 100644 --- a/src/main/kotlin/ru/dbotthepony/kommons/io/DecimalIO.kt +++ b/src/main/kotlin/ru/dbotthepony/kommons/io/DecimalIO.kt @@ -20,4 +20,4 @@ fun OutputStream.writeDecimal(value: Decimal) { write(bytes) } -val DecimalValueCodec = StreamCodec(DataInputStream::readDecimal, DataOutputStream::writeDecimal) +val DecimalValueCodec = StreamCodec.Impl(DataInputStream::readDecimal, DataOutputStream::writeDecimal) diff --git a/src/main/kotlin/ru/dbotthepony/kommons/io/EnumValueCodec.kt b/src/main/kotlin/ru/dbotthepony/kommons/io/EnumValueCodec.kt deleted file mode 100644 index eae5883..0000000 --- a/src/main/kotlin/ru/dbotthepony/kommons/io/EnumValueCodec.kt +++ /dev/null @@ -1,48 +0,0 @@ -package ru.dbotthepony.kommons.io - -import java.io.DataInputStream -import java.io.DataOutputStream - -class EnumValueCodec>(clazz: Class) : IStreamCodec { - val clazz = searchClass(clazz) - val values: List = listOf(*this.clazz.enumConstants!!) - val valuesMap = values.associateBy { it.name } - - override fun read(stream: DataInputStream): V { - val id = stream.readVarInt() - return values.getOrNull(id) ?: throw NoSuchElementException("No such enum with index $id") - } - - override fun write(stream: DataOutputStream, value: V) { - stream.writeVarInt(value.ordinal) - } - - override fun copy(value: V): V { - return value - } - - override fun compare(a: V, b: V): Boolean { - return a === b - } - - companion object { - /** - * FIXME: enums with abstract methods which get compiled to subclasses, whose DO NOT expose "parent's" enum constants array - * - * is there an already existing solution? - */ - fun > searchClass(clazz: Class): Class { - var search: Class<*> = clazz - - while (search.enumConstants == null && search.superclass != null) { - search = search.superclass - } - - if (search.enumConstants == null) { - throw ClassCastException("$clazz does not represent an enum or enum subclass") - } - - return search as Class - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/ru/dbotthepony/kommons/io/IStreamCodec.kt b/src/main/kotlin/ru/dbotthepony/kommons/io/IStreamCodec.kt deleted file mode 100644 index c1b170a..0000000 --- a/src/main/kotlin/ru/dbotthepony/kommons/io/IStreamCodec.kt +++ /dev/null @@ -1,28 +0,0 @@ -package ru.dbotthepony.kommons.io - -import java.io.DataInputStream -import java.io.DataOutputStream - -/** - * Represents value which can be encoded onto or decoded from stream. - * - * Also provides [copy] and [compare] methods - */ -interface IStreamCodec { - fun read(stream: DataInputStream): V - fun write(stream: DataOutputStream, value: V) - - /** - * if value is immutable, return it right away - */ - fun copy(value: V): V - - /** - * Optional equality check override. Utilized to determine whenever e.g. network value is different from new value - * - * By default uses [Any.equals] - */ - fun compare(a: V, b: V): Boolean { - return a == b - } -} \ No newline at end of file diff --git a/src/main/kotlin/ru/dbotthepony/kommons/networking/MapChangeset.kt b/src/main/kotlin/ru/dbotthepony/kommons/io/MapChangeset.kt similarity index 93% rename from src/main/kotlin/ru/dbotthepony/kommons/networking/MapChangeset.kt rename to src/main/kotlin/ru/dbotthepony/kommons/io/MapChangeset.kt index acd2b2a..5d29eab 100644 --- a/src/main/kotlin/ru/dbotthepony/kommons/networking/MapChangeset.kt +++ b/src/main/kotlin/ru/dbotthepony/kommons/io/MapChangeset.kt @@ -1,4 +1,4 @@ -package ru.dbotthepony.kommons.networking +package ru.dbotthepony.kommons.io data class MapChangeset( val action: ChangesetAction, diff --git a/src/main/kotlin/ru/dbotthepony/kommons/io/OutputStreamUtils.kt b/src/main/kotlin/ru/dbotthepony/kommons/io/OutputStreamUtils.kt index 69d2294..9c9c826 100644 --- a/src/main/kotlin/ru/dbotthepony/kommons/io/OutputStreamUtils.kt +++ b/src/main/kotlin/ru/dbotthepony/kommons/io/OutputStreamUtils.kt @@ -1,23 +1,23 @@ package ru.dbotthepony.kommons.io -import ru.dbotthepony.kommons.core.IStruct2b -import ru.dbotthepony.kommons.core.IStruct2d -import ru.dbotthepony.kommons.core.IStruct2f -import ru.dbotthepony.kommons.core.IStruct2i -import ru.dbotthepony.kommons.core.IStruct2l -import ru.dbotthepony.kommons.core.IStruct2s -import ru.dbotthepony.kommons.core.IStruct3b -import ru.dbotthepony.kommons.core.IStruct3d -import ru.dbotthepony.kommons.core.IStruct3f -import ru.dbotthepony.kommons.core.IStruct3i -import ru.dbotthepony.kommons.core.IStruct3l -import ru.dbotthepony.kommons.core.IStruct3s -import ru.dbotthepony.kommons.core.IStruct4b -import ru.dbotthepony.kommons.core.IStruct4d -import ru.dbotthepony.kommons.core.IStruct4f -import ru.dbotthepony.kommons.core.IStruct4i -import ru.dbotthepony.kommons.core.IStruct4l -import ru.dbotthepony.kommons.core.IStruct4s +import ru.dbotthepony.kommons.util.IStruct2b +import ru.dbotthepony.kommons.util.IStruct2d +import ru.dbotthepony.kommons.util.IStruct2f +import ru.dbotthepony.kommons.util.IStruct2i +import ru.dbotthepony.kommons.util.IStruct2l +import ru.dbotthepony.kommons.util.IStruct2s +import ru.dbotthepony.kommons.util.IStruct3b +import ru.dbotthepony.kommons.util.IStruct3d +import ru.dbotthepony.kommons.util.IStruct3f +import ru.dbotthepony.kommons.util.IStruct3i +import ru.dbotthepony.kommons.util.IStruct3l +import ru.dbotthepony.kommons.util.IStruct3s +import ru.dbotthepony.kommons.util.IStruct4b +import ru.dbotthepony.kommons.util.IStruct4d +import ru.dbotthepony.kommons.util.IStruct4f +import ru.dbotthepony.kommons.util.IStruct4i +import ru.dbotthepony.kommons.util.IStruct4l +import ru.dbotthepony.kommons.util.IStruct4s import java.io.DataOutput import java.io.OutputStream import java.io.RandomAccessFile diff --git a/src/main/kotlin/ru/dbotthepony/kommons/networking/SetChangeset.kt b/src/main/kotlin/ru/dbotthepony/kommons/io/SetChangeset.kt similarity index 92% rename from src/main/kotlin/ru/dbotthepony/kommons/networking/SetChangeset.kt rename to src/main/kotlin/ru/dbotthepony/kommons/io/SetChangeset.kt index 1bb0d35..ded392e 100644 --- a/src/main/kotlin/ru/dbotthepony/kommons/networking/SetChangeset.kt +++ b/src/main/kotlin/ru/dbotthepony/kommons/io/SetChangeset.kt @@ -1,4 +1,4 @@ -package ru.dbotthepony.kommons.networking +package ru.dbotthepony.kommons.io data class SetChangeset( val action: ChangesetAction, diff --git a/src/main/kotlin/ru/dbotthepony/kommons/io/StreamCodec.kt b/src/main/kotlin/ru/dbotthepony/kommons/io/StreamCodec.kt new file mode 100644 index 0000000..3f9d042 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kommons/io/StreamCodec.kt @@ -0,0 +1,175 @@ +package ru.dbotthepony.kommons.io + +import java.io.DataInputStream +import java.io.DataOutputStream +import java.util.* +import kotlin.reflect.KClass + +/** + * Represents value which can be encoded onto or decoded from stream. + * + * Also provides [copy] and [compare] methods + */ +interface StreamCodec { + fun read(stream: DataInputStream): V + fun write(stream: DataOutputStream, value: V) + + /** + * defensive copy + */ + fun copy(value: V): V + + /** + * Optional equality check override. Utilized to determine whenever e.g. network value is different from new value + * + * By default uses [Any.equals] + */ + fun compare(a: V, b: V): Boolean { + return a == b + } + + class Impl( + private val reader: (stream: DataInputStream) -> V, + private val writer: (stream: DataOutputStream, value: V) -> Unit, + private val copier: ((value: V) -> V) = { it }, + private val comparator: ((a: V, b: V) -> Boolean) = { a, b -> a == b } + ) : StreamCodec { + constructor( + reader: (stream: DataInputStream) -> V, + payloadSize: Long, + writer: (stream: DataOutputStream, value: V) -> Unit, + copier: ((value: V) -> V) = { it }, + comparator: ((a: V, b: V) -> Boolean) = { a, b -> a == b } + ) : this({ stream -> reader.invoke(stream) }, writer, copier, comparator) + + val nullable = object : StreamCodec { + override fun read(stream: DataInputStream): V? { + return if (stream.read() == 0) null else reader.invoke(stream) + } + + override fun write(stream: DataOutputStream, value: V?) { + if (value === null) stream.write(0) else { + stream.write(1) + writer.invoke(stream, value) + } + } + + override fun copy(value: V?): V? { + return if (value === null) null else copier.invoke(value) + } + + override fun compare(a: V?, b: V?): Boolean { + if (a === null && b === null) return true + if (a === null || b === null) return false + return comparator.invoke(a, b) + } + } + + override fun read(stream: DataInputStream): V { + return reader.invoke(stream) + } + + override fun write(stream: DataOutputStream, value: V) { + writer.invoke(stream, value) + } + + override fun copy(value: V): V { + return copier.invoke(value) + } + + override fun compare(a: V, b: V): Boolean { + return comparator.invoke(a, b) + } + } + + class Collection>(val elementCodec: StreamCodec, val collectionFactory: (Int) -> C) : StreamCodec { + override fun read(stream: DataInputStream): C { + val size = stream.readVarInt() + + if (size <= 0) { + return collectionFactory.invoke(0) + } + + val collection = collectionFactory.invoke(size) + + for (i in 0 until size) { + collection.add(elementCodec.read(stream)) + } + + return collection + } + + override fun write(stream: DataOutputStream, value: C) { + stream.writeVarInt(value.size) + value.forEach { elementCodec.write(stream, it) } + } + + override fun copy(value: C): C { + val new = collectionFactory.invoke(value.size) + value.forEach { new.add(elementCodec.copy(it)) } + return new + } + } + + class Enum>(clazz: Class) : StreamCodec { + val clazz = searchClass(clazz) + val values: List = listOf(*this.clazz.enumConstants!!) + val valuesMap = values.associateBy { it.name } + + override fun read(stream: DataInputStream): V { + val id = stream.readVarInt() + return values.getOrNull(id) ?: throw NoSuchElementException("No such enum with index $id") + } + + override fun write(stream: DataOutputStream, value: V) { + stream.writeVarInt(value.ordinal) + } + + override fun copy(value: V): V { + return value + } + + override fun compare(a: V, b: V): Boolean { + return a === b + } + + companion object { + /** + * FIXME: enums with abstract methods which get compiled to subclasses, whose DO NOT expose "parent's" enum constants array + * + * is there an already existing solution? + */ + fun > searchClass(clazz: Class): Class { + var search: Class<*> = clazz + + while (search.enumConstants == null && search.superclass != null) { + search = search.superclass + } + + if (search.enumConstants == null) { + throw ClassCastException("$clazz does not represent an enum or enum subclass") + } + + return search as Class + } + } + } +} + +val NullValueCodec = StreamCodec.Impl({ _ -> null }, { _, _ -> }) +val BooleanValueCodec = StreamCodec.Impl(DataInputStream::readBoolean, 1L, DataOutputStream::writeBoolean) +val ByteValueCodec = StreamCodec.Impl(DataInputStream::readByte, 1L, { s, v -> s.writeByte(v.toInt()) }) +val ShortValueCodec = StreamCodec.Impl(DataInputStream::readShort, 2L, { s, v -> s.writeShort(v.toInt()) }) +val CharValueCodec = StreamCodec.Impl(DataInputStream::readChar, 2L, { s, v -> s.writeShort(v.code) }) +val IntValueCodec = StreamCodec.Impl(DataInputStream::readInt, 4L, DataOutputStream::writeInt) +val LongValueCodec = StreamCodec.Impl(DataInputStream::readLong, 8L, DataOutputStream::writeLong) +val FloatValueCodec = StreamCodec.Impl(DataInputStream::readFloat, 4L, DataOutputStream::writeFloat) +val DoubleValueCodec = StreamCodec.Impl(DataInputStream::readDouble, 8L, DataOutputStream::writeDouble) +val BigDecimalValueCodec = StreamCodec.Impl(DataInputStream::readBigDecimal, DataOutputStream::writeBigDecimal) +val UUIDValueCodec = StreamCodec.Impl({ s -> UUID(s.readLong(), s.readLong()) }, { s, v -> s.writeLong(v.mostSignificantBits); s.writeLong(v.leastSignificantBits) }) +val VarIntValueCodec = StreamCodec.Impl(DataInputStream::readSignedVarInt, DataOutputStream::writeSignedVarInt) +val VarLongValueCodec = StreamCodec.Impl(DataInputStream::readSignedVarLong, DataOutputStream::writeSignedVarLong) +val BinaryStringCodec = StreamCodec.Impl(DataInputStream::readBinaryString, DataOutputStream::writeBinaryString) + +fun > Class.codec() = StreamCodec.Enum(this) +fun > KClass.codec() = StreamCodec.Enum(this.java) diff --git a/src/main/kotlin/ru/dbotthepony/kommons/io/StreamCodecs.kt b/src/main/kotlin/ru/dbotthepony/kommons/io/StreamCodecs.kt deleted file mode 100644 index 7f56857..0000000 --- a/src/main/kotlin/ru/dbotthepony/kommons/io/StreamCodecs.kt +++ /dev/null @@ -1,77 +0,0 @@ -package ru.dbotthepony.kommons.io - -import java.io.DataInputStream -import java.io.DataOutputStream -import java.util.* -import kotlin.reflect.KClass - -class StreamCodec( - private val reader: (stream: DataInputStream) -> V, - private val writer: (stream: DataOutputStream, value: V) -> Unit, - private val copier: ((value: V) -> V) = { it }, - private val comparator: ((a: V, b: V) -> Boolean) = { a, b -> a == b } -) : IStreamCodec { - constructor( - reader: (stream: DataInputStream) -> V, - payloadSize: Long, - writer: (stream: DataOutputStream, value: V) -> Unit, - copier: ((value: V) -> V) = { it }, - comparator: ((a: V, b: V) -> Boolean) = { a, b -> a == b } - ) : this({ stream -> reader.invoke(stream) }, writer, copier, comparator) - - val nullable = object : IStreamCodec { - override fun read(stream: DataInputStream): V? { - return if (stream.read() == 0) null else reader.invoke(stream) - } - - override fun write(stream: DataOutputStream, value: V?) { - if (value === null) stream.write(0) else { - stream.write(1) - writer.invoke(stream, value) - } - } - - override fun copy(value: V?): V? { - return if (value === null) null else copier.invoke(value) - } - - override fun compare(a: V?, b: V?): Boolean { - if (a === null && b === null) return true - if (a === null || b === null) return false - return comparator.invoke(a, b) - } - } - - override fun read(stream: DataInputStream): V { - return reader.invoke(stream) - } - - override fun write(stream: DataOutputStream, value: V) { - writer.invoke(stream, value) - } - - override fun copy(value: V): V { - return copier.invoke(value) - } - - override fun compare(a: V, b: V): Boolean { - return comparator.invoke(a, b) - } -} - -val NullValueCodec = StreamCodec({ _ -> null }, { _, _ -> }) -val BooleanValueCodec = StreamCodec(DataInputStream::readBoolean, 1L, DataOutputStream::writeBoolean) { a, b -> a == b } -val ByteValueCodec = StreamCodec(DataInputStream::readByte, 1L, { s, v -> s.writeByte(v.toInt()) }) { a, b -> a == b } -val ShortValueCodec = StreamCodec(DataInputStream::readShort, 2L, { s, v -> s.writeShort(v.toInt()) }) { a, b -> a == b } -val IntValueCodec = StreamCodec(DataInputStream::readInt, 4L, DataOutputStream::writeInt) { a, b -> a == b } -val LongValueCodec = StreamCodec(DataInputStream::readLong, 8L, DataOutputStream::writeLong) { a, b -> a == b } -val FloatValueCodec = StreamCodec(DataInputStream::readFloat, 4L, DataOutputStream::writeFloat) { a, b -> a == b } -val DoubleValueCodec = StreamCodec(DataInputStream::readDouble, 8L, DataOutputStream::writeDouble) { a, b -> a == b } -val BigDecimalValueCodec = StreamCodec(DataInputStream::readBigDecimal, DataOutputStream::writeBigDecimal) -val UUIDValueCodec = StreamCodec({ s -> UUID(s.readLong(), s.readLong()) }, { s, v -> s.writeLong(v.mostSignificantBits); s.writeLong(v.leastSignificantBits) }) -val VarIntValueCodec = StreamCodec(DataInputStream::readSignedVarInt, DataOutputStream::writeSignedVarInt) { a, b -> a == b } -val VarLongValueCodec = StreamCodec(DataInputStream::readSignedVarLong, DataOutputStream::writeSignedVarLong) { a, b -> a == b } -val BinaryStringCodec = StreamCodec(DataInputStream::readBinaryString, DataOutputStream::writeBinaryString) - -fun > Class.codec() = EnumValueCodec(this) -fun > KClass.codec() = EnumValueCodec(this.java) diff --git a/src/main/kotlin/ru/dbotthepony/kommons/io/SynchedDelegates.kt b/src/main/kotlin/ru/dbotthepony/kommons/io/SynchedDelegates.kt new file mode 100644 index 0000000..9e4ff4c --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kommons/io/SynchedDelegates.kt @@ -0,0 +1,945 @@ +@file:Suppress("DeprecatedCallableAddReplaceWith") + +package ru.dbotthepony.kommons.io + +import it.unimi.dsi.fastutil.objects.Reference2ObjectFunction +import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap +import it.unimi.dsi.fastutil.objects.ReferenceArraySet +import ru.dbotthepony.kommons.collect.ProxiedMap +import ru.dbotthepony.kommons.collect.forValidRefs +import ru.dbotthepony.kommons.util.Observer +import ru.dbotthepony.kommons.util.ListenableDelegate +import ru.dbotthepony.kommons.util.DelegateGetter +import ru.dbotthepony.kommons.util.DelegateSetter +import ru.dbotthepony.kommons.util.Listenable +import java.io.ByteArrayOutputStream +import java.io.DataInputStream +import java.io.DataOutputStream +import java.io.InputStream +import java.lang.ref.WeakReference +import java.util.* +import java.util.function.Consumer +import java.util.function.Supplier + +/** + * Universal, one-to-many value synchronizer, allowing to synchronize values from server to client + * anywhere, where input/output streams are supported + */ +@Suppress("unused", "BlockingMethodInNonBlockingContext") +class SynchedDelegates(private val callback: Runnable, private val alwaysCallCallback: Boolean) { + constructor() : this(Runnable {}, false) + constructor(callback: Runnable) : this(callback, false) + + private var freeSlots = 0 + // почему не удалять поля напрямую? + // чтоб не возникло проблем в состоянии гонки + // формируем пакет -> удаляем поле по обе стороны -> клиент принимает пакет -> клиент считывает неверные данные + // конечно, всё равно всё сломается если было удалено поле, которое находится в пакете + // но если поля нет в пакете, то всё окей + private val fields = ArrayList(0) + private val observers = ArrayList(0) + + private var nextFieldID = 0 + + val hasObservers: Boolean get() = observers.isNotEmpty() + + var isEmpty: Boolean = true + private set + + val isNotEmpty: Boolean get() = !isEmpty + + var isDirty: Boolean = false + private set(value) { + if (value != field) { + field = value + + if (value && !alwaysCallCallback) { + callback.run() + } + } + + if (alwaysCallCallback && value) { + callback.run() + } + } + + fun markClean() { + isDirty = false + } + + private var endpointsMaxCapacity = 1 + private val endpoints = ArrayList>(1) + val defaultEndpoint = Endpoint() + + private var lastEndpointCleanup = System.nanoTime() + + private fun notifyEndpoints(dirtyField: AbstractValue) { + isDirty = true + + forEachEndpoint { + it.addDirty(dirtyField) + } + } + + private inline fun forEachEndpoint(execute: (Endpoint) -> Unit) { + synchronized(endpoints) { + endpoints.forValidRefs { execute.invoke(it) } + + if (endpoints.size < endpointsMaxCapacity / 2) { + endpoints.trimToSize() + endpointsMaxCapacity = endpoints.size + } + } + } + + inner class Endpoint { + init { + synchronized(endpoints) { + endpoints.add(WeakReference(this)) + endpointsMaxCapacity = endpointsMaxCapacity.coerceAtLeast(endpoints.size) + + if (System.nanoTime() - lastEndpointCleanup <= 60) { + lastEndpointCleanup = System.nanoTime() + + val iterator = endpoints.listIterator() + + for (value in iterator) { + if (value.get() == null) { + iterator.remove() + } + } + + if (endpoints.size < endpointsMaxCapacity / 2) { + endpoints.trimToSize() + endpointsMaxCapacity = endpoints.size + } + } + } + } + + private val dirty = ReferenceArraySet(4) + + // use LinkedList because it is ensured memory is freed on LinkedList#clear + private val mapBacklogs = Reference2ObjectOpenHashMap, LinkedList Unit>>>() + private val setBacklogs = Reference2ObjectOpenHashMap, LinkedList Unit>>>() + + var unused: Boolean = false + private set + + fun markUnused() { + require(this === defaultEndpoint) { "This is not a default endpoint" } + if (unused) return + unused = true + mapBacklogs.clear() + dirty.clear() + + val iterator = endpoints.listIterator() + + for (value in iterator) { + if (value.get() === this) { + iterator.remove() + } + } + } + + init { + markDirty() + } + + fun markDirty() { + for (field in fields) { + field?.markDirty(this) + } + } + + internal fun addDirty(field: AbstractValue) { + if (unused) { + return + } + + dirty.add(field) + } + + internal fun removeDirty(field: AbstractValue) { + dirty.remove(field) + } + + internal fun getMapBacklog(map: Map): LinkedList Unit>> { + if (unused) { + return LinkedList() + } + + return mapBacklogs.computeIfAbsent(map, Reference2ObjectFunction { + LinkedList() + }) + } + + internal fun removeMapBacklog(map: Map) { + mapBacklogs.remove(map) + } + + internal fun getSetBacklog(set: Set): LinkedList Unit>> { + if (unused) { + return LinkedList() + } + + return setBacklogs.computeIfAbsent(set, Reference2ObjectFunction { + LinkedList() + }) + } + + internal fun removeSetBacklog(set: Set) { + setBacklogs.remove(set) + } + + fun collectNetworkPayload(): ByteArrayOutputStream? { + if (unused || dirty.isEmpty()) { + return null + } + + val stream = ByteArrayOutputStream() + val dataStream = DataOutputStream(stream) + + for (field in dirty) { + stream.writeVarInt(field.id) + field.write(dataStream, this) + } + + dirty.clear() + stream.write(0) + + return stream + } + } + + private val boundEndpoints = WeakHashMap() + + fun computeEndpointFor(obj: Any): Endpoint { + return boundEndpoints.computeIfAbsent(obj) { Endpoint() } + } + + fun removeEndpointFor(obj: Any): Endpoint? { + return boundEndpoints.remove(obj) + } + + fun endpointFor(obj: Any): Endpoint? { + return boundEndpoints[obj] + } + + abstract inner class AbstractValue : Observer { + val id: Int + + init { + if (freeSlots > 0) { + var found = -1 + + for (i in fields.indices) { + if (fields[i] == null) { + fields[i] = this + found = i + 1 + freeSlots-- + break + } + } + + if (found == -1) { + throw RuntimeException("freeSlots = $freeSlots but no null entries in field list!") + } else { + id = found + } + } else { + fields.add(this) + id = fields.size + isEmpty = false + } + } + + var isRemoved = false + private set + + protected var isDirty = false + protected var isRemote = false + + open fun remove() { + if (isRemoved) + return + + isRemoved = true + freeSlots++ + fields[id - 1] = null + observers.remove(this) + isEmpty = fields.all { it == null } + + while (fields[fields.size - 1] == null) { + fields.removeAt(fields.size - 1) + freeSlots-- + } + + forEachEndpoint { + it.removeDirty(this) + } + } + + open fun markDirty(endpoint: Endpoint) { + check(!isRemoved) { "Field was removed" } + endpoint.addDirty(this) + } + + open fun markDirty() { + check(!isRemoved) { "Field was removed" } + notifyEndpoints(this) + isDirty = true + } + + abstract fun write(stream: DataOutputStream, endpoint: Endpoint) + abstract fun read(stream: DataInputStream) + } + + inner class RegularValue( + value: T, + private val codec: StreamCodec, + getter: DelegateGetter = DelegateGetter.passthrough(), + setter: DelegateSetter = DelegateSetter.passthrough() + ) : AbstractValue(), ListenableDelegate by ListenableDelegate.maskSmart(value, getter, setter) { + init { + addListener(Runnable { + if (!isDirty && !isRemote) { + notifyEndpoints(this) + isDirty = true + } + }) + } + + override fun observe(): Boolean { + return false + } + + override fun write(stream: DataOutputStream, endpoint: Endpoint) { + codec.write(stream, get()) + isDirty = false + } + + override fun read(stream: DataInputStream) { + isRemote = true + accept(codec.read(stream)) + isDirty = false + } + } + + inner class ObservedValue( + value: T, + private val codec: StreamCodec, + getter: DelegateGetter = DelegateGetter.passthrough(), + setter: DelegateSetter = DelegateSetter.passthrough() + ) : AbstractValue(), ListenableDelegate by ListenableDelegate.maskSmart(value, getter, setter) { + private var observed = codec.copy(value) + + init { + observers.add(this) + + addListener(Runnable { + if (!isDirty && !isRemote) { + notifyEndpoints(this) + isDirty = true + } + }) + } + + override fun observe(): Boolean { + if (!isRemote) { + val get = get() + + if (!codec.compare(get, observed)) { + observed = codec.copy(get) + isDirty = true + } + } + + return isDirty + } + + override fun write(stream: DataOutputStream, endpoint: Endpoint) { + codec.write(stream, get()) + isDirty = false + } + + override fun read(stream: DataInputStream) { + isRemote = true + accept(codec.read(stream)) + isDirty = false + } + } + + inner class ComputedValue(getter: Supplier, private val codec: StreamCodec) : AbstractValue(), ListenableDelegate { + private val shadow = ListenableDelegate.SmartShadow(getter, codec::compare, codec::copy) + + override fun get(): T { + return shadow.get() + } + + override fun accept(t: T) { + shadow.accept(t) + } + + override fun addListener(listener: Consumer): Listenable.L { + return shadow.addListener(listener) + } + + override fun observe(): Boolean { + return shadow.observe() + } + + init { + shadow.addListener(Runnable { + if (!isDirty && !isRemote) { + notifyEndpoints(this) + isDirty = true + } + }) + } + + override fun write(stream: DataOutputStream, endpoint: Endpoint) { + codec.write(stream, shadow.get()) + isDirty = false + } + + override fun read(stream: DataInputStream) { + isRemote = true + shadow.accept(codec.read(stream)) + isDirty = false + } + } + + @JvmName("vbyte") fun byte(value: Byte = 0, getter: DelegateGetter = DelegateGetter.passthrough(), setter: DelegateSetter = DelegateSetter.passthrough()): ListenableDelegate = RegularValue(value, ByteValueCodec, getter, setter) + @JvmName("vshort") fun short(value: Short = 0, getter: DelegateGetter = DelegateGetter.passthrough(), setter: DelegateSetter = DelegateSetter.passthrough()): ListenableDelegate = RegularValue(value, ShortValueCodec, getter, setter) + @JvmName("vchar") fun char(value: Char = 0.toChar(), getter: DelegateGetter = DelegateGetter.passthrough(), setter: DelegateSetter = DelegateSetter.passthrough()): ListenableDelegate = RegularValue(value, CharValueCodec, getter, setter) + @JvmName("vint") fun int(value: Int = 0, getter: DelegateGetter = DelegateGetter.passthrough(), setter: DelegateSetter = DelegateSetter.passthrough()): ListenableDelegate = RegularValue(value, IntValueCodec, getter, setter) + @JvmName("vlong") fun long(value: Long = 0L, getter: DelegateGetter = DelegateGetter.passthrough(), setter: DelegateSetter = DelegateSetter.passthrough()): ListenableDelegate = RegularValue(value, LongValueCodec, getter, setter) + @JvmName("vfloat") fun float(value: Float = 0f, getter: DelegateGetter = DelegateGetter.passthrough(), setter: DelegateSetter = DelegateSetter.passthrough()): ListenableDelegate = RegularValue(value, FloatValueCodec, getter, setter) + @JvmName("vdouble") fun double(value: Double = 0.0, getter: DelegateGetter = DelegateGetter.passthrough(), setter: DelegateSetter = DelegateSetter.passthrough()): ListenableDelegate = RegularValue(value, DoubleValueCodec, getter, setter) + @JvmName("vboolean") fun boolean(value: Boolean = false, getter: DelegateGetter = DelegateGetter.passthrough(), setter: DelegateSetter = DelegateSetter.passthrough()): ListenableDelegate = RegularValue(value, BooleanValueCodec, getter, setter) + + fun string(value: String, getter: DelegateGetter = DelegateGetter.passthrough(), setter: DelegateSetter = DelegateSetter.passthrough()): ListenableDelegate = RegularValue(value, BinaryStringCodec, getter, setter) + fun uuid(value: UUID, getter: DelegateGetter = DelegateGetter.passthrough(), setter: DelegateSetter = DelegateSetter.passthrough()): ListenableDelegate = RegularValue(value, UUIDValueCodec, getter, setter) + + inner class Set( + private val codec: StreamCodec, + private val backingSet: MutableSet, + private val callback: ((changes: Collection>) -> Unit)? = null, + ) : AbstractValue() { + private fun pushBacklog(element: E, action: (DataOutputStream) -> Unit) { + check(!isRemote) { "Field marked as remote" } + + val pair = element to action + + forEachEndpoint { + val list = it.getSetBacklog(this) + val iterator = list.listIterator() + + for (value in iterator) { + if (value.first == element) { + iterator.remove() + } + } + + list.addLast(pair) + } + } + + override fun observe(): Boolean { + return false + } + + override fun remove() { + if (!isRemoved) { + forEachEndpoint { it.removeSetBacklog(this) } + } + + super.remove() + } + + override fun markDirty() { + check(!isRemoved) { "Field was removed" } + + if (isRemote || endpoints.isEmpty()) + return + + isDirty = true + + val endpoints = LinkedList Unit>>>() + + forEachEndpoint { + endpoints.add(it.getSetBacklog(this)) + it.addDirty(this) + } + + endpoints.forEach { + it.clear() + it.add(null to ClearBacklogEntry) + } + + for (value in backingSet) { + val valueCopy = codec.copy(value) + val pair: Pair Unit> = valueCopy to { it.write(ChangesetAction.ADD.ordinal + 1); codec.write(it, valueCopy) } + + endpoints.forEach { + it.add(pair) + } + } + } + + override fun markDirty(endpoint: Endpoint) { + super.markDirty(endpoint) + + endpoint.getSetBacklog(this).let { + it.clear() + it.add(null to ClearBacklogEntry) + + for (value in backingSet) { + val valueCopy = codec.copy(value) + it.add(valueCopy to { it.write(ChangesetAction.ADD.ordinal + 1); codec.write(it, valueCopy) }) + } + } + } + + val value: MutableSet = object : MutableSet { + override fun add(element: E): Boolean { + if (backingSet.add(element)) { + if (!isRemote) { + markDirty() + + val copy = codec.copy(element) + + pushBacklog(element) { + it.write(ChangesetAction.ADD.ordinal + 1) + codec.write(it, copy) + } + } + + return true + } + + return false + } + + override fun addAll(elements: Collection): Boolean { + var any = false + elements.forEach { any = add(it) || any } + return any + } + + override fun clear() { + if (backingSet.isNotEmpty()) { + backingSet.clear() + + if (!isRemote) { + markDirty() + + forEachEndpoint { + it.getSetBacklog(this@Set).let { + it.clear() + it.add(null to ClearBacklogEntry) + } + } + } + } + } + + override fun iterator(): MutableIterator { + return object : MutableIterator { + private val parent = backingSet.iterator() + private var lastElement: Any? = Mark + + override fun hasNext(): Boolean { + return parent.hasNext() + } + + override fun next(): E { + return parent.next().also { lastElement = it } + } + + override fun remove() { + parent.remove() + val lastElement = lastElement + + if (lastElement !== Mark) { + this.lastElement = Mark + + if (!isRemote) { + markDirty() + + @Suppress("unchecked_cast") + pushBacklog(lastElement as E) { + it.write(ChangesetAction.REMOVE.ordinal + 1) + codec.write(it, lastElement as E) + } + } + } + } + } + } + + override fun remove(element: E): Boolean { + if (backingSet.remove(element)) { + if (!isRemote) { + markDirty() + + val copy = codec.copy(element) + + pushBacklog(element) { + it.write(ChangesetAction.REMOVE.ordinal + 1) + codec.write(it, copy) + } + } + + return true + } + + return false + } + + override fun removeAll(elements: Collection): Boolean { + var any = false + elements.forEach { any = remove(it) || any } + return any + } + + override fun retainAll(elements: Collection): Boolean { + var any = false + + val iterator = iterator() + + for (value in iterator) { + if (value !in elements) { + any = true + iterator.remove() + } + } + + return any + } + + override val size: Int + get() = backingSet.size + + override fun contains(element: E): Boolean { + return element in backingSet + } + + override fun containsAll(elements: Collection): Boolean { + return backingSet.containsAll(elements) + } + + override fun isEmpty(): Boolean { + return backingSet.isEmpty() + } + } + + override fun write(stream: DataOutputStream, endpoint: Endpoint) { + val list = endpoint.getSetBacklog(this) + + for (value in list) { + value.second.invoke(stream) + } + + stream.write(0) + list.clear() + isDirty = false + } + + override fun read(stream: DataInputStream) { + if (!isRemote) { + isRemote = true + forEachEndpoint { it.removeSetBacklog(this) } + } + + isDirty = false + + var action = stream.read() + val changeset = LinkedList>() + + while (action != 0) { + if ((action - 1) !in ChangesetActionList.indices) { + throw IllegalArgumentException("Unknown changeset action with index ${action - 1}") + } + + when (ChangesetActionList[action - 1]) { + ChangesetAction.CLEAR -> { + changeset.add(SetChangeset(ChangesetAction.CLEAR, null)) + backingSet.clear() + } + + ChangesetAction.ADD -> { + val read = codec.read(stream) + changeset.add(SetChangeset(ChangesetAction.ADD, read)) + backingSet.add(read) + } + + ChangesetAction.REMOVE -> { + val read = codec.read(stream) + changeset.add(SetChangeset(ChangesetAction.REMOVE, read)) + backingSet.remove(read) + } + } + + action = stream.read() + } + + callback?.invoke(changeset) + } + } + + inner class Map( + private val keyCodec: StreamCodec, + private val valueCodec: StreamCodec, + private val backingMap: MutableMap, + private val callback: ((changes: Collection>) -> Unit)? = null, + ) : AbstractValue() { + private var sentAllValues = false + + private fun pushBacklog(key: Any?, value: (DataOutputStream) -> Unit) { + val pair = key to value + + forEachEndpoint { + val list = it.getMapBacklog(this) + val iterator = list.listIterator() + + for (e in iterator) { + if (e.first == key) { + iterator.remove() + } + } + + list.addLast(pair) + } + } + + override fun observe(): Boolean { + return false + } + + override fun markDirty() { + check(!isRemoved) { "Field was removed" } + + if (isRemote || endpoints.isEmpty()) + return + + isDirty = true + val backlogs = LinkedList Unit>>>() + + forEachEndpoint { + it.addDirty(this) + val value = it.getMapBacklog(this) + backlogs.add(value) + value.clear() + value.add(null to ClearBacklogEntry) + } + + for ((key, value) in backingMap) { + val valueCopy = valueCodec.copy(value) + + val action = key to { it: DataOutputStream -> + it.write(ChangesetAction.ADD.ordinal + 1) + keyCodec.write(it, key) + valueCodec.write(it, valueCopy) + } + + for (backlog in backlogs) { + backlog.add(action) + } + } + } + + override fun markDirty(endpoint: Endpoint) { + check(!isRemoved) { "Field was removed" } + + if (isRemote) + return + + val backlog = endpoint.getMapBacklog(this) + + backlog.clear() + backlog.add(null to ClearBacklogEntry) + + for ((key, value) in backingMap) { + val valueCopy = valueCodec.copy(value) + + backlog.add(key to { + it.write(ChangesetAction.ADD.ordinal + 1) + keyCodec.write(it, key) + valueCodec.write(it, valueCopy) + }) + } + } + + private fun lmarkDirty() { + if (!isDirty) { + notifyEndpoints(this@Map) + isDirty = true + } + } + + val value: MutableMap = object : ProxiedMap(backingMap) { + override fun onClear() { + if (isRemote || endpoints.isEmpty()) + return + + forEachEndpoint { endpoint -> + endpoint.getMapBacklog(this@Map).let { + it.clear() + it.add(null to ClearBacklogEntry) + } + } + + lmarkDirty() + this@SynchedDelegates.isDirty = true + } + + override fun onValueAdded(key: K, value: V) { + if (isRemote || endpoints.isEmpty()) + return + + val keyCopy = keyCodec.copy(key) + val valueCopy = valueCodec.copy(value) + + pushBacklog(key) { + it.write(ChangesetAction.ADD.ordinal + 1) + keyCodec.write(it, keyCopy) + valueCodec.write(it, valueCopy) + } + + lmarkDirty() + } + + override fun onValueRemoved(key: K, value: V) { + if (isRemote || endpoints.isEmpty()) + return + + val keyCopy = keyCodec.copy(key) + + pushBacklog(key) { + it.write(ChangesetAction.REMOVE.ordinal + 1) + keyCodec.write(it, keyCopy) + } + + lmarkDirty() + } + } + + override fun write(stream: DataOutputStream, endpoint: Endpoint) { + sentAllValues = false + isDirty = false + + val iterator = endpoint.getMapBacklog(this).listIterator() + + for (entry in iterator) { + entry.second.invoke(stream) + iterator.remove() + } + + stream.write(0) + } + + override fun read(stream: DataInputStream) { + if (!isRemote) { + isRemote = true + forEachEndpoint { it.removeMapBacklog(this) } + } + + isDirty = false + + val changeset = LinkedList>() + var readAction = stream.read() - 1 + + while (readAction != -1) { + if (readAction >= ChangesetActionList.size) { + throw IndexOutOfBoundsException("Unknown map action with ID $readAction") + } + + when (ChangesetActionList[readAction]) { + ChangesetAction.CLEAR -> { + backingMap.clear() + changeset.add(ClearMapChangeset) + } + + ChangesetAction.ADD -> { + val key = keyCodec.read(stream) + val value = valueCodec.read(stream) + backingMap[key] = value + changeset.add(MapChangeset(ChangesetAction.ADD, key, value)) + } + + ChangesetAction.REMOVE -> { + val key = keyCodec.read(stream) + backingMap.remove(key) + changeset.add(MapChangeset(ChangesetAction.REMOVE, key, null)) + } + } + + readAction = stream.read() - 1 + } + + if (changeset.size != 0) { + callback?.invoke(changeset) + } + } + } + + /** + * marks all fields dirty, invalidates mappings for each endpoint + */ + fun invalidate() { + for (field in fields) { + field?.markDirty() + } + } + + /** + * Observe changes of all fields with backing computation lambda + */ + fun observe(): Boolean { + var changes = false + + if (observers.isNotEmpty()) { + for (field in observers) { + changes = field.observe() || changes + } + } + + return changes + } + + /** + * [defaultEndpoint]#collectNetworkPayload + */ + fun collectNetworkPayload(): ByteArrayOutputStream? { + check(!defaultEndpoint.unused) { "Default endpoint is not used" } + observe() + val values = defaultEndpoint.collectNetworkPayload() + markClean() + return values + } + + fun read(stream: InputStream): Int { + var fieldId = stream.readVarInt() + var i = 0 + val dataStream = DataInputStream(stream) + + while (fieldId != 0) { + val field = fields.getOrNull(fieldId - 1) ?: throw IllegalArgumentException("Unknown field ID ${fieldId - 1}") + field.read(dataStream) + fieldId = stream.readVarInt() + i++ + } + + return i + } + + private object Mark + + companion object { + private val ClearBacklogEntry = { stream: DataOutputStream -> stream.write(ChangesetAction.CLEAR.ordinal + 1) } + private val ChangesetActionList = ChangesetAction.values() + private val ClearMapChangeset = MapChangeset(ChangesetAction.CLEAR, null, null) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kommons/math/Intersect.kt b/src/main/kotlin/ru/dbotthepony/kommons/math/Intersect.kt index 2efa0fb..a4eb0a7 100644 --- a/src/main/kotlin/ru/dbotthepony/kommons/math/Intersect.kt +++ b/src/main/kotlin/ru/dbotthepony/kommons/math/Intersect.kt @@ -3,10 +3,10 @@ package ru.dbotthepony.kommons.math -import ru.dbotthepony.kommons.core.IStruct2d -import ru.dbotthepony.kommons.core.IStruct2f -import ru.dbotthepony.kommons.core.IStruct2i -import ru.dbotthepony.kommons.core.IStruct2l +import ru.dbotthepony.kommons.util.IStruct2d +import ru.dbotthepony.kommons.util.IStruct2f +import ru.dbotthepony.kommons.util.IStruct2i +import ru.dbotthepony.kommons.util.IStruct2l private inline fun span(x0: Float, x1: Float): Float { return x1 - x0 diff --git a/src/main/kotlin/ru/dbotthepony/kommons/math/RGBAColor.kt b/src/main/kotlin/ru/dbotthepony/kommons/math/RGBAColor.kt index 02fbef9..e61fc91 100644 --- a/src/main/kotlin/ru/dbotthepony/kommons/math/RGBAColor.kt +++ b/src/main/kotlin/ru/dbotthepony/kommons/math/RGBAColor.kt @@ -1,6 +1,6 @@ package ru.dbotthepony.kommons.math -import ru.dbotthepony.kommons.core.IStruct4f +import ru.dbotthepony.kommons.util.IStruct4f import kotlin.math.roundToInt private fun hex(value: Int): String { diff --git a/src/main/kotlin/ru/dbotthepony/kommons/networking/FieldAccess.kt b/src/main/kotlin/ru/dbotthepony/kommons/networking/FieldAccess.kt deleted file mode 100644 index a653bef..0000000 --- a/src/main/kotlin/ru/dbotthepony/kommons/networking/FieldAccess.kt +++ /dev/null @@ -1,41 +0,0 @@ -package ru.dbotthepony.kommons.networking - -interface FieldAccess { - fun read(): V - fun write(value: V) -} - -interface FloatFieldAccess : FieldAccess { - override fun write(value: Float) - @Deprecated("Use type specific method", replaceWith = ReplaceWith("this.readFloat()")) - override fun read() = readFloat() - fun readFloat(): Float -} - -interface DoubleFieldAccess : FieldAccess { - override fun write(value: Double) - @Deprecated("Use type specific method", replaceWith = ReplaceWith("this.readDouble()")) - override fun read() = readDouble() - fun readDouble(): Double -} - -interface IntFieldAccess : FieldAccess { - override fun write(value: Int) - @Deprecated("Use type specific method", replaceWith = ReplaceWith("this.readInt()")) - override fun read() = readInt() - fun readInt(): Int -} - -interface LongFieldAccess : FieldAccess { - override fun write(value: Long) - @Deprecated("Use type specific method", replaceWith = ReplaceWith("this.readLong()")) - override fun read() = readLong() - fun readLong(): Long -} - -interface BooleanFieldAccess : FieldAccess { - override fun write(value: Boolean) - @Deprecated("Use type specific method", replaceWith = ReplaceWith("this.readBoolean()")) - override fun read() = readBoolean() - fun readBoolean(): Boolean -} diff --git a/src/main/kotlin/ru/dbotthepony/kommons/networking/FieldGetter.kt b/src/main/kotlin/ru/dbotthepony/kommons/networking/FieldGetter.kt deleted file mode 100644 index 36ee42d..0000000 --- a/src/main/kotlin/ru/dbotthepony/kommons/networking/FieldGetter.kt +++ /dev/null @@ -1,50 +0,0 @@ -package ru.dbotthepony.kommons.networking - -fun interface FieldGetter { - fun invoke(field: FieldAccess): V -} - -fun interface FloatFieldGetter : FieldGetter { - fun invoke(field: FloatFieldAccess): Float - - @Deprecated("Use type specific invoke") - override fun invoke(field: FieldAccess): Float { - return invoke(field as FloatFieldAccess) - } -} - -fun interface DoubleFieldGetter : FieldGetter { - fun invoke(field: DoubleFieldAccess): Double - - @Deprecated("Use type specific invoke") - override fun invoke(field: FieldAccess): Double { - return invoke(field as DoubleFieldAccess) - } -} - -fun interface IntFieldGetter : FieldGetter { - fun invoke(field: IntFieldAccess): Int - - @Deprecated("Use type specific invoke") - override fun invoke(field: FieldAccess): Int { - return invoke(field as IntFieldAccess) - } -} - -fun interface LongFieldGetter : FieldGetter { - fun invoke(field: LongFieldAccess): Long - - @Deprecated("Use type specific invoke") - override fun invoke(field: FieldAccess): Long { - return invoke(field as LongFieldAccess) - } -} - -fun interface BooleanFieldGetter : FieldGetter { - fun invoke(field: BooleanFieldAccess): Boolean - - @Deprecated("Use type specific invoke") - override fun invoke(field: FieldAccess): Boolean { - return invoke(field as BooleanFieldAccess) - } -} diff --git a/src/main/kotlin/ru/dbotthepony/kommons/networking/FieldSetter.kt b/src/main/kotlin/ru/dbotthepony/kommons/networking/FieldSetter.kt deleted file mode 100644 index 4c4b690..0000000 --- a/src/main/kotlin/ru/dbotthepony/kommons/networking/FieldSetter.kt +++ /dev/null @@ -1,50 +0,0 @@ -package ru.dbotthepony.kommons.networking - -fun interface FieldSetter { - fun invoke(value: V, access: FieldAccess, setByRemote: Boolean) -} - -fun interface FloatFieldSetter : FieldSetter { - fun invoke(value: Float, access: FloatFieldAccess, setByRemote: Boolean) - - @Deprecated("Use type specific invoke") - override fun invoke(value: Float, access: FieldAccess, setByRemote: Boolean) { - invoke(value, access as FloatFieldAccess, setByRemote) - } -} - -fun interface DoubleFieldSetter : FieldSetter { - fun invoke(value: Double, access: DoubleFieldAccess, setByRemote: Boolean) - - @Deprecated("Use type specific invoke") - override fun invoke(value: Double, access: FieldAccess, setByRemote: Boolean) { - invoke(value, access as DoubleFieldAccess, setByRemote) - } -} - -fun interface IntFieldSetter : FieldSetter { - fun invoke(value: Int, access: IntFieldAccess, setByRemote: Boolean) - - @Deprecated("Use type specific invoke") - override fun invoke(value: Int, access: FieldAccess, setByRemote: Boolean) { - invoke(value, access as IntFieldAccess, setByRemote) - } -} - -fun interface LongFieldSetter : FieldSetter { - fun invoke(value: Long, access: LongFieldAccess, setByRemote: Boolean) - - @Deprecated("Use type specific invoke") - override fun invoke(value: Long, access: FieldAccess, setByRemote: Boolean) { - invoke(value, access as LongFieldAccess, setByRemote) - } -} - -fun interface BooleanFieldSetter : FieldSetter { - fun invoke(value: Boolean, access: BooleanFieldAccess, setByRemote: Boolean) - - @Deprecated("Use type specific invoke") - override fun invoke(value: Boolean, access: FieldAccess, setByRemote: Boolean) { - invoke(value, access as BooleanFieldAccess, setByRemote) - } -} diff --git a/src/main/kotlin/ru/dbotthepony/kommons/networking/FieldSynchronizer.kt b/src/main/kotlin/ru/dbotthepony/kommons/networking/FieldSynchronizer.kt deleted file mode 100644 index e0766ed..0000000 --- a/src/main/kotlin/ru/dbotthepony/kommons/networking/FieldSynchronizer.kt +++ /dev/null @@ -1,2175 +0,0 @@ -@file:Suppress("DeprecatedCallableAddReplaceWith") - -package ru.dbotthepony.kommons.networking - -import it.unimi.dsi.fastutil.booleans.BooleanConsumer -import it.unimi.dsi.fastutil.floats.FloatConsumer -import it.unimi.dsi.fastutil.objects.Reference2ObjectFunction -import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap -import it.unimi.dsi.fastutil.objects.ReferenceArraySet -import ru.dbotthepony.kommons.collect.ProxiedMap -import ru.dbotthepony.kommons.collect.forValidRefs -import ru.dbotthepony.kommons.event.IBooleanSubscriptable -import ru.dbotthepony.kommons.event.IDoubleSubcriptable -import ru.dbotthepony.kommons.event.IFloatSubcriptable -import ru.dbotthepony.kommons.event.IIntSubcriptable -import ru.dbotthepony.kommons.event.ILongSubcriptable -import ru.dbotthepony.kommons.event.ISubscriptable -import ru.dbotthepony.kommons.io.BigDecimalValueCodec -import ru.dbotthepony.kommons.io.BinaryStringCodec -import ru.dbotthepony.kommons.io.ByteValueCodec -import ru.dbotthepony.kommons.io.DecimalValueCodec -import ru.dbotthepony.kommons.io.EnumValueCodec -import ru.dbotthepony.kommons.io.IStreamCodec -import ru.dbotthepony.kommons.io.ShortValueCodec -import ru.dbotthepony.kommons.io.UUIDValueCodec -import ru.dbotthepony.kommons.io.readSignedVarInt -import ru.dbotthepony.kommons.io.readSignedVarLong -import ru.dbotthepony.kommons.io.readVarInt -import ru.dbotthepony.kommons.io.readVarLong -import ru.dbotthepony.kommons.io.writeSignedVarInt -import ru.dbotthepony.kommons.io.writeSignedVarLong -import ru.dbotthepony.kommons.io.writeVarInt -import ru.dbotthepony.kommons.io.writeVarLong -import ru.dbotthepony.kommons.math.Decimal -import ru.dbotthepony.kommons.util.FloatSupplier -import java.io.ByteArrayOutputStream -import java.io.DataInputStream -import java.io.DataOutputStream -import java.io.InputStream -import java.lang.ref.WeakReference -import java.math.BigDecimal -import java.util.* -import java.util.function.BooleanSupplier -import java.util.function.Consumer -import java.util.function.DoubleConsumer -import java.util.function.DoubleSupplier -import java.util.function.IntConsumer -import java.util.function.IntSupplier -import java.util.function.LongConsumer -import java.util.function.LongSupplier -import java.util.function.Supplier -import kotlin.reflect.KMutableProperty0 -import kotlin.reflect.KProperty -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", "BlockingMethodInNonBlockingContext") -class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCallback: Boolean) { - constructor() : this(Runnable {}, false) - constructor(callback: Runnable) : this(callback, false) - - private var freeSlots = 0 - // почему не удалять поля напрямую? - // чтоб не возникло проблем в состоянии гонки - // формируем пакет -> удаляем поле по обе стороны -> клиент принимает пакет -> клиент считывает неверные данные - // конечно, всё равно всё сломается если было удалено поле, которое находится в пакете - // но если поля нет в пакете, то всё окей - private val fields = ArrayList?>(0) - private val observers = ArrayList>(0) - - private var nextFieldID = 0 - - val hasObservers: Boolean get() = observers.isNotEmpty() - - var isEmpty: Boolean = true - private set - - val isNotEmpty: Boolean get() = !isEmpty - - var isDirty: Boolean = false - private set(value) { - if (value != field) { - field = value - - if (value && !alwaysCallCallback) { - callback.run() - } - } - - if (alwaysCallCallback && value) { - callback.run() - } - } - - fun markClean() { - isDirty = false - } - - fun computedByte(getter: () -> Byte) = ComputedField(getter, ByteValueCodec) - fun computedBool(getter: BooleanSupplier) = ComputedBooleanField(getter) - fun computedShort(getter: () -> Short) = ComputedField(getter, ShortValueCodec) - fun computedLong(getter: LongSupplier) = ComputedLongField(getter) - fun computedFixedLong(getter: LongSupplier) = ComputedFixedLongField(getter) - fun computedFloat(getter: FloatSupplier) = ComputedFloatField(getter) - fun computedDouble(getter: DoubleSupplier) = ComputedDoubleField(getter) - fun computedUuid(getter: () -> UUID) = ComputedField(getter, UUIDValueCodec) - fun computedInt(getter: IntSupplier) = ComputedIntField(getter) - fun computedFixedInt(getter: IntSupplier) = ComputedFixedIntField(getter) - fun computedDecimal(getter: () -> Decimal) = ComputedField(getter, DecimalValueCodec) - fun computedBigDecimal(getter: () -> BigDecimal) = ComputedField(getter, BigDecimalValueCodec) - fun computedString(getter: () -> String) = ComputedField(getter, BinaryStringCodec) - - fun computedByte(getter: KProperty0) = ComputedField(getter, ByteValueCodec) - fun computedBool(getter: KProperty0) = ComputedBooleanField(getter::get) - fun computedShort(getter: KProperty0) = ComputedField(getter, ShortValueCodec) - fun computedLong(getter: KProperty0) = ComputedLongField(getter::get) - fun computedFixedLong(getter: KProperty0) = ComputedFixedLongField(getter::get) - fun computedFloat(getter: KProperty0) = ComputedFloatField(getter::get) - fun computedDouble(getter: KProperty0) = ComputedDoubleField(getter::get) - fun computedUuid(getter: KProperty0) = ComputedField(getter, UUIDValueCodec) - fun computedInt(getter: KProperty0) = ComputedIntField(getter::get) - fun computedFixedInt(getter: KProperty0) = ComputedFixedIntField(getter::get) - fun computedDecimal(getter: KProperty0) = ComputedField(getter, DecimalValueCodec) - fun computedBigDecimal(getter: KProperty0) = ComputedField(getter, BigDecimalValueCodec) - fun computedString(getter: KProperty0) = ComputedField(getter, BinaryStringCodec) - - fun computedByte(getter: Supplier) = ComputedField(getter::get, ByteValueCodec) - fun computedBool(getter: Supplier) = ComputedBooleanField(getter::get) - fun computedShort(getter: Supplier) = ComputedField(getter::get, ShortValueCodec) - fun computedLong(getter: Supplier) = ComputedLongField(getter::get) - fun computedFixedLong(getter: Supplier) = ComputedFixedLongField(getter::get) - fun computedFloat(getter: Supplier) = ComputedFloatField(getter::get) - fun computedDouble(getter: Supplier) = ComputedDoubleField(getter::get) - fun computedUuid(getter: Supplier) = ComputedField(getter::get, UUIDValueCodec) - fun computedInt(getter: Supplier) = ComputedIntField(getter::get) - fun computedFixedInt(getter: Supplier) = ComputedFixedIntField(getter::get) - fun computedDecimal(getter: Supplier) = ComputedField(getter::get, DecimalValueCodec) - fun computedBigDecimal(getter: Supplier) = ComputedField(getter::get, BigDecimalValueCodec) - fun computedString(getter: Supplier) = ComputedField(getter::get, BinaryStringCodec) - - fun > computedEnum(type: Class, getter: () -> T) = ComputedField(getter, EnumValueCodec(type)) - inline fun > computedEnum(noinline getter: () -> T) = ComputedField(getter, EnumValueCodec(T::class.java)) - - fun > computedEnum(type: Class, getter: KProperty0) = ComputedField(getter, EnumValueCodec(type)) - inline fun > computedEnum(getter: KProperty0) = ComputedField(getter, EnumValueCodec(T::class.java)) - - fun > computedEnum(type: Class, getter: Supplier) = ComputedField(getter::get, EnumValueCodec(type)) - inline fun > computedEnum(getter: Supplier) = ComputedField(getter::get, EnumValueCodec(T::class.java)) - - @JvmOverloads - fun byte( - value: Byte = 0, - getter: FieldGetter? = null, - setter: FieldSetter? = null, - ): Field { - return Field(value, ByteValueCodec, getter, setter) - } - - @JvmOverloads - fun bool( - value: Boolean = false, - getter: BooleanFieldGetter? = null, - setter: BooleanFieldSetter? = null, - ): BooleanField { - return BooleanField(value, getter, setter) - } - - @JvmOverloads - fun short( - value: Short = 0, - getter: FieldGetter? = null, - setter: FieldSetter? = null, - ): Field { - return Field(value, ShortValueCodec, getter, setter) - } - - @JvmOverloads - fun long( - value: Long = 0L, - getter: LongFieldGetter? = null, - setter: LongFieldSetter? = null, - ): LongField { - return LongField(value, getter, setter) - } - - @JvmOverloads - fun fixedLong( - value: Long = 0L, - getter: LongFieldGetter? = null, - setter: LongFieldSetter? = null, - ): FixedLongField { - return FixedLongField(value, getter, setter) - } - - @JvmOverloads - fun float( - value: Float = 0f, - getter: FloatFieldGetter? = null, - setter: FloatFieldSetter? = null, - ): FloatField { - return FloatField(value, getter, setter) - } - - @JvmOverloads - fun double( - value: Double = 0.0, - getter: DoubleFieldGetter? = null, - setter: DoubleFieldSetter? = null, - ): DoubleField { - return DoubleField(value, getter, setter) - } - - @JvmOverloads - fun uuid( - value: UUID = UUID(0L, 0L), - getter: FieldGetter? = null, - setter: FieldSetter? = null, - ): Field { - return Field(value, UUIDValueCodec, getter, setter) - } - - @JvmOverloads - fun int( - value: Int = 0, - getter: IntFieldGetter? = null, - setter: IntFieldSetter? = null, - ): IntField { - return IntField(value, getter, setter) - } - - @JvmOverloads - fun string( - value: String = "", - getter: FieldGetter? = null, - setter: FieldSetter? = null, - ): Field { - return Field(value, BinaryStringCodec, getter, setter) - } - - @JvmOverloads - fun fixedInt( - value: Int = 0, - getter: IntFieldGetter? = null, - setter: IntFieldSetter? = null, - ): FixedIntField { - return FixedIntField(value, getter, setter) - } - - @JvmOverloads - fun decimal( - value: Decimal = Decimal.ZERO, - getter: FieldGetter? = null, - setter: FieldSetter? = null, - ): Field { - return Field(value, DecimalValueCodec, getter, setter) - } - - @JvmOverloads - fun bigDecimal( - value: BigDecimal = BigDecimal.ZERO, - getter: FieldGetter? = null, - setter: FieldSetter? = null, - ): Field { - return Field(value, BigDecimalValueCodec, getter, setter) - } - - @JvmOverloads - fun > enum( - type: Class, - value: T = type.enumConstants[0], - getter: FieldGetter? = null, - setter: FieldSetter? = null, - ): Field { - return Field(value, EnumValueCodec(type), getter, setter) - } - - @JvmOverloads - fun > enum( - value: T, - getter: FieldGetter? = null, - setter: FieldSetter? = null, - ): Field { - return Field(value, EnumValueCodec(value::class.java), getter, setter) - } - - private var endpointsMaxCapacity = 1 - private val endpoints = ArrayList>(1) - val defaultEndpoint = Endpoint() - - private var lastEndpointCleanup = System.nanoTime() - - private fun notifyEndpoints(dirtyField: AbstractField<*>) { - isDirty = true - - forEachEndpoint { - it.addDirtyField(dirtyField) - } - } - - private inline fun forEachEndpoint(execute: (Endpoint) -> Unit) { - synchronized(endpoints) { - endpoints.forValidRefs { execute.invoke(it) } - - if (endpoints.size < endpointsMaxCapacity / 2) { - endpoints.trimToSize() - endpointsMaxCapacity = endpoints.size - } - } - } - - inner class Endpoint { - init { - synchronized(endpoints) { - endpoints.add(WeakReference(this)) - endpointsMaxCapacity = endpointsMaxCapacity.coerceAtLeast(endpoints.size) - - if (System.nanoTime() - lastEndpointCleanup <= 60) { - lastEndpointCleanup = System.nanoTime() - - val iterator = endpoints.listIterator() - - for (value in iterator) { - if (value.get() == null) { - iterator.remove() - } - } - - if (endpoints.size < endpointsMaxCapacity / 2) { - endpoints.trimToSize() - endpointsMaxCapacity = endpoints.size - } - } - } - } - - private val dirtyFields = ReferenceArraySet>(4) - - // use LinkedList because it is ensured memory is freed on LinkedList#clear - private val mapBacklogs = Reference2ObjectOpenHashMap, LinkedList Unit>>>() - private val setBacklogs = Reference2ObjectOpenHashMap, LinkedList Unit>>>() - - var unused: Boolean = false - private set - - fun markUnused() { - require(this === defaultEndpoint) { "This is not a default endpoint" } - if (unused) return - unused = true - mapBacklogs.clear() - dirtyFields.clear() - - val iterator = endpoints.listIterator() - - for (value in iterator) { - if (value.get() === this) { - iterator.remove() - } - } - } - - init { - markDirty() - } - - fun markDirty() { - for (field in fields) { - field?.markDirty(this) - } - } - - internal fun addDirtyField(field: AbstractField<*>) { - if (unused) { - return - } - - dirtyFields.add(field) - } - - internal fun removeDirtyField(field: AbstractField<*>) { - dirtyFields.remove(field) - } - - internal fun getMapBacklog(map: Map): LinkedList Unit>> { - if (unused) { - return LinkedList() - } - - return mapBacklogs.computeIfAbsent(map, Reference2ObjectFunction { - LinkedList() - }) - } - - internal fun removeMapBacklog(map: Map) { - mapBacklogs.remove(map) - } - - internal fun getSetBacklog(set: Set): LinkedList Unit>> { - if (unused) { - return LinkedList() - } - - return setBacklogs.computeIfAbsent(set, Reference2ObjectFunction { - LinkedList() - }) - } - - internal fun removeSetBacklog(set: Set) { - setBacklogs.remove(set) - } - - fun collectNetworkPayload(): ByteArrayOutputStream? { - if (unused || dirtyFields.isEmpty()) { - return null - } - - val stream = ByteArrayOutputStream() - val dataStream = DataOutputStream(stream) - - for (field in dirtyFields) { - stream.writeVarInt(field.id) - field.write(dataStream, this) - } - - dirtyFields.clear() - stream.write(0) - - return stream - } - } - - private val boundEndpoints = WeakHashMap() - - fun computeEndpointFor(obj: Any): Endpoint { - return boundEndpoints.computeIfAbsent(obj) { Endpoint() } - } - - fun removeEndpointFor(obj: Any): Endpoint? { - return boundEndpoints.remove(obj) - } - - fun endpointFor(obj: Any): Endpoint? { - return boundEndpoints[obj] - } - - @Suppress("LeakingThis") - abstract inner class AbstractField : IField { - val id: Int - - init { - if (freeSlots > 0) { - var found = -1 - - for (i in fields.indices) { - if (fields[i] == null) { - fields[i] = this - found = i + 1 - freeSlots-- - break - } - } - - if (found == -1) { - throw RuntimeException("freeSlots = $freeSlots but no null entries in field list!") - } else { - id = found - } - } else { - fields.add(this) - id = fields.size - isEmpty = false - } - } - - final override var isRemoved = false - private set - - protected var isDirty = false - - override fun remove() { - if (isRemoved) - return - - isRemoved = true - freeSlots++ - fields[id - 1] = null - observers.remove(this) - isEmpty = fields.all { it == null } - - while (fields[fields.size - 1] == null) { - fields.removeAt(fields.size - 1) - freeSlots-- - } - - forEachEndpoint { - it.removeDirtyField(this) - } - } - - override fun markDirty(endpoint: Endpoint) { - check(!isRemoved) { "Field was removed" } - endpoint.addDirtyField(this) - } - - override fun markDirty() { - check(!isRemoved) { "Field was removed" } - notifyEndpoints(this@AbstractField) - isDirty = true - } - } - - /** - * Networked variable with backing field holding immutable value - */ - inner class Field( - private var field: V, - private val codec: IStreamCodec, - private val getter: FieldGetter? = null, - private val setter: FieldSetter? = null, - isObserver: Boolean = false, - ) : AbstractField(), IMutableField { - private var remote: V = codec.copy(field) - private val subs = ISubscriptable.Impl() - - override fun addListener(listener: Consumer): ISubscriptable.L { - return subs.addListener(listener) - } - - init { - if (isObserver) { - observers.add(this) - } - } - - private val access = object : FieldAccess { - override fun read(): V { - return field - } - - override fun write(value: V) { - if (!isDirty && !codec.compare(remote, value)) { - notifyEndpoints(this@Field) - isDirty = true - } - - this@Field.field = value - subs.accept(value) - } - } - - override fun observe(): Boolean { - check(!isRemoved) { "Field was removed" } - - if (!isDirty && !codec.compare(remote, field)) { - notifyEndpoints(this@Field) - isDirty = true - } - - return isDirty - } - - override var value: V - get() { - val getter = this.getter - - if (getter != null) { - return getter.invoke(access) - } - - return this.field - } - set(value) { - val setter = this.setter - - if (setter != null) { - setter.invoke(value, access, false) - } else { - if (!isDirty && !codec.compare(remote, value)) { - notifyEndpoints(this@Field) - isDirty = true - } - - this.field = value - subs.accept(value) - } - } - - override fun write(stream: DataOutputStream, endpoint: Endpoint) { - check(!isRemoved) { "Field was removed" } - codec.write(stream, field) - isDirty = false - remote = codec.copy(field) - } - - override fun read(stream: DataInputStream) { - check(!isRemoved) { "Field was removed" } - val value = codec.read(stream) - val setter = this.setter - - if (setter != null) { - setter.invoke(value, access, true) - } else { - this.field = value - subs.accept(value) - } - } - } - - abstract inner class PrimitiveField : AbstractField() { - override fun observe(): Boolean { - check(!isRemoved) { "Field was removed" } - return isDirty - } - } - - /** - * Type specific field, storing primitive [Float] directly - */ - inner class FloatField(field: Float, private val getter: FloatFieldGetter? = null, private val setter: FloatFieldSetter? = null) : PrimitiveField(), IMutableFloatField { - private val subs = IFloatSubcriptable.Impl() - - override fun addListener(listener: FloatConsumer): ISubscriptable.L { - return subs.addListener(listener) - } - - private var field = field - set(value) { - if (field != value) { - field = value - subs.accept(value) - } - } - - override val property = object : IMutableFloatProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): Float { - return this@FloatField.float - } - - override fun setValue(thisRef: Any?, property: KProperty<*>, value: Float) { - this@FloatField.float = value - } - } - - private val access = object : FloatFieldAccess { - override fun readFloat(): Float { - return this@FloatField.field - } - - override fun write(value: Float) { - if (!isDirty && value != this@FloatField.field) { - notifyEndpoints(this@FloatField) - isDirty = true - } - - this@FloatField.field = value - } - } - - override var float: Float - get() { - return getter?.invoke(access) ?: this.field - } - set(value) { - val setter = this.setter - - if (setter != null) { - setter.invoke(value, access, false) - } else if (value != this.field) { - if (!isDirty) { - notifyEndpoints(this) - isDirty = true - } - - this.field = value - } - } - - override fun write(stream: DataOutputStream, endpoint: Endpoint) { - check(!isRemoved) { "Field was removed" } - stream.writeFloat(this.field) - isDirty = false - } - - override fun read(stream: DataInputStream) { - check(!isRemoved) { "Field was removed" } - val value = stream.readFloat() - val setter = this.setter - - if (setter != null) { - setter.invoke(value, access, true) - } else { - this.field = value - } - } - } - - /** - * Type specific field, storing primitive [Double] directly - */ - inner class DoubleField(field: Double, private val getter: DoubleFieldGetter? = null, private val setter: DoubleFieldSetter? = null) : PrimitiveField(), IMutableDoubleField { - private val subs = IDoubleSubcriptable.Impl() - - override fun addListener(listener: DoubleConsumer): ISubscriptable.L { - return subs.addListener(listener) - } - - private var field = field - set(value) { - if (field != value) { - field = value - subs.accept(value) - } - } - - override val property = object : IMutableDoubleProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): Double { - return this@DoubleField.double - } - - override fun setValue(thisRef: Any?, property: KProperty<*>, value: Double) { - this@DoubleField.double = value - } - } - - private val access = object : DoubleFieldAccess { - override fun readDouble(): Double { - return this@DoubleField.field - } - - override fun write(value: Double) { - if (!isDirty && value != this@DoubleField.field) { - notifyEndpoints(this@DoubleField) - isDirty = true - } - - this@DoubleField.field = value - } - } - - override var double: Double - get() { - return getter?.invoke(access) ?: this.field - } - set(value) { - val setter = this.setter - - if (setter != null) { - setter.invoke(value, access, false) - } else if (value != this.field) { - if (!isDirty) { - notifyEndpoints(this) - isDirty = true - } - - this.field = value - } - } - - override fun write(stream: DataOutputStream, endpoint: Endpoint) { - check(!isRemoved) { "Field was removed" } - stream.writeDouble(this.field) - isDirty = false - } - - override fun read(stream: DataInputStream) { - check(!isRemoved) { "Field was removed" } - val value = stream.readDouble() - val setter = this.setter - - if (setter != null) { - setter.invoke(value, access, true) - } else { - this.field = value - } - } - } - - abstract inner class AbstractIntField(field: Int, private val getter: IntFieldGetter? = null, protected val setter: IntFieldSetter? = null) : PrimitiveField(), IMutableIntField { - private val subs = IIntSubcriptable.Impl() - - override fun addListener(listener: IntConsumer): ISubscriptable.L { - return subs.addListener(listener) - } - - protected var field = field - set(value) { - if (field != value) { - field = value - subs.accept(value) - } - } - - final override val property = object : IMutableIntProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): Int { - return this@AbstractIntField.int - } - - override fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { - this@AbstractIntField.int = value - } - } - - protected val access = object : IntFieldAccess { - override fun readInt(): Int { - return this@AbstractIntField.field - } - - override fun write(value: Int) { - if (!isDirty && value != this@AbstractIntField.field) { - notifyEndpoints(this@AbstractIntField) - isDirty = true - } - - this@AbstractIntField.field = value - } - } - - final override var int: Int - get() { - return getter?.invoke(access) ?: this.field - } - set(value) { - val setter = this.setter - - if (setter != null) { - setter.invoke(value, access, false) - } else if (value != this.field) { - if (!isDirty) { - notifyEndpoints(this) - isDirty = true - } - - this.field = value - } - } - } - - /** - * Type specific field, storing primitive [Int] directly, and networking it as variable length integer - */ - inner class IntField(field: Int, getter: IntFieldGetter? = null, setter: IntFieldSetter? = null) : AbstractIntField(field, getter, setter) { - override fun write(stream: DataOutputStream, endpoint: Endpoint) { - check(!isRemoved) { "Field was removed" } - stream.writeVarInt(this.field) - isDirty = false - } - - override fun read(stream: DataInputStream) { - check(!isRemoved) { "Field was removed" } - val value = stream.readVarInt() - val setter = this.setter - - if (setter != null) { - setter.invoke(value, access, true) - } else { - this.field = value - } - } - } - - /** - * Type specific field, storing primitive [Int] directly, and networking it as 4 octets - */ - inner class FixedIntField(field: Int, getter: IntFieldGetter? = null, setter: IntFieldSetter? = null) : AbstractIntField(field, getter, setter) { - override fun write(stream: DataOutputStream, endpoint: Endpoint) { - check(!isRemoved) { "Field was removed" } - stream.writeInt(this.field) - isDirty = false - } - - override fun read(stream: DataInputStream) { - check(!isRemoved) { "Field was removed" } - val value = stream.readInt() - val setter = this.setter - - if (setter != null) { - setter.invoke(value, access, true) - } else { - this.field = value - } - } - } - - /** - * Type specific field, storing primitive [Long] directly - */ - abstract inner class AbstractLongField(field: Long, private val getter: LongFieldGetter? = null, protected val setter: LongFieldSetter? = null) : PrimitiveField(), IMutableLongField { - private val subs = ILongSubcriptable.Impl() - - override fun addListener(listener: LongConsumer): ISubscriptable.L { - return subs.addListener(listener) - } - - protected var field = field - set(value) { - if (field != value) { - field = value - subs.accept(value) - } - } - - final override val property = object : IMutableLongProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): Long { - return this@AbstractLongField.long - } - - override fun setValue(thisRef: Any?, property: KProperty<*>, value: Long) { - this@AbstractLongField.long = value - } - } - - protected val access = object : LongFieldAccess { - override fun readLong(): Long { - return this@AbstractLongField.field - } - - override fun write(value: Long) { - if (!isDirty && value != this@AbstractLongField.field) { - notifyEndpoints(this@AbstractLongField) - isDirty = true - } - - this@AbstractLongField.field = value - } - } - - final override var long: Long - get() { - return getter?.invoke(access) ?: this.field - } - set(value) { - val setter = this.setter - - if (setter != null) { - setter.invoke(value, access, false) - } else if (value != this.field) { - if (!isDirty) { - notifyEndpoints(this) - isDirty = true - } - - this.field = value - } - } - } - - /** - * Type specific field, storing primitive [Long] directly, and networking it as variable length integer - */ - inner class LongField(field: Long, getter: LongFieldGetter? = null, setter: LongFieldSetter? = null) : AbstractLongField(field, getter, setter) { - override fun write(stream: DataOutputStream, endpoint: Endpoint) { - check(!isRemoved) { "Field was removed" } - stream.writeVarLong(this.field) - isDirty = false - } - - override fun read(stream: DataInputStream) { - check(!isRemoved) { "Field was removed" } - val value = stream.readVarLong() - val setter = this.setter - - if (setter != null) { - setter.invoke(value, access, true) - } else { - this.field = value - } - } - } - - /** - * Type specific field, storing primitive [Long] directly, and networking it as 8 octets - */ - inner class FixedLongField(field: Long, getter: LongFieldGetter? = null, setter: LongFieldSetter? = null) : AbstractLongField(field, getter, setter) { - override fun write(stream: DataOutputStream, endpoint: Endpoint) { - check(!isRemoved) { "Field was removed" } - stream.writeLong(this.field) - isDirty = false - } - - override fun read(stream: DataInputStream) { - check(!isRemoved) { "Field was removed" } - val value = stream.readLong() - val setter = this.setter - - if (setter != null) { - setter.invoke(value, access, true) - } else { - this.field = value - } - } - } - - /** - * Type specific field, storing primitive [Boolean] directly - */ - inner class BooleanField(field: Boolean, private val getter: BooleanFieldGetter? = null, private val setter: BooleanFieldSetter? = null) : PrimitiveField(), IMutableBooleanField { - private val subs = IBooleanSubscriptable.Impl() - - override fun addListener(listener: BooleanConsumer): ISubscriptable.L { - return subs.addListener(listener) - } - - private var field = field - set(value) { - if (field != value) { - field = value - subs.accept(value) - } - } - - override val property = object : IMutableBooleanProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean { - return this@BooleanField.boolean - } - - override fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) { - this@BooleanField.boolean = value - } - } - - private val access = object : BooleanFieldAccess { - override fun readBoolean(): Boolean { - return this@BooleanField.field - } - - override fun write(value: Boolean) { - if (!isDirty && value != this@BooleanField.field) { - notifyEndpoints(this@BooleanField) - isDirty = true - } - - this@BooleanField.field = value - } - } - - override var boolean: Boolean - get() { - return getter?.invoke(access) ?: this.field - } - set(value) { - val setter = this.setter - - if (setter != null) { - setter.invoke(value, access, false) - } else if (value != this.field) { - if (!isDirty) { - notifyEndpoints(this) - isDirty = true - } - - this.field = value - } - } - - override fun write(stream: DataOutputStream, endpoint: Endpoint) { - check(!isRemoved) { "Field was removed" } - stream.writeBoolean(this.field) - isDirty = false - } - - override fun read(stream: DataInputStream) { - check(!isRemoved) { "Field was removed" } - val value = stream.readBoolean() - val setter = this.setter - - if (setter != null) { - setter.invoke(value, access, true) - } else { - this.field = value - } - } - } - - /** - * Networked value with backing getter which is constantly polled - */ - inner class ComputedField( - private val getter: () -> V, - private val codec: IStreamCodec, - observer: ((new: V) -> Unit)? = null - ) : AbstractField(), IField { - private var remote: Any? = Mark - private var clientValue: Any? = Mark - private val subs = ISubscriptable.Impl() - - init { - if (observer != null) { - subs.addListener(observer) - } - } - - override fun addListener(listener: Consumer): ISubscriptable.L { - return subs.addListener(listener) - } - - init { - observers.add(this) - } - - override fun observe(): Boolean { - check(!isRemoved) { "Field was removed" } - - val value = value - - if (!isDirty && (remote === Mark || !codec.compare(remote as V, value))) { - notifyEndpoints(this) - isDirty = true - remote = codec.copy(value) - } - - return isDirty - } - - override val value: V - get() { - val clientValue = clientValue - - if (clientValue === Mark) { - return getter.invoke() - } else { - return clientValue as V - } - } - - override fun write(stream: DataOutputStream, endpoint: Endpoint) { - check(!isRemoved) { "Field was removed" } - codec.write(stream, value) - isDirty = false - } - - override fun read(stream: DataInputStream) { - check(!isRemoved) { "Field was removed" } - val newValue = codec.read(stream) - clientValue = newValue - subs.accept(newValue) - } - } - - /** - * Networked value with backing getter which is constantly polled - * - * This class has concrete implementation for [Float] primitive - */ - inner class ComputedFloatField(private val getter: FloatSupplier, observer: FloatConsumer? = null) : AbstractField(), IFloatField { - private var remote: Float = 0f - private var isRemoteSet = false - private var clientValue: Float = 0f - private var isClientValue = false - private val subs = IFloatSubcriptable.Impl() - - init { - if (observer != null) { - subs.addListener(observer) - } - } - - override fun addListener(listener: FloatConsumer): ISubscriptable.L { - return subs.addListener(listener) - } - - override val property = object : IFloatProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): Float { - return float - } - } - - init { - observers.add(this) - } - - override fun observe(): Boolean { - check(!isRemoved) { "Field was removed" } - - val value = getter.getAsFloat() - - if (!isDirty && (!isRemoteSet || remote != value)) { - notifyEndpoints(this) - isDirty = true - isRemoteSet = true - remote = value - } - - return isDirty - } - - override fun markDirty() { - check(!isRemoved) { "Field was removed" } - notifyEndpoints(this) - isDirty = true - } - - override val float: Float - get() { - if (isClientValue) { - return clientValue - } else { - return getter.getAsFloat() - } - } - - override fun write(stream: DataOutputStream, endpoint: Endpoint) { - check(!isRemoved) { "Field was removed" } - stream.writeFloat(getter.getAsFloat()) - isDirty = false - } - - override fun read(stream: DataInputStream) { - check(!isRemoved) { "Field was removed" } - val newValue = stream.readFloat() - clientValue = newValue - isClientValue = true - subs.accept(newValue) - } - - @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property")) - override fun getValue(thisRef: Any?, property: KProperty<*>): Float { - return float - } - } - - /** - * Networked value with backing getter which is constantly polled - * - * This class has concrete implementation for [Double] primitive - */ - inner class ComputedDoubleField(private val getter: DoubleSupplier, observer: DoubleConsumer? = null) : AbstractField(), IDoubleField { - private var remote: Double = 0.0 - private var isRemoteSet = false - private var clientValue: Double = 0.0 - private var isClientValue = false - private val subs = IDoubleSubcriptable.Impl() - - init { - if (observer != null) { - subs.addListener(observer) - } - } - - override fun addListener(listener: DoubleConsumer): ISubscriptable.L { - return subs.addListener(listener) - } - - override val property = object : IDoubleProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): Double { - return double - } - } - - init { - observers.add(this) - } - - override fun observe(): Boolean { - check(!isRemoved) { "Field was removed" } - - val value = getter.asDouble - - if (!isDirty && (!isRemoteSet || remote != value)) { - notifyEndpoints(this) - isDirty = true - isRemoteSet = true - remote = value - } - - return isDirty - } - - override val double: Double - get() { - if (isClientValue) { - return clientValue - } else { - return getter.asDouble - } - } - - override fun write(stream: DataOutputStream, endpoint: Endpoint) { - check(!isRemoved) { "Field was removed" } - stream.writeDouble(getter.asDouble) - isDirty = false - } - - override fun read(stream: DataInputStream) { - check(!isRemoved) { "Field was removed" } - val newValue = stream.readDouble() - clientValue = newValue - isClientValue = true - subs.accept(newValue) - } - - @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property")) - override fun getValue(thisRef: Any?, property: KProperty<*>): Double { - return double - } - } - - /** - * Networked value with backing getter which is constantly polled - * - * This class has concrete implementation for [Int] primitive - */ - abstract inner class AbstractComputedIntField(protected val getter: IntSupplier, observer: IntConsumer? = null) : AbstractField(), IIntField { - private var remote: Int = 0 - private var isRemoteSet = false - protected var clientValue: Int = 0 - set(value) { - isClientValue = true - - if (field != value) { - field = value - subs.accept(value) - } - } - - protected var isClientValue = false - private val subs = IIntSubcriptable.Impl() - - init { - if (observer != null) { - subs.addListener(observer) - } - } - - override fun addListener(listener: IntConsumer): ISubscriptable.L { - return subs.addListener(listener) - } - - final override val property = object : IIntProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): Int { - return int - } - } - - init { - observers.add(this) - } - - final override fun observe(): Boolean { - check(!isRemoved) { "Field was removed" } - - val value = getter.asInt - - if (!isDirty && (!isRemoteSet || remote != value)) { - notifyEndpoints(this) - isDirty = true - isRemoteSet = true - remote = value - } - - return isDirty - } - - final override val int: Int - get() { - if (isClientValue) { - return clientValue - } else { - return getter.asInt - } - } - - @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property")) - override fun getValue(thisRef: Any?, property: KProperty<*>): Int { - return int - } - } - - /** - * Networked value with backing getter which is constantly polled - * - * This class has concrete implementation for [Int] primitive, networking it as variable length integer - */ - inner class ComputedIntField(getter: IntSupplier, observer: IntConsumer = IntConsumer {}) : AbstractComputedIntField(getter, observer) { - override fun write(stream: DataOutputStream, endpoint: Endpoint) { - check(!isRemoved) { "Field was removed" } - stream.writeSignedVarInt(getter.asInt) - isDirty = false - } - - override fun read(stream: DataInputStream) { - check(!isRemoved) { "Field was removed" } - clientValue = stream.readSignedVarInt() - } - } - - /** - * Networked value with backing getter which is constantly polled - * - * This class has concrete implementation for [Int] primitive, networking it as 4 octets - */ - inner class ComputedFixedIntField(getter: IntSupplier, observer: IntConsumer = IntConsumer {}) : AbstractComputedIntField(getter, observer) { - override fun write(stream: DataOutputStream, endpoint: Endpoint) { - check(!isRemoved) { "Field was removed" } - stream.writeInt(getter.asInt) - isDirty = false - } - - override fun read(stream: DataInputStream) { - check(!isRemoved) { "Field was removed" } - clientValue = stream.readInt() - } - } - - /** - * Networked value with backing getter which is constantly polled - * - * This class has concrete implementation for [Long] primitive - */ - abstract inner class AbstractComputedLongField(protected val getter: LongSupplier, observer: LongConsumer? = null) : AbstractField(), ILongField { - private var remote: Long = 0L - private var isRemoteSet = false - protected var clientValue: Long = 0L - set(value) { - isClientValue = true - - if (field != value) { - field = value - subs.accept(value) - } - } - - protected var isClientValue = false - private val subs = ILongSubcriptable.Impl() - - init { - if (observer != null) { - subs.addListener(observer) - } - } - - override fun addListener(listener: LongConsumer): ISubscriptable.L { - return subs.addListener(listener) - } - - final override val property = object : ILongProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): Long { - return long - } - } - - init { - observers.add(this) - } - - final override fun observe(): Boolean { - check(!isRemoved) { "Field was removed" } - - val value = getter.asLong - - if (!isDirty && (!isRemoteSet || remote != value)) { - notifyEndpoints(this) - isDirty = true - isRemoteSet = true - remote = value - } - - return isDirty - } - - final override val long: Long - get() { - if (isClientValue) { - return clientValue - } else { - return getter.asLong - } - } - - @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property")) - override fun getValue(thisRef: Any?, property: KProperty<*>): Long { - return long - } - } - - /** - * Networked value with backing getter which is constantly polled - * - * This class has concrete implementation for [Long] primitive, networking it as variable length integer - */ - inner class ComputedLongField(getter: LongSupplier, observer: LongConsumer = LongConsumer {}) : AbstractComputedLongField(getter, observer) { - override fun write(stream: DataOutputStream, endpoint: Endpoint) { - check(!isRemoved) { "Field was removed" } - stream.writeSignedVarLong(getter.asLong) - isDirty = false - } - - override fun read(stream: DataInputStream) { - check(!isRemoved) { "Field was removed" } - clientValue = stream.readSignedVarLong() - } - } - - /** - * Networked value with backing getter which is constantly polled - * - * This class has concrete implementation for [Long] primitive, networking it as 8 octets - */ - inner class ComputedFixedLongField(getter: LongSupplier, observer: LongConsumer = LongConsumer {}) : AbstractComputedLongField(getter, observer) { - override fun write(stream: DataOutputStream, endpoint: Endpoint) { - check(!isRemoved) { "Field was removed" } - stream.writeLong(getter.asLong) - isDirty = false - } - - override fun read(stream: DataInputStream) { - check(!isRemoved) { "Field was removed" } - clientValue = stream.readLong() - } - } - - /** - * Networked value with backing getter which is constantly polled - * - * This class has concrete implementation for [Boolean] primitive - */ - inner class ComputedBooleanField(private val getter: BooleanSupplier, observer: BooleanConsumer? = null) : AbstractField(), IBooleanField { - private var remote: Boolean = false - private var isRemoteSet = false - private var clientValue: Boolean = false - private var isClientValue = false - private val subs = IBooleanSubscriptable.Impl() - - init { - if (observer != null) { - subs.addListener(observer) - } - } - - override fun addListener(listener: BooleanConsumer): ISubscriptable.L { - return subs.addListener(listener) - } - - override val property = object : IBooleanProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean { - return boolean - } - } - - init { - observers.add(this) - } - - override fun observe(): Boolean { - check(!isRemoved) { "Field was removed" } - - val value = getter.asBoolean - - if (!isDirty && (!isRemoteSet || remote != value)) { - notifyEndpoints(this) - isDirty = true - isRemoteSet = true - remote = value - } - - return isDirty - } - - override val boolean: Boolean - get() { - if (isClientValue) { - return clientValue - } else { - return getter.asBoolean - } - } - - override fun write(stream: DataOutputStream, endpoint: Endpoint) { - check(!isRemoved) { "Field was removed" } - stream.writeBoolean(getter.asBoolean) - isDirty = false - } - - override fun read(stream: DataInputStream) { - check(!isRemoved) { "Field was removed" } - val newValue = stream.readBoolean() - clientValue = newValue - isClientValue = true - subs.accept(newValue) - } - - @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property")) - override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean { - return boolean - } - } - - /** - * Networked variable with backing field holding mutable value, which is constantly observed for changes - */ - inner class ObservedField : AbstractField, IMutableField { - private val codec: IStreamCodec - private val getter: () -> V - private val setter: (V) -> Unit - private var remote: V - private val subs = ISubscriptable.Impl() - - override fun addListener(listener: Consumer): ISubscriptable.L { - return subs.addListener(listener) - } - - override var value: V - get() = getter.invoke() - set(value) { setter.invoke(value) } - - constructor(field: KMutableProperty0, codec: IStreamCodec) : super() { - this.codec = codec - getter = field::get - setter = field::set - remote = codec.copy(value) - } - - constructor(getter: () -> V, setter: (V) -> Unit, codec: IStreamCodec) : super() { - this.codec = codec - this.getter = getter - this.setter = setter - remote = codec.copy(value) - } - - init { - observers.add(this) - } - - override fun observe(): Boolean { - check(!isRemoved) { "Field was removed" } - - val value = value - - if (!isDirty && !codec.compare(remote, value)) { - notifyEndpoints(this) - isDirty = true - remote = codec.copy(value) - } - - return isDirty - } - - override fun write(stream: DataOutputStream, endpoint: Endpoint) { - check(!isRemoved) { "Field was removed" } - codec.write(stream, value) - isDirty = false - } - - override fun read(stream: DataInputStream) { - check(!isRemoved) { "Field was removed" } - this.value = codec.read(stream) - subs.accept(this.value) - } - } - - inner class Set( - private val codec: IStreamCodec, - private val backingSet: MutableSet, - private val callback: ((changes: Collection>) -> Unit)? = null, - ) : AbstractField>(), ISubscriptable> by ISubscriptable.empty() { - private var isRemote = false - - private fun pushBacklog(element: E, action: (DataOutputStream) -> Unit) { - check(!isRemote) { "Field marked as remote" } - - val pair = element to action - - forEachEndpoint { - val list = it.getSetBacklog(this) - val iterator = list.listIterator() - - for (value in iterator) { - if (value.first == element) { - iterator.remove() - } - } - - list.addLast(pair) - } - } - - override fun observe(): Boolean { - return false - } - - override fun remove() { - if (!isRemoved) { - forEachEndpoint { it.removeSetBacklog(this) } - } - - super.remove() - } - - override fun markDirty() { - check(!isRemoved) { "Field was removed" } - - if (isRemote || endpoints.isEmpty()) - return - - isDirty = true - - val endpoints = LinkedList Unit>>>() - - forEachEndpoint { - endpoints.add(it.getSetBacklog(this)) - it.addDirtyField(this) - } - - endpoints.forEach { - it.clear() - it.add(null to ClearBacklogEntry) - } - - for (value in backingSet) { - val valueCopy = codec.copy(value) - val pair: Pair Unit> = valueCopy to { it.write(ChangesetAction.ADD.ordinal + 1); codec.write(it, valueCopy) } - - endpoints.forEach { - it.add(pair) - } - } - } - - override fun markDirty(endpoint: Endpoint) { - super.markDirty(endpoint) - - endpoint.getSetBacklog(this).let { - it.clear() - it.add(null to ClearBacklogEntry) - - for (value in backingSet) { - val valueCopy = codec.copy(value) - it.add(valueCopy to { it.write(ChangesetAction.ADD.ordinal + 1); codec.write(it, valueCopy) }) - } - } - } - - override val value: MutableSet = object : MutableSet { - override fun add(element: E): Boolean { - if (backingSet.add(element)) { - if (!isRemote) { - markDirty() - - val copy = codec.copy(element) - - pushBacklog(element) { - it.write(ChangesetAction.ADD.ordinal + 1) - codec.write(it, copy) - } - } - - return true - } - - return false - } - - override fun addAll(elements: Collection): Boolean { - var any = false - elements.forEach { any = add(it) || any } - return any - } - - override fun clear() { - if (backingSet.isNotEmpty()) { - backingSet.clear() - - if (!isRemote) { - markDirty() - - forEachEndpoint { - it.getSetBacklog(this@Set).let { - it.clear() - it.add(null to ClearBacklogEntry) - } - } - } - } - } - - override fun iterator(): MutableIterator { - return object : MutableIterator { - private val parent = backingSet.iterator() - private var lastElement: Any? = Mark - - override fun hasNext(): Boolean { - return parent.hasNext() - } - - override fun next(): E { - return parent.next().also { lastElement = it } - } - - override fun remove() { - parent.remove() - val lastElement = lastElement - - if (lastElement !== Mark) { - this.lastElement = Mark - - if (!isRemote) { - markDirty() - - @Suppress("unchecked_cast") - pushBacklog(lastElement as E) { - it.write(ChangesetAction.REMOVE.ordinal + 1) - codec.write(it, lastElement as E) - } - } - } - } - } - } - - override fun remove(element: E): Boolean { - if (backingSet.remove(element)) { - if (!isRemote) { - markDirty() - - val copy = codec.copy(element) - - pushBacklog(element) { - it.write(ChangesetAction.REMOVE.ordinal + 1) - codec.write(it, copy) - } - } - - return true - } - - return false - } - - override fun removeAll(elements: Collection): Boolean { - var any = false - elements.forEach { any = remove(it) || any } - return any - } - - override fun retainAll(elements: Collection): Boolean { - var any = false - - val iterator = iterator() - - for (value in iterator) { - if (value !in elements) { - any = true - iterator.remove() - } - } - - return any - } - - override val size: Int - get() = backingSet.size - - override fun contains(element: E): Boolean { - return element in backingSet - } - - override fun containsAll(elements: Collection): Boolean { - return backingSet.containsAll(elements) - } - - override fun isEmpty(): Boolean { - return backingSet.isEmpty() - } - } - - override fun write(stream: DataOutputStream, endpoint: Endpoint) { - val list = endpoint.getSetBacklog(this) - - for (value in list) { - value.second.invoke(stream) - } - - stream.write(0) - list.clear() - isDirty = false - } - - override fun read(stream: DataInputStream) { - if (!isRemote) { - isRemote = true - forEachEndpoint { it.removeSetBacklog(this) } - } - - isDirty = false - - var action = stream.read() - val changeset = LinkedList>() - - while (action != 0) { - if ((action - 1) !in ChangesetActionList.indices) { - throw IllegalArgumentException("Unknown changeset action with index ${action - 1}") - } - - when (ChangesetActionList[action - 1]) { - ChangesetAction.CLEAR -> { - changeset.add(SetChangeset(ChangesetAction.CLEAR, null)) - backingSet.clear() - } - - ChangesetAction.ADD -> { - val read = codec.read(stream) - changeset.add(SetChangeset(ChangesetAction.ADD, read)) - backingSet.add(read) - } - - ChangesetAction.REMOVE -> { - val read = codec.read(stream) - changeset.add(SetChangeset(ChangesetAction.REMOVE, read)) - backingSet.remove(read) - } - } - - action = stream.read() - } - - callback?.invoke(changeset) - } - } - - inner class Map( - private val keyCodec: IStreamCodec, - private val valueCodec: IStreamCodec, - private val backingMap: MutableMap, - private val callback: ((changes: Collection>) -> Unit)? = null, - ) : AbstractField>(), IField>, ISubscriptable> by ISubscriptable.empty() { - private var sentAllValues = false - private var isRemote = false - - private fun pushBacklog(key: Any?, value: (DataOutputStream) -> Unit) { - val pair = key to value - - forEachEndpoint { - val list = it.getMapBacklog(this) - val iterator = list.listIterator() - - for (e in iterator) { - if (e.first == key) { - iterator.remove() - } - } - - list.addLast(pair) - } - } - - override fun observe(): Boolean { - return false - } - - override fun markDirty() { - check(!isRemoved) { "Field was removed" } - - if (isRemote || endpoints.isEmpty()) - return - - isDirty = true - val backlogs = LinkedList Unit>>>() - - forEachEndpoint { - it.addDirtyField(this) - val value = it.getMapBacklog(this) - backlogs.add(value) - value.clear() - value.add(null to ClearBacklogEntry) - } - - for ((key, value) in backingMap) { - val valueCopy = valueCodec.copy(value) - - val action = key to { it: DataOutputStream -> - it.write(ChangesetAction.ADD.ordinal + 1) - keyCodec.write(it, key) - valueCodec.write(it, valueCopy) - } - - for (backlog in backlogs) { - backlog.add(action) - } - } - } - - override fun markDirty(endpoint: Endpoint) { - check(!isRemoved) { "Field was removed" } - - if (isRemote) - return - - val backlog = endpoint.getMapBacklog(this) - - backlog.clear() - backlog.add(null to ClearBacklogEntry) - - for ((key, value) in backingMap) { - val valueCopy = valueCodec.copy(value) - - backlog.add(key to { - it.write(ChangesetAction.ADD.ordinal + 1) - keyCodec.write(it, key) - valueCodec.write(it, valueCopy) - }) - } - } - - private fun lmarkDirty() { - if (!isDirty) { - notifyEndpoints(this@Map) - isDirty = true - } - } - - override val value: MutableMap = object : ProxiedMap(backingMap) { - override fun onClear() { - if (isRemote || endpoints.isEmpty()) - return - - forEachEndpoint { endpoint -> - endpoint.getMapBacklog(this@Map).let { - it.clear() - it.add(null to ClearBacklogEntry) - } - } - - lmarkDirty() - this@FieldSynchronizer.isDirty = true - } - - override fun onValueAdded(key: K, value: V) { - if (isRemote || endpoints.isEmpty()) - return - - val keyCopy = keyCodec.copy(key) - val valueCopy = valueCodec.copy(value) - - pushBacklog(key) { - it.write(ChangesetAction.ADD.ordinal + 1) - keyCodec.write(it, keyCopy) - valueCodec.write(it, valueCopy) - } - - lmarkDirty() - } - - override fun onValueRemoved(key: K, value: V) { - if (isRemote || endpoints.isEmpty()) - return - - val keyCopy = keyCodec.copy(key) - - pushBacklog(key) { - it.write(ChangesetAction.REMOVE.ordinal + 1) - keyCodec.write(it, keyCopy) - } - - lmarkDirty() - } - } - - override fun write(stream: DataOutputStream, endpoint: Endpoint) { - sentAllValues = false - isDirty = false - - val iterator = endpoint.getMapBacklog(this).listIterator() - - for (entry in iterator) { - entry.second.invoke(stream) - iterator.remove() - } - - stream.write(0) - } - - override fun read(stream: DataInputStream) { - if (!isRemote) { - isRemote = true - forEachEndpoint { it.removeMapBacklog(this) } - } - - isDirty = false - - val changeset = LinkedList>() - var readAction = stream.read() - 1 - - while (readAction != -1) { - if (readAction >= ChangesetActionList.size) { - throw IndexOutOfBoundsException("Unknown map action with ID $readAction") - } - - when (ChangesetActionList[readAction]) { - ChangesetAction.CLEAR -> { - backingMap.clear() - changeset.add(ClearMapChangeset) - } - - ChangesetAction.ADD -> { - val key = keyCodec.read(stream) - val value = valueCodec.read(stream) - backingMap[key] = value - changeset.add(MapChangeset(ChangesetAction.ADD, key, value)) - } - - ChangesetAction.REMOVE -> { - val key = keyCodec.read(stream) - backingMap.remove(key) - changeset.add(MapChangeset(ChangesetAction.REMOVE, key, null)) - } - } - - readAction = stream.read() - 1 - } - - if (changeset.size != 0) { - callback?.invoke(changeset) - } - } - } - - /** - * marks all fields dirty, invalidates mappings for each endpoint - */ - fun invalidate() { - for (field in fields) { - field?.markDirty() - } - } - - /** - * Observe changes of all fields with backing computation lambda - */ - fun observe(): Boolean { - var changes = false - - if (observers.isNotEmpty()) { - for (field in observers) { - changes = field.observe() || changes - } - } - - return changes - } - - /** - * [defaultEndpoint]#collectNetworkPayload - */ - fun collectNetworkPayload(): ByteArrayOutputStream? { - check(!defaultEndpoint.unused) { "Default endpoint is not used" } - observe() - val values = defaultEndpoint.collectNetworkPayload() - markClean() - return values - } - - fun read(stream: InputStream): Int { - var fieldId = stream.readVarInt() - var i = 0 - val dataStream = DataInputStream(stream) - - while (fieldId != 0) { - val field = fields.getOrNull(fieldId - 1) ?: throw IllegalArgumentException("Unknown field ID ${fieldId - 1}") - field.read(dataStream) - fieldId = stream.readVarInt() - i++ - } - - return i - } - - private object Mark - - companion object { - private val ClearBacklogEntry = { stream: DataOutputStream -> stream.write(ChangesetAction.CLEAR.ordinal + 1) } - private val ChangesetActionList = ChangesetAction.values() - private val ClearMapChangeset = MapChangeset(ChangesetAction.CLEAR, null, null) - } -} diff --git a/src/main/kotlin/ru/dbotthepony/kommons/networking/Fields.kt b/src/main/kotlin/ru/dbotthepony/kommons/networking/Fields.kt deleted file mode 100644 index efdbe36..0000000 --- a/src/main/kotlin/ru/dbotthepony/kommons/networking/Fields.kt +++ /dev/null @@ -1,173 +0,0 @@ -package ru.dbotthepony.kommons.networking - -import ru.dbotthepony.kommons.event.IBooleanSubscriptable -import ru.dbotthepony.kommons.event.IDoubleSubcriptable -import ru.dbotthepony.kommons.event.IFloatSubcriptable -import ru.dbotthepony.kommons.event.IIntSubcriptable -import ru.dbotthepony.kommons.event.ILongSubcriptable -import ru.dbotthepony.kommons.event.ISubscriptable -import ru.dbotthepony.kommons.util.FloatSupplier -import java.io.DataInputStream -import java.io.DataOutputStream -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.properties.ReadOnlyProperty -import kotlin.reflect.KProperty - -sealed interface IField : ReadOnlyProperty, Supplier, () -> V, ISubscriptable { - fun observe(): Boolean - fun markDirty() - fun markDirty(endpoint: FieldSynchronizer.Endpoint) - val value: V - val isRemoved: Boolean - - fun remove() - - fun write(stream: DataOutputStream, endpoint: FieldSynchronizer.Endpoint) - fun read(stream: DataInputStream) - - override fun getValue(thisRef: Any?, property: KProperty<*>): V { - return this.value - } - - override fun get(): V { - return value - } - - override fun invoke(): V { - return value - } -} - -interface IFloatProperty { - operator fun getValue(thisRef: Any?, property: KProperty<*>): Float -} - -sealed interface IFloatField : IField, FloatSupplier, IFloatSubcriptable { - val float: Float - val property: IFloatProperty - - override val value: Float - get() = float - - @Deprecated("Use type specific Supplier interface") - override fun get(): Float { - return float - } - - override fun getAsFloat(): Float { - return float - } - - @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property")) - override fun getValue(thisRef: Any?, property: KProperty<*>): Float { - return float - } -} - -interface IDoubleProperty { - operator fun getValue(thisRef: Any?, property: KProperty<*>): Double -} - -sealed interface IDoubleField : IField, DoubleSupplier, IDoubleSubcriptable { - val double: Double - val property: IDoubleProperty - - @Deprecated("Use type specific Supplier interface") - override fun get(): Double { - return double - } - - override val value: Double - get() = double - - override fun getAsDouble(): Double { - return double - } - - @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property")) - override fun getValue(thisRef: Any?, property: KProperty<*>): Double { - return double - } -} - -interface IIntProperty { - operator fun getValue(thisRef: Any?, property: KProperty<*>): Int -} - -sealed interface IIntField : IField, IntSupplier, IIntSubcriptable { - val int: Int - val property: IIntProperty - - @Deprecated("Use type specific Supplier interface") - override fun get(): Int { - return int - } - - override val value: Int - get() = int - - override fun getAsInt(): Int { - return int - } - - @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property")) - override fun getValue(thisRef: Any?, property: KProperty<*>): Int { - return int - } -} - -interface ILongProperty { - operator fun getValue(thisRef: Any?, property: KProperty<*>): Long -} - -sealed interface ILongField : IField, LongSupplier, ILongSubcriptable { - val long: Long - val property: ILongProperty - - @Deprecated("Use type specific Supplier interface") - override fun get(): Long { - return long - } - - override val value: Long - get() = long - - override fun getAsLong(): Long { - return long - } - - @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property")) - override fun getValue(thisRef: Any?, property: KProperty<*>): Long { - return long - } -} - -interface IBooleanProperty { - operator fun getValue(thisRef: Any?, property: KProperty<*>): Boolean -} - -sealed interface IBooleanField : IField, BooleanSupplier, IBooleanSubscriptable { - val boolean: Boolean - val property: IBooleanProperty - - @Deprecated("Use type specific Supplier interface") - override fun get(): Boolean { - return boolean - } - - override val value: Boolean - get() = boolean - - override fun getAsBoolean(): Boolean { - return boolean - } - - @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property")) - override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean { - return boolean - } -} diff --git a/src/main/kotlin/ru/dbotthepony/kommons/networking/MutableFields.kt b/src/main/kotlin/ru/dbotthepony/kommons/networking/MutableFields.kt deleted file mode 100644 index 9744871..0000000 --- a/src/main/kotlin/ru/dbotthepony/kommons/networking/MutableFields.kt +++ /dev/null @@ -1,165 +0,0 @@ -package ru.dbotthepony.kommons.networking - -import it.unimi.dsi.fastutil.booleans.BooleanConsumer -import it.unimi.dsi.fastutil.floats.FloatConsumer -import ru.dbotthepony.kommons.core.SentientGetterSetter -import java.util.function.DoubleConsumer -import java.util.function.IntConsumer -import java.util.function.LongConsumer -import kotlin.reflect.KProperty - -sealed interface IMutableField : IField, SentientGetterSetter { - override fun getValue(thisRef: Any?, property: KProperty<*>): V { - return this.value - } - - override var value: V - - override fun accept(t: V) { - value = t - } - - override fun invoke(): V { - return this.value - } -} - -interface IMutableFloatProperty : IFloatProperty { - operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Float) -} - -sealed interface IMutableFloatField : IMutableField, IFloatField, FloatConsumer { - override var float: Float - override val property: IMutableFloatProperty - - @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.float")) - override var value: Float - get() = float - set(value) { float = value } - - override fun accept(t: Float) { - float = t - } - - @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property")) - override fun getValue(thisRef: Any?, property: KProperty<*>): Float { - return float - } - - @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property")) - override fun setValue(thisRef: Any?, property: KProperty<*>, value: Float) { - float = value - } -} - -interface IMutableDoubleProperty : IDoubleProperty { - operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Double) -} - -sealed interface IMutableDoubleField : IMutableField, IDoubleField, DoubleConsumer { - override var double: Double - override val property: IMutableDoubleProperty - - @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.double")) - override var value: Double - get() = double - set(value) { double = value } - - override fun accept(t: Double) { - double = t - } - - @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property")) - override fun getValue(thisRef: Any?, property: KProperty<*>): Double { - return double - } - - @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property")) - override fun setValue(thisRef: Any?, property: KProperty<*>, value: Double) { - double = value - } -} - -interface IMutableIntProperty : IIntProperty { - operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) -} - -sealed interface IMutableIntField : IMutableField, IIntField, IntConsumer { - override var int: Int - override val property: IMutableIntProperty - - @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.int")) - override var value: Int - get() = int - set(value) { int = value } - - override fun accept(t: Int) { - int = t - } - - @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property")) - override fun getValue(thisRef: Any?, property: KProperty<*>): Int { - return int - } - - @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property")) - override fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { - int = value - } -} - -interface IMutableLongProperty : ILongProperty { - operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Long) -} - -sealed interface IMutableLongField : IMutableField, ILongField, LongConsumer { - override var long: Long - override val property: IMutableLongProperty - - @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.long")) - override var value: Long - get() = long - set(value) { long = value } - - override fun accept(t: Long) { - long = t - } - - @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property")) - override fun getValue(thisRef: Any?, property: KProperty<*>): Long { - return long - } - - @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property")) - override fun setValue(thisRef: Any?, property: KProperty<*>, value: Long) { - long = value - } -} - -interface IMutableBooleanProperty : IBooleanProperty { - operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) -} - -sealed interface IMutableBooleanField : IMutableField, IBooleanField, BooleanConsumer { - override var boolean: Boolean - override val property: IMutableBooleanProperty - - @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.boolean")) - override var value: Boolean - get() = boolean - set(value) { boolean = value } - - override fun accept(t: Boolean) { - boolean = t - } - - @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property")) - override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean { - return boolean - } - - @Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property")) - override fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) { - boolean = value - } -} diff --git a/src/main/kotlin/ru/dbotthepony/kommons/util/Delegate.kt b/src/main/kotlin/ru/dbotthepony/kommons/util/Delegate.kt new file mode 100644 index 0000000..e55b7cf --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kommons/util/Delegate.kt @@ -0,0 +1,64 @@ +@file:Suppress("UsePropertyAccessSyntax") + +package ru.dbotthepony.kommons.util + +import java.util.function.Consumer +import java.util.function.Supplier +import kotlin.reflect.KProperty + +/** + * [Supplier] and [Consumer] combined in single interface + */ +interface Delegate : Supplier, Consumer { + class UnboundBox : Delegate { + private var value: Any? = Companion + + override fun get(): V { + val get = value + if (get === Companion) throw UninitializedPropertyAccessException() + return get as V + } + + override fun accept(t: V) { + value = t + } + + private companion object + } + + class Box(private var value: V) : Delegate { + override fun get(): V { + return value + } + + override fun accept(t: V) { + value = t + } + } + + class SmartBox(value: V, private val getter: DelegateGetter, private val setter: DelegateSetter) : Delegate { + private val value = Box(value) + + override fun get(): V { + return getter(value) + } + + override fun accept(t: V) { + setter(value, t) + } + } + + class Of(getter: Supplier, setter: Consumer) : Delegate, Supplier by getter, Consumer by setter +} + +operator fun Supplier.getValue(ref: Any?, property: KProperty<*>): V { + return get() +} + +operator fun Consumer.setValue(ref: Any?, property: KProperty<*>, value: V) { + accept(value) +} + +var Delegate.value: V + get() = get() + set(value) { accept(value) } diff --git a/src/main/kotlin/ru/dbotthepony/kommons/util/DelegateGetter.kt b/src/main/kotlin/ru/dbotthepony/kommons/util/DelegateGetter.kt new file mode 100644 index 0000000..b5a4720 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kommons/util/DelegateGetter.kt @@ -0,0 +1,17 @@ +package ru.dbotthepony.kommons.util + +fun interface DelegateGetter { + operator fun invoke(field: Delegate): V + + private object PASSTHROUGH : DelegateGetter { + override fun invoke(field: Delegate): Any? { + return field.get() + } + } + + companion object { + fun passthrough(): DelegateGetter { + return PASSTHROUGH as DelegateGetter + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kommons/util/DelegateSetter.kt b/src/main/kotlin/ru/dbotthepony/kommons/util/DelegateSetter.kt new file mode 100644 index 0000000..ee59da8 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kommons/util/DelegateSetter.kt @@ -0,0 +1,17 @@ +package ru.dbotthepony.kommons.util + +fun interface DelegateSetter { + operator fun invoke(field: Delegate, value: V) + + private object PASSTHROUGH : DelegateSetter { + override fun invoke(field: Delegate, value: Any?) { + field.accept(value) + } + } + + companion object { + fun passthrough(): DelegateSetter { + return PASSTHROUGH as DelegateSetter + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kommons/core/Either.kt b/src/main/kotlin/ru/dbotthepony/kommons/util/Either.kt similarity index 97% rename from src/main/kotlin/ru/dbotthepony/kommons/core/Either.kt rename to src/main/kotlin/ru/dbotthepony/kommons/util/Either.kt index 98137d0..514b29f 100644 --- a/src/main/kotlin/ru/dbotthepony/kommons/core/Either.kt +++ b/src/main/kotlin/ru/dbotthepony/kommons/util/Either.kt @@ -1,4 +1,4 @@ -package ru.dbotthepony.kommons.core +package ru.dbotthepony.kommons.util /** * Implements a value container which contain either [L] or [R] value diff --git a/src/main/kotlin/ru/dbotthepony/kommons/util/FloatSupplier.kt b/src/main/kotlin/ru/dbotthepony/kommons/util/FloatSupplier.kt deleted file mode 100644 index 082fc5e..0000000 --- a/src/main/kotlin/ru/dbotthepony/kommons/util/FloatSupplier.kt +++ /dev/null @@ -1,12 +0,0 @@ -package ru.dbotthepony.kommons.util - -import java.util.function.Supplier - -fun interface FloatSupplier : Supplier { - fun getAsFloat(): Float - - @Deprecated("Use type-specific method", replaceWith = ReplaceWith("this.getAsFloat()")) - override fun get(): Float { - return getAsFloat() - } -} diff --git a/src/main/kotlin/ru/dbotthepony/kommons/core/KOptional.kt b/src/main/kotlin/ru/dbotthepony/kommons/util/KOptional.kt similarity index 98% rename from src/main/kotlin/ru/dbotthepony/kommons/core/KOptional.kt rename to src/main/kotlin/ru/dbotthepony/kommons/util/KOptional.kt index e624cdc..37fe34b 100644 --- a/src/main/kotlin/ru/dbotthepony/kommons/core/KOptional.kt +++ b/src/main/kotlin/ru/dbotthepony/kommons/util/KOptional.kt @@ -1,4 +1,4 @@ -package ru.dbotthepony.kommons.core +package ru.dbotthepony.kommons.util import java.util.function.Supplier diff --git a/src/main/kotlin/ru/dbotthepony/kommons/util/Listenable.kt b/src/main/kotlin/ru/dbotthepony/kommons/util/Listenable.kt new file mode 100644 index 0000000..a40613c --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kommons/util/Listenable.kt @@ -0,0 +1,100 @@ +package ru.dbotthepony.kommons.util + +import java.util.concurrent.CopyOnWriteArrayList +import java.util.function.Consumer + +interface Listenable { + /** + * Listener token, allows to remove listener from subscriber list + */ + fun interface L { + /** + * Removes this listener + */ + fun remove() + } + + fun addListener(listener: Runnable): L { + return addListener(Consumer { listener.run() }) + } + + fun addListener(listener: Consumer): L + + /** + * Adding and removing listeners is thread-safe. + */ + class Impl : Listenable, Consumer { + private inner class L(val callback: Consumer) : Listenable.L { + private var isRemoved = false + + init { + subscribers.add(this) + } + + override fun remove() { + if (!isRemoved) { + isRemoved = true + subscribers.remove(this) + } + } + } + + private val subscribers = CopyOnWriteArrayList() + + override fun addListener(listener: Consumer): Listenable.L { + return L(listener) + } + + override fun accept(t: V) { + subscribers.forEach { it.callback.accept(t) } + } + } + + class Void : Listenable, Runnable { + private inner class L(val callback: Runnable) : Listenable.L { + private var isRemoved = false + + init { + subscribers.add(this) + } + + override fun remove() { + if (!isRemoved) { + isRemoved = true + subscribers.remove(this) + } + } + } + + private val subscribers = CopyOnWriteArrayList() + + override fun addListener(listener: Runnable): Listenable.L { + return L(listener) + } + + override fun addListener(listener: Consumer): Listenable.L { + return L(Runnable { listener.accept(Unit) }) + } + + override fun run() { + subscribers.forEach { it.callback.run() } + } + } + + companion object : Listenable, L { + @Suppress("unchecked_cast") + fun empty(): Listenable { + return this as Listenable + } + + override fun remove() {} + + override fun addListener(listener: Consumer): L { + return this + } + + override fun addListener(listener: Runnable): L { + return this + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kommons/util/ListenableDelegate.kt b/src/main/kotlin/ru/dbotthepony/kommons/util/ListenableDelegate.kt new file mode 100644 index 0000000..4b8171f --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kommons/util/ListenableDelegate.kt @@ -0,0 +1,177 @@ +package ru.dbotthepony.kommons.util + +import java.util.concurrent.locks.ReentrantLock +import java.util.function.Consumer +import java.util.function.Supplier +import kotlin.concurrent.withLock + +interface ListenableDelegate : Delegate, Listenable { + class UnboundBox : ListenableDelegate { + private var value: Any? = Companion + private val listeners = Listenable.Impl() + + override fun addListener(listener: Consumer): Listenable.L { + return listeners.addListener(listener) + } + + override fun get(): V { + val get = value + if (get === Companion) throw UninitializedPropertyAccessException() + return get as V + } + + override fun accept(t: V) { + value = t + } + + private companion object + } + + class Box(private var value: V) : ListenableDelegate { + private val listeners = Listenable.Impl() + + override fun addListener(listener: Consumer): Listenable.L { + return listeners.addListener(listener) + } + + override fun get(): V { + return value + } + + override fun accept(t: V) { + if (value != t) { + value = t + listeners.accept(t) + } + } + } + + class SmartBox(value: V, private val getter: DelegateGetter, private val setter: DelegateSetter) : ListenableDelegate { + private val value = Box(value) + + override fun addListener(listener: Consumer): Listenable.L { + return value.addListener(listener) + } + + override fun get(): V { + return getter(value) + } + + override fun accept(t: V) { + setter(value, t) + } + } + + abstract class AbstractShadow(private val parent: Supplier) : ListenableDelegate, ValueObserver { + @Volatile + private var shadow: Any? = Companion + @Volatile + private var observed: Any? = Companion + private val listeners = Listenable.Impl() + private val lock = ReentrantLock() + + protected abstract fun compare(a: V, b: V): Boolean + protected abstract fun copy(value: V): V + + override fun get(): V { + if (shadow !== Companion) { + return shadow as V + } + + return parent.get() + } + + override fun accept(t: V) { + val shadow = shadow + this.shadow = t + + if (shadow != t) { + listeners.accept(t) + } + } + + override fun addListener(listener: Consumer): Listenable.L { + return listeners.addListener(listener) + } + + override fun observe(): Boolean { + if (shadow !== Companion) + return false + + val get = parent.get() + + if (observed === Companion || !compare(observed as V, get)) { + lock.withLock { + if (shadow !== Companion) + return false + + if (observed === Companion || !compare(observed as V, get)) { + observed = copy(get) + listeners.accept(get) + return true + } + } + } + + return false + } + + override fun getAndObserve(): Pair { + if (shadow !== Companion) + return (shadow as V) to false + + val get = parent.get() + + if (observed === Companion || !compare(observed as V, get)) { + lock.withLock { + if (shadow !== Companion) + return (shadow as V) to false + + if (observed === Companion || !compare(observed as V, get)) { + observed = copy(get) + listeners.accept(get) + return get to true + } + } + } + + return get to false + } + + private companion object + } + + class Shadow(parent: Supplier) : AbstractShadow(parent) { + override fun compare(a: V, b: V): Boolean { + return a == b + } + + override fun copy(value: V): V { + return value + } + } + + class SmartShadow(parent: Supplier, private val comparator: (V, V) -> Boolean, private val copy: (V) -> V) : AbstractShadow(parent) { + override fun compare(a: V, b: V): Boolean { + return comparator(a, b) + } + + override fun copy(value: V): V { + return copy.invoke(value) + } + } + + companion object { + /** + * Returns smart box if [getter] or [setter] is not passthrough + */ + @JvmStatic + fun maskSmart(value: V, getter: DelegateGetter, setter: DelegateSetter): ListenableDelegate { + if (getter === DelegateGetter.passthrough() && setter === DelegateSetter.passthrough()) { + return Box(value) + } else { + return SmartBox(value, getter, setter) + } + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kommons/util/Observer.kt b/src/main/kotlin/ru/dbotthepony/kommons/util/Observer.kt new file mode 100644 index 0000000..7ed8444 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kommons/util/Observer.kt @@ -0,0 +1,11 @@ +package ru.dbotthepony.kommons.util + +/** + * Observes upstream value for changes when [observe] is called + */ +interface Observer { + /** + * Returns true if value changed since last observation. + */ + fun observe(): Boolean +} diff --git a/src/main/kotlin/ru/dbotthepony/kommons/core/Struct.kt b/src/main/kotlin/ru/dbotthepony/kommons/util/Struct.kt similarity index 97% rename from src/main/kotlin/ru/dbotthepony/kommons/core/Struct.kt rename to src/main/kotlin/ru/dbotthepony/kommons/util/Struct.kt index 1062cf2..65ecaff 100644 --- a/src/main/kotlin/ru/dbotthepony/kommons/core/Struct.kt +++ b/src/main/kotlin/ru/dbotthepony/kommons/util/Struct.kt @@ -1,7 +1,7 @@ @file:Suppress("unused") -package ru.dbotthepony.kommons.core +package ru.dbotthepony.kommons.util interface IStruct2f { operator fun component1(): Float diff --git a/src/main/kotlin/ru/dbotthepony/kommons/util/ValueObserver.kt b/src/main/kotlin/ru/dbotthepony/kommons/util/ValueObserver.kt new file mode 100644 index 0000000..c533d0a --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kommons/util/ValueObserver.kt @@ -0,0 +1,157 @@ +package ru.dbotthepony.kommons.util + +import java.util.concurrent.locks.ReentrantLock +import java.util.function.Consumer +import java.util.function.Supplier +import kotlin.concurrent.withLock + +interface ValueObserver : Listenable, Supplier, Observer { + fun getAndObserve(): Pair + + /** + * Notifies listeners when value returned by [provider] changes. + * + * Observation happen on [get] call, or on [observe]/[getAndObserve]. + * + * This implementation is not synchronized, calls to [get]/[observe]/[getAndObserve] from multiple threads + * will result in undefined behavior; + * For thread-safe implementation, use [SynchronizedObserved]. + */ + class Impl(private val provider: Supplier) : ValueObserver { + private val subs = Listenable.Impl() + private var observed: Any? = Companion + + override fun addListener(listener: Consumer): Listenable.L { + return subs.addListener(listener) + } + + /** + * Returns last observed value or observes it if it were never observed before + */ + fun getObserved(): V { + if (observed === Companion) { + return get() + } + + return observed as V + } + + override fun get(): V { + val get = provider.get() + + if (get != observed) { + observed = get + subs.accept(get) + } + + return get + } + + override fun observe(): Boolean { + val get = provider.get() + + if (get != observed) { + observed = get + subs.accept(get) + return true + } + + return false + } + + /** + * Returns pair representing value and if value changed since last observation. + */ + override fun getAndObserve(): Pair { + val get = provider.get() + + if (get != observed) { + observed = get + subs.accept(get) + return get to true + } + + return get to false + } + + private companion object + } + + /** + * Notifies listeners when value returned by [provider] changes. + * + * Observation happen on [get] call, or on [observe]/[getAndObserve]. + */ + class SynchronizedImpl(private val provider: Supplier) : ValueObserver { + private val subs = Listenable.Impl() + private val lock = ReentrantLock() // reentrant lock to avoid deadlock when doing observe -> listeners triggered -> listeners call observe + private var observed: Any? = Companion + + override fun addListener(listener: Consumer): Listenable.L { + return subs.addListener(listener) + } + + /** + * Returns last observed value or observes it if it were never observed before + */ + fun getObserved(): V { + if (observed === Companion) { + return get() + } + + return observed as V + } + + override fun get(): V { + val get = provider.get() + + if (get != observed) { + lock.withLock { + if (get != observed) { + observed = get + subs.accept(get) + } + } + } + + return get + } + + override fun observe(): Boolean { + val get = provider.get() + + if (get != observed) { + lock.withLock { + if (get != observed) { + observed = get + subs.accept(get) + return true + } + } + } + + return false + } + + /** + * Returns pair representing value and if value changed since last observation. + */ + override fun getAndObserve(): Pair { + val get = provider.get() + + if (get != observed) { + lock.withLock { + if (get != observed) { + observed = get + subs.accept(get) + return get to true + } + } + } + + return get to false + } + + private companion object + } +}