diff --git a/silk-core/src/main/kotlin/net/silkmc/silk/core/math/vector/VectorOperations.kt b/silk-core/src/main/kotlin/net/silkmc/silk/core/math/vector/VectorOperations.kt index bbc351dd..6c037ef4 100644 --- a/silk-core/src/main/kotlin/net/silkmc/silk/core/math/vector/VectorOperations.kt +++ b/silk-core/src/main/kotlin/net/silkmc/silk/core/math/vector/VectorOperations.kt @@ -1,5 +1,6 @@ package net.silkmc.silk.core.math.vector +import com.mojang.math.Vector3d import com.mojang.math.Vector3f import net.minecraft.core.Vec3i import net.minecraft.world.phys.Vec3 @@ -18,6 +19,9 @@ operator fun Vec3.times(vec: Vec3): Vec3 = multiply(vec) operator fun Vec3.div(vec: Vec3): Vec3 = Vec3(x / vec.x, y / vec.y, z / vec.z) operator fun Vec3.compareTo(vec: Vec3) = lengthSqr().compareTo(vec.lengthSqr()) +fun Vec3.toTriple() = Triple(x, y, z) +fun Vec3(x: Number, y: Number, z: Number) = Vec3(x.toDouble(), y.toDouble(), z.toDouble()) + operator fun Vec3.component1() = x operator fun Vec3.component2() = y operator fun Vec3.component3() = z @@ -25,9 +29,15 @@ operator fun Vec3.component3() = z operator fun Vector3f.not(): Vector3f = times(-1f) -operator fun Vector3f.plus(n: Number): Vector3f = copy().apply { n.toFloat().let { nFloat -> add(nFloat, nFloat, nFloat) } } -operator fun Vector3f.minus(n: Number): Vector3f = copy().apply { n.toFloat().let { nFloat -> sub(Vector3f(nFloat, nFloat, nFloat)) } } -operator fun Vector3f.times(n: Number): Vector3f = n.toFloat().let { nFloat -> Vector3f(x() * nFloat, y() * nFloat, z() * nFloat) } +operator fun Vector3f.plus(n: Number): Vector3f = + copy().apply { n.toFloat().let { nFloat -> add(nFloat, nFloat, nFloat) } } + +operator fun Vector3f.minus(n: Number): Vector3f = + copy().apply { sub(Vector3f(n, n, n)) } + +operator fun Vector3f.times(n: Number): Vector3f = + n.toFloat().let { nFloat -> Vector3f(x() * nFloat, y() * nFloat, z() * nFloat) } + operator fun Vector3f.div(n: Number): Vector3f = times(1.0 / n.toDouble()) operator fun Vector3f.compareTo(n: Number) = Vec3(this).length().compareTo(n.toDouble()) @@ -37,6 +47,16 @@ operator fun Vector3f.times(vec: Vector3f): Vector3f = Vector3f(x() * vec.x(), y operator fun Vector3f.div(vec: Vector3f): Vector3f = Vector3f(x() / vec.x(), y() / vec.y(), z() / vec.z()) operator fun Vector3f.compareTo(vec: Vector3f) = Vec3(this).lengthSqr().compareTo(Vec3(vec).lengthSqr()) +fun Vector3f.toTriple() = Triple(x, y, z) +fun Vector3f(x: Number, y: Number, z: Number) = Vector3f(x.toFloat(), y.toFloat(), z.toFloat()) + +val Vector3f.x: Float + get() = x() +val Vector3f.y: Float + get() = y() +val Vector3f.z: Float + get() = z() + operator fun Vector3f.component1() = x() operator fun Vector3f.component2() = y() operator fun Vector3f.component3() = z() @@ -45,17 +65,56 @@ operator fun Vector3f.component3() = z() operator fun Vec3i.not(): Vec3i = times(-1) inline operator fun T.plus(n: Number): T = n.toInt().let { nInt -> offset(nInt, nInt, nInt) } as T -inline operator fun T.minus(n: Number): T = (-n.toInt()).let { nInt -> offset(nInt, nInt, nInt) } as T +inline operator fun T.minus(n: Number): T = + (-n.toInt()).let { nInt -> offset(nInt, nInt, nInt) } as T + operator fun Vec3i.times(n: Number): Vec3i = multiply(n.toInt()) operator fun Vec3i.div(n: Number) = (1.0 / n.toDouble()).let { nDouble -> Vec3i(x * nDouble, y * nDouble, z * nDouble) } -operator fun Vec3i.compareTo(n: Number) = Vec3(x.toDouble(), y.toDouble(), z.toDouble()).length().compareTo(n.toDouble()) +operator fun Vec3i.compareTo(n: Number) = Vec3(x, y, z).length().compareTo(n.toDouble()) inline operator fun T.plus(vec: Vec3i): T = offset(vec) as T inline operator fun T.minus(vec: Vec3i): T = offset(!vec) as T operator fun Vec3i.times(vec: Vec3i): Vec3i = Vec3i(x * vec.x, y * vec.y, z * vec.z) operator fun Vec3i.div(vec: Vec3i): Vec3i = Vec3i(x / vec.x, y / vec.y, z / vec.z) -operator fun Vec3i.compareTo(vec: Vec3i) = Vec3(x.toDouble(), y.toDouble(), z.toDouble()).lengthSqr().compareTo(Vec3(vec.x.toDouble(), vec.y.toDouble(), vec.z.toDouble()).lengthSqr()) +operator fun Vec3i.compareTo(vec: Vec3i) = + Vec3(x, y, z).lengthSqr().compareTo(Vec3(vec.x, vec.y, vec.z).lengthSqr()) + +fun Vec3i.toTriple() = Triple(x, y, z) +fun Vec3i(x: Number, y: Number, z: Number) = Vec3i(x.toDouble(), y.toDouble(), z.toDouble()) operator fun Vec3i.component1() = x operator fun Vec3i.component2() = y operator fun Vec3i.component3() = z + +operator fun Vector3d.not(): Vector3d = times(-1f) + +operator fun Vector3d.plus(n: Number): Vector3d = + copy().apply { n.toDouble().let { set(it + x, it + y, it + z) } } + +operator fun Vector3d.minus(n: Number): Vector3d = + copy().apply { n.toDouble().let { set(x - it, y - it, z - it) } } + +operator fun Vector3d.times(n: Number): Vector3d = copy().apply { n.toDouble().let { times -> scale(times) } } +operator fun Vector3d.div(n: Number): Vector3d = times(1.0 / n.toDouble()) +operator fun Vector3d.compareTo(n: Number) = Vec3(x, y, z).length().compareTo(n.toDouble()) + +operator fun Vector3d.plus(vec: Vector3d): Vector3d = copy().apply { add(vec) } +operator fun Vector3d.minus(vec: Vector3d): Vector3d = Vector3d(x - vec.x, y - vec.y, z - vec.z) +operator fun Vector3d.times(vec: Vector3d): Vector3d = Vector3d(x * vec.x, y * vec.y, z * vec.z) +operator fun Vector3d.div(vec: Vector3d): Vector3d = Vector3d(x / vec.x, y / vec.y, z / vec.z) +operator fun Vector3d.compareTo(vec: Vector3d) = + Vec3(x, y, z).lengthSqr().compareTo(Vec3(vec.x, vec.y, vec.z).lengthSqr()) + +fun Vector3d.copy() = Vector3d(x, y, z) +fun Vector3d.toTriple() = Triple(x, y, z) +fun Vector3d(x: Number, y: Number, z: Number) = Vector3d(x.toDouble(), y.toDouble(), z.toDouble()) + +operator fun Vector3d.component1() = x +operator fun Vector3d.component2() = y +operator fun Vector3d.component3() = z + + +fun Triple.toVec3() = Vec3(first, second, third) +fun Triple.toVec3i() = Vec3i(first, second, third) +fun Triple.toVector3f() = Vector3f(first, second, third) +fun Triple.toVector3d() = Vector3d(first, second, third) \ No newline at end of file diff --git a/silk-core/src/main/kotlin/net/silkmc/silk/core/serialization/SilkDelegateSerializer.kt b/silk-core/src/main/kotlin/net/silkmc/silk/core/serialization/SilkDelegateSerializer.kt new file mode 100644 index 00000000..3f7a8693 --- /dev/null +++ b/silk-core/src/main/kotlin/net/silkmc/silk/core/serialization/SilkDelegateSerializer.kt @@ -0,0 +1,24 @@ +package net.silkmc.silk.core.serialization + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder + +abstract class SilkDelegateSerializer( + private val delegateSerializer: KSerializer, + private val convertTo: T.() -> D, + private val convertFrom: D.() -> T, +) : KSerializer { + + override val descriptor: SerialDescriptor + get() = delegateSerializer.descriptor + + override fun deserialize(decoder: Decoder): T { + return decoder.decodeSerializableValue(delegateSerializer).convertFrom() + } + + override fun serialize(encoder: Encoder, value: T) { + encoder.encodeSerializableValue(delegateSerializer, value.convertTo()) + } +} diff --git a/silk-core/src/main/kotlin/net/silkmc/silk/core/serialization/SilkSerializersModule.kt b/silk-core/src/main/kotlin/net/silkmc/silk/core/serialization/SilkSerializersModule.kt new file mode 100644 index 00000000..b5f6b829 --- /dev/null +++ b/silk-core/src/main/kotlin/net/silkmc/silk/core/serialization/SilkSerializersModule.kt @@ -0,0 +1,25 @@ +package net.silkmc.silk.core.serialization + +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.cbor.Cbor +import kotlinx.serialization.json.Json +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.contextual +import net.silkmc.silk.core.serialization.serializers.Vec3iSerializer +import net.silkmc.silk.core.serialization.serializers.Vector3dSerializer +import net.silkmc.silk.core.serialization.serializers.Vector3fSerializer + +val silkSerializersModule = SerializersModule { + contextual(Vec3iSerializer) + contextual(Vector3fSerializer) + contextual(Vector3dSerializer) +} + +val SilkJson = Json { + serializersModule = silkSerializersModule +} + +@OptIn(ExperimentalSerializationApi::class) +val SilkCbor = Cbor { + serializersModule = silkSerializersModule +} diff --git a/silk-core/src/main/kotlin/net/silkmc/silk/core/serialization/serializers/BlockPosSerializer.kt b/silk-core/src/main/kotlin/net/silkmc/silk/core/serialization/serializers/BlockPosSerializer.kt new file mode 100644 index 00000000..e4300356 --- /dev/null +++ b/silk-core/src/main/kotlin/net/silkmc/silk/core/serialization/serializers/BlockPosSerializer.kt @@ -0,0 +1,22 @@ +package net.silkmc.silk.core.serialization.serializers + +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import net.minecraft.core.Vec3i +import net.minecraft.core.BlockPos +import net.silkmc.silk.core.serialization.SilkSerializer + +/** + * Use vector serializers for structure. [BlockPos] is a [Vec3i] + */ +@ExperimentalSerializationApi +object BlockPosSerializer : SilkSerializer() { + override val descriptor = PrimitiveSerialDescriptor(descriptorName, PrimitiveKind.LONG) + + override fun deserialize(decoder: Decoder) = BlockPos.of(decoder.decodeLong())!! + + override fun serialize(encoder: Encoder, value: BlockPos) = encoder.encodeLong(value.asLong()) +} \ No newline at end of file diff --git a/silk-core/src/main/kotlin/net/silkmc/silk/core/serialization/serializers/VectorSerializer.kt b/silk-core/src/main/kotlin/net/silkmc/silk/core/serialization/serializers/VectorSerializer.kt new file mode 100644 index 00000000..6b71c8ae --- /dev/null +++ b/silk-core/src/main/kotlin/net/silkmc/silk/core/serialization/serializers/VectorSerializer.kt @@ -0,0 +1,18 @@ +package net.silkmc.silk.core.serialization.serializers + +import com.mojang.math.Vector3d +import com.mojang.math.Vector3f +import net.minecraft.core.Vec3i +import net.silkmc.silk.core.serialization.SilkDelegateSerializer +import net.silkmc.silk.core.world.pos.Pos3d +import net.silkmc.silk.core.world.pos.Pos3f +import net.silkmc.silk.core.world.pos.Pos3i + +object Vec3iSerializer : + SilkDelegateSerializer(Pos3i.serializer(), ::Pos3i, Pos3i::toMcVec3i) + +object Vector3fSerializer : + SilkDelegateSerializer(Pos3f.serializer(), ::Pos3f, Pos3f::toMcVector3f) + +object Vector3dSerializer : + SilkDelegateSerializer(Pos3d.serializer(), ::Pos3d, Pos3d::toMcVector3d) diff --git a/silk-core/src/main/kotlin/net/silkmc/silk/core/world/pos/BasicPositions.kt b/silk-core/src/main/kotlin/net/silkmc/silk/core/world/pos/BasicPositions.kt index 2ccd7e3c..00caaab5 100644 --- a/silk-core/src/main/kotlin/net/silkmc/silk/core/world/pos/BasicPositions.kt +++ b/silk-core/src/main/kotlin/net/silkmc/silk/core/world/pos/BasicPositions.kt @@ -150,6 +150,8 @@ data class Pos2d(override val x: Double, override val z: Double) : Pos2Dimension @Serializable @SerialName("pos3i") data class Pos3i(override val x: Int, override val y: Int, override val z: Int) : Pos3DimensionalConvertible { + constructor(vec3i: Vec3i) : this(vec3i.x, vec3i.y, vec3i.z) + override fun atY(y: Int) = Pos3i(x, y, z) override fun withoutHeight() = Pos2i(x, z) } @@ -161,6 +163,8 @@ data class Pos3i(override val x: Int, override val y: Int, override val z: Int) @Serializable @SerialName("pos3f") data class Pos3f(override val x: Float, override val y: Float, override val z: Float) : Pos3DimensionalConvertible { + constructor(vector3f: Vector3f) : this(vector3f.x(), vector3f.y(), vector3f.z()) + override fun atY(y: Float) = Pos3f(x, y, z) override fun withoutHeight() = Pos2f(x, z) } @@ -172,6 +176,8 @@ data class Pos3f(override val x: Float, override val y: Float, override val z: F @Serializable @SerialName("pos3d") data class Pos3d(override val x: Double, override val y: Double, override val z: Double) : Pos3DimensionalConvertible { + constructor(vector3d: Vector3d) : this(vector3d.x, vector3d.y, vector3d.z) + override fun atY(y: Double) = Pos3d(x, y, z) override fun withoutHeight() = Pos2d(x, z) } diff --git a/silk-core/src/test/kotlin/net/silkmc/silk/core/test/serialization/VectorSerialization.kt b/silk-core/src/test/kotlin/net/silkmc/silk/core/test/serialization/VectorSerialization.kt new file mode 100644 index 00000000..e7e48d9e --- /dev/null +++ b/silk-core/src/test/kotlin/net/silkmc/silk/core/test/serialization/VectorSerialization.kt @@ -0,0 +1,17 @@ +package net.silkmc.silk.core.test.serialization + +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe +import kotlinx.serialization.encodeToString +import net.silkmc.silk.core.math.vector.Vec3i +import net.silkmc.silk.core.serialization.SilkJson + +class VectorSerialization : FunSpec({ + val json = SilkJson + + context("serialize 3d vectors") { + test("blockpos") { + json.encodeToString(Vec3i(1, 2, 3)) shouldBe """{"x":1,"y":2,"z":3}""" + } + } +})