Added specialized Vector3 as an example
Added a specialized Vector3 serializer Fixed issues relating to the differences between header bits and data bits Fixed issues with deserializing data with mismatched sizes compared to the surrogate data Added WriteBuffer and ReadBuffer for simpler serialization and deserialization Added annotation to prevent superclass serialization
This commit is contained in:
parent
60cebfb966
commit
2af938169c
@ -1,3 +1,5 @@
|
|||||||
|
import net.tofvesson.math.MathSerializer;
|
||||||
|
import net.tofvesson.math.Vector3;
|
||||||
import net.tofvesson.networking.*;
|
import net.tofvesson.networking.*;
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
@ -25,10 +27,13 @@ public class Main {
|
|||||||
@SyncedVar
|
@SyncedVar
|
||||||
public static DiffTrackedArray<Long> tracker2 = new DiffTrackedArray<>(Long.class, 8, i -> (long)i);
|
public static DiffTrackedArray<Long> tracker2 = new DiffTrackedArray<>(Long.class, 8, i -> (long)i);
|
||||||
|
|
||||||
|
@SyncedVar({MathSerializer.flagLowCompressionRotation, MathSerializer.flagPassiveCompress})
|
||||||
|
public static Vector3 lookDirection = new Vector3(60, 90, 80);
|
||||||
|
|
||||||
public static void main(String[] args){
|
public static void main(String[] args){
|
||||||
Main testObject = new Main();
|
Main testObject = new Main();
|
||||||
SyncHandler.Companion.registerSerializer(DiffTrackedSerializer.Companion.getSingleton());
|
|
||||||
|
|
||||||
|
SyncHandler.Companion.registerSerializer(MathSerializer.Companion.getSingleton());
|
||||||
SyncHandler sync = new SyncHandler();
|
SyncHandler sync = new SyncHandler();
|
||||||
sync.registerSyncObject(testObject);
|
sync.registerSyncObject(testObject);
|
||||||
sync.registerSyncObject(Main.class);
|
sync.registerSyncObject(Main.class);
|
||||||
@ -40,9 +45,11 @@ public class Main {
|
|||||||
tracker.setValue(9);
|
tracker.setValue(9);
|
||||||
tracker2.set(3L, 2);
|
tracker2.set(3L, 2);
|
||||||
tracker2.set(5L, 0);
|
tracker2.set(5L, 0);
|
||||||
|
lookDirection.setX(355);
|
||||||
|
lookDirection.setZ(0);
|
||||||
|
|
||||||
// Generate snapshot of values to serialize
|
// Generate snapshot of values to serialize
|
||||||
byte[] ser = sync.serialize();
|
byte[] ser = sync.serialize().array();
|
||||||
|
|
||||||
System.out.print("Created and serialized snapshot of field values:\n\t"+
|
System.out.print("Created and serialized snapshot of field values:\n\t"+
|
||||||
testObject.syncTest+"\n\t"+
|
testObject.syncTest+"\n\t"+
|
||||||
@ -52,7 +59,8 @@ public class Main {
|
|||||||
testbool1+"\n\t"+
|
testbool1+"\n\t"+
|
||||||
test[0]+"\n\t"+
|
test[0]+"\n\t"+
|
||||||
test[1]+"\n\t"+
|
test[1]+"\n\t"+
|
||||||
tracker
|
tracker+"\n\t"+
|
||||||
|
lookDirection
|
||||||
);
|
);
|
||||||
for(Long value : tracker2.getValues())
|
for(Long value : tracker2.getValues())
|
||||||
System.out.print("\n\t"+value);
|
System.out.print("\n\t"+value);
|
||||||
@ -64,13 +72,13 @@ public class Main {
|
|||||||
value = 9.0f;
|
value = 9.0f;
|
||||||
testObject.testbool = true;
|
testObject.testbool = true;
|
||||||
testbool1 = false;
|
testbool1 = false;
|
||||||
test = new boolean[3];
|
|
||||||
test[0] = false;
|
test[0] = false;
|
||||||
test[1] = true;
|
test[1] = true;
|
||||||
test[2] = true;
|
|
||||||
tracker.setValue(400);
|
tracker.setValue(400);
|
||||||
tracker2.set(8L, 2);
|
|
||||||
tracker2.set(100L, 0);
|
tracker2.set(100L, 0);
|
||||||
|
tracker2.set(8L, 2);
|
||||||
|
lookDirection.setX(200);
|
||||||
|
lookDirection.setZ(360);
|
||||||
|
|
||||||
System.out.print("Set a new state of test values:\n\t"+
|
System.out.print("Set a new state of test values:\n\t"+
|
||||||
testObject.syncTest+"\n\t"+
|
testObject.syncTest+"\n\t"+
|
||||||
@ -80,8 +88,8 @@ public class Main {
|
|||||||
testbool1+"\n\t"+
|
testbool1+"\n\t"+
|
||||||
test[0]+"\n\t"+
|
test[0]+"\n\t"+
|
||||||
test[1]+"\n\t"+
|
test[1]+"\n\t"+
|
||||||
test[2]+"\n\t"+
|
tracker+"\n\t"+
|
||||||
tracker
|
lookDirection
|
||||||
);
|
);
|
||||||
for(Long value : tracker2.getValues())
|
for(Long value : tracker2.getValues())
|
||||||
System.out.print("\n\t"+value);
|
System.out.print("\n\t"+value);
|
||||||
@ -101,7 +109,8 @@ public class Main {
|
|||||||
testbool1+"\n\t"+
|
testbool1+"\n\t"+
|
||||||
test[0]+"\n\t"+
|
test[0]+"\n\t"+
|
||||||
test[1]+"\n\t"+
|
test[1]+"\n\t"+
|
||||||
tracker);
|
tracker+"\n\t"+
|
||||||
|
lookDirection);
|
||||||
for(Long value : tracker2.getValues())
|
for(Long value : tracker2.getValues())
|
||||||
System.out.print("\n\t"+value);
|
System.out.print("\n\t"+value);
|
||||||
System.out.println("\n\nSnapshot size: "+ser.length+" bytes");
|
System.out.println("\n\nSnapshot size: "+ser.length+" bytes");
|
||||||
|
13
src/net/tofvesson/math/Extensions.kt
Normal file
13
src/net/tofvesson/math/Extensions.kt
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package net.tofvesson.math
|
||||||
|
|
||||||
|
fun Boolean.toNumber() = if(this) 1 else 0
|
||||||
|
fun Number.toBoolean() = this!=0
|
||||||
|
|
||||||
|
fun Int.collapseLowerByte(): Int =
|
||||||
|
((this ushr 7) or
|
||||||
|
(this ushr 6) or
|
||||||
|
(this ushr 5) or
|
||||||
|
(this ushr 4) or
|
||||||
|
(this ushr 3) or
|
||||||
|
(this ushr 2) or
|
||||||
|
(this ushr 1)) and 1
|
193
src/net/tofvesson/math/MathSerializer.kt
Normal file
193
src/net/tofvesson/math/MathSerializer.kt
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
package net.tofvesson.math
|
||||||
|
|
||||||
|
import net.tofvesson.networking.*
|
||||||
|
import net.tofvesson.reflect.access
|
||||||
|
import java.lang.reflect.Field
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST", "MemberVisibilityCanBePrivate")
|
||||||
|
class MathSerializer: Serializer(arrayOf(
|
||||||
|
Vector3::class.java
|
||||||
|
)){
|
||||||
|
companion object {
|
||||||
|
val singleton = MathSerializer()
|
||||||
|
const val flagHighCompressionRotation = "rotCompress1"
|
||||||
|
const val flagMidCompressionRotation = "rotCompress2"
|
||||||
|
const val flagLowCompressionRotation = "rotCompress3"
|
||||||
|
const val flagPassiveCompress = "passiveCompress"
|
||||||
|
private val compressedRotationVector1 = SyncFlag.createFlag(flagHighCompressionRotation)
|
||||||
|
private val compressedRotationVector2 = SyncFlag.createFlag(flagMidCompressionRotation)
|
||||||
|
private val compressedRotationVector3 = SyncFlag.createFlag(flagLowCompressionRotation)
|
||||||
|
private val passiveCompress = SyncFlag.createFlag(flagPassiveCompress)
|
||||||
|
|
||||||
|
private val xField = Vector3::class.java.getDeclaredField("_x").access()
|
||||||
|
private val yField = Vector3::class.java.getDeclaredField("_y").access()
|
||||||
|
private val zField = Vector3::class.java.getDeclaredField("_z").access()
|
||||||
|
|
||||||
|
private val diffValue = DiffTracked::class.java.getDeclaredField("_value")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun computeSizeExplicit(field: Field, flags: Array<out SyncFlag>, owner: Any?, state: WriteState, fieldType: Class<*>) {
|
||||||
|
when (fieldType) {
|
||||||
|
Vector3::class.java -> {
|
||||||
|
val vector = field.access().get(owner) as Vector3
|
||||||
|
val xDiff = xField.get(vector) as DiffTracked<Float>
|
||||||
|
val yDiff = yField.get(vector) as DiffTracked<Float>
|
||||||
|
val zDiff = zField.get(vector) as DiffTracked<Float>
|
||||||
|
val c1 = flags.contains(compressedRotationVector1)
|
||||||
|
val c2 = flags.contains(compressedRotationVector2)
|
||||||
|
val c3 = flags.contains(compressedRotationVector3)
|
||||||
|
if ((c1 and c2) or (c2 and c3) or (c1 and c3)) throw MismatchedFlagException("Cannot have more than one rotation compression flag!")
|
||||||
|
|
||||||
|
state.registerHeader(3)
|
||||||
|
when {
|
||||||
|
c1 || c2 || c3 -> {
|
||||||
|
val bytes =
|
||||||
|
when {
|
||||||
|
c1 -> 1
|
||||||
|
c2 -> 2
|
||||||
|
else -> 3
|
||||||
|
}
|
||||||
|
state
|
||||||
|
.registerBytes(if (xDiff.hasChanged()) varIntSize(xDiff.value.encodeRotation(bytes).toLong()) else 0)
|
||||||
|
.registerBytes(if (yDiff.hasChanged()) varIntSize(yDiff.value.encodeRotation(bytes).toLong()) else 0)
|
||||||
|
.registerBytes(if (zDiff.hasChanged()) varIntSize(zDiff.value.encodeRotation(bytes).toLong()) else 0)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val floatSerializer = SyncHandler.getCompatibleSerializer(Float::class.java)
|
||||||
|
if (xDiff.hasChanged()) floatSerializer.computeSizeExplicit(diffValue, flags, xDiff, state, Float::class.java)
|
||||||
|
if (yDiff.hasChanged()) floatSerializer.computeSizeExplicit(diffValue, flags, yDiff, state, Float::class.java)
|
||||||
|
if (zDiff.hasChanged()) floatSerializer.computeSizeExplicit(diffValue, flags, zDiff, state, Float::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throwInvalidType(fieldType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serializeExplicit(field: Field, _flags: Array<out SyncFlag>, owner: Any?, writeBuffer: WriteBuffer, fieldType: Class<*>) {
|
||||||
|
when (fieldType) {
|
||||||
|
Vector3::class.java -> {
|
||||||
|
var flags = _flags
|
||||||
|
val vector = field.access().get(owner) as Vector3
|
||||||
|
val c1 = flags.contains(compressedRotationVector1)
|
||||||
|
val c2 = flags.contains(compressedRotationVector2)
|
||||||
|
val c3 = flags.contains(compressedRotationVector3)
|
||||||
|
val xDiff = xField.get(vector) as DiffTracked<Float>
|
||||||
|
val yDiff = yField.get(vector) as DiffTracked<Float>
|
||||||
|
val zDiff = zField.get(vector) as DiffTracked<Float>
|
||||||
|
|
||||||
|
if ((c1 and c2) or (c2 and c3) or (c1 and c3)) throw MismatchedFlagException("Cannot have more than one rotation compression flag!")
|
||||||
|
|
||||||
|
writeBuffer.writeHeader(xDiff.hasChanged())
|
||||||
|
writeBuffer.writeHeader(yDiff.hasChanged())
|
||||||
|
writeBuffer.writeHeader(zDiff.hasChanged())
|
||||||
|
|
||||||
|
when {
|
||||||
|
c1 || c2 || c3 -> {
|
||||||
|
val intSerializer = SyncHandler.getCompatibleSerializer(Int::class.java)
|
||||||
|
val bytes =
|
||||||
|
when {
|
||||||
|
c1 -> 1
|
||||||
|
c2 -> 2
|
||||||
|
else -> 3
|
||||||
|
}
|
||||||
|
|
||||||
|
val xHolder = Holder(vector.x.encodeRotation(bytes))
|
||||||
|
val yHolder = Holder(vector.y.encodeRotation(bytes))
|
||||||
|
val zHolder = Holder(vector.z.encodeRotation(bytes))
|
||||||
|
|
||||||
|
val nn = flags.contains(SyncFlag.NonNegative)
|
||||||
|
val pc = flags.contains(passiveCompress)
|
||||||
|
val resize = nn.toNumber() + pc.toNumber()
|
||||||
|
if(nn || pc){
|
||||||
|
var track = 0
|
||||||
|
flags = Array(flags.size - resize){
|
||||||
|
if(flags[track]==SyncFlag.NonNegative && nn){
|
||||||
|
if(flags[++track]==SyncFlag.NoCompress && pc)
|
||||||
|
flags[++track]
|
||||||
|
else flags[track]
|
||||||
|
}
|
||||||
|
else if(flags[track]==SyncFlag.NoCompress && nn){
|
||||||
|
if(flags[++track]==SyncFlag.NonNegative && pc)
|
||||||
|
flags[++track]
|
||||||
|
else flags[track]
|
||||||
|
}else flags[track]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xDiff.hasChanged()) intSerializer.serializeExplicit(Holder.valueField, flags, xHolder, writeBuffer, Int::class.java)
|
||||||
|
if (yDiff.hasChanged()) intSerializer.serializeExplicit(Holder.valueField, flags, yHolder, writeBuffer, Int::class.java)
|
||||||
|
if (zDiff.hasChanged()) intSerializer.serializeExplicit(Holder.valueField, flags, zHolder, writeBuffer, Int::class.java)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val floatSerializer = SyncHandler.getCompatibleSerializer(Float::class.java)
|
||||||
|
|
||||||
|
if (xDiff.hasChanged()) floatSerializer.serializeExplicit(diffValue, _flags, xDiff, writeBuffer, Float::class.java)
|
||||||
|
if (yDiff.hasChanged()) floatSerializer.serializeExplicit(diffValue, _flags, yDiff, writeBuffer, Float::class.java)
|
||||||
|
if (zDiff.hasChanged()) floatSerializer.serializeExplicit(diffValue, _flags, zDiff, writeBuffer, Float::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throwInvalidType(fieldType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserializeExplicit(field: Field, flags: Array<out SyncFlag>, owner: Any?, readBuffer: ReadBuffer, fieldType: Class<*>) {
|
||||||
|
when (fieldType) {
|
||||||
|
Vector3::class.java -> {
|
||||||
|
val vector = field.access().get(owner) as Vector3
|
||||||
|
val c1 = flags.contains(compressedRotationVector1)
|
||||||
|
val c2 = flags.contains(compressedRotationVector2)
|
||||||
|
val c3 = flags.contains(compressedRotationVector3)
|
||||||
|
val xHolder = Holder(null)
|
||||||
|
val yHolder = Holder(null)
|
||||||
|
val zHolder = Holder(null)
|
||||||
|
val xDiff = xField.get(vector) as DiffTracked<Float>
|
||||||
|
val yDiff = yField.get(vector) as DiffTracked<Float>
|
||||||
|
val zDiff = zField.get(vector) as DiffTracked<Float>
|
||||||
|
|
||||||
|
if ((c1 and c2) or (c2 and c3) or (c1 and c3)) throw MismatchedFlagException("Cannot have more than one rotation compression flag!")
|
||||||
|
|
||||||
|
when {
|
||||||
|
c1 || c2 || c3 -> {
|
||||||
|
val intSerializer = SyncHandler.getCompatibleSerializer(Int::class.java)
|
||||||
|
val bytes = if (c1) 1 else if (c2) 2 else 3
|
||||||
|
|
||||||
|
if (readBuffer.readHeader()) {
|
||||||
|
intSerializer.deserializeExplicit(Holder.valueField, flags, xHolder, readBuffer, Int::class.java)
|
||||||
|
xDiff.value = (xHolder.value as Int).decodeRotation(bytes)
|
||||||
|
xDiff.clearChangeState()
|
||||||
|
}
|
||||||
|
if (readBuffer.readHeader()) {
|
||||||
|
intSerializer.deserializeExplicit(Holder.valueField, flags, yHolder, readBuffer, Int::class.java)
|
||||||
|
yDiff.value = (yHolder.value as Int).decodeRotation(bytes)
|
||||||
|
yDiff.clearChangeState()
|
||||||
|
}
|
||||||
|
if (readBuffer.readHeader()) {
|
||||||
|
intSerializer.deserializeExplicit(Holder.valueField, flags, zHolder, readBuffer, Int::class.java)
|
||||||
|
zDiff.value = (zHolder.value as Int).decodeRotation(bytes)
|
||||||
|
zDiff.clearChangeState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val floatSerializer = SyncHandler.getCompatibleSerializer(Float::class.java)
|
||||||
|
|
||||||
|
if (readBuffer.readHeader()){
|
||||||
|
floatSerializer.deserializeExplicit(diffValue, flags, xField.get(vector), readBuffer, Float::class.java)
|
||||||
|
xDiff.clearChangeState()
|
||||||
|
}
|
||||||
|
if (readBuffer.readHeader()){
|
||||||
|
floatSerializer.deserializeExplicit(diffValue, flags, yField.get(vector), readBuffer, Float::class.java)
|
||||||
|
yDiff.clearChangeState()
|
||||||
|
}
|
||||||
|
if (readBuffer.readHeader()){
|
||||||
|
floatSerializer.deserializeExplicit(diffValue, flags, zField.get(vector), readBuffer, Float::class.java)
|
||||||
|
zDiff.clearChangeState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throwInvalidType(fieldType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
src/net/tofvesson/math/Vector3.kt
Normal file
29
src/net/tofvesson/math/Vector3.kt
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package net.tofvesson.math
|
||||||
|
|
||||||
|
import net.tofvesson.networking.DiffTracked
|
||||||
|
|
||||||
|
class Vector3(xPos: Float, yPos: Float, zPos: Float) {
|
||||||
|
|
||||||
|
private val _x = DiffTracked(xPos, Float::class.java)
|
||||||
|
private val _y = DiffTracked(yPos, Float::class.java)
|
||||||
|
private val _z = DiffTracked(zPos, Float::class.java)
|
||||||
|
|
||||||
|
|
||||||
|
var x: Float
|
||||||
|
get() = _x.value
|
||||||
|
set(value) {
|
||||||
|
_x.value = value
|
||||||
|
}
|
||||||
|
var y: Float
|
||||||
|
get() = _y.value
|
||||||
|
set(value) {
|
||||||
|
_y.value = value
|
||||||
|
}
|
||||||
|
var z: Float
|
||||||
|
get() = _z.value
|
||||||
|
set(value) {
|
||||||
|
_z.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString() = "($x; $y; $z)"
|
||||||
|
}
|
@ -2,6 +2,7 @@ package net.tofvesson.networking
|
|||||||
|
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import kotlin.experimental.or
|
import kotlin.experimental.or
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
fun varIntSize(value: Long): Int =
|
fun varIntSize(value: Long): Int =
|
||||||
when {
|
when {
|
||||||
@ -158,4 +159,29 @@ fun swapEndian(value: Long) =
|
|||||||
fun bitConvert(value: Int): Long = value.toLong() and 0xFFFFFFFFL
|
fun bitConvert(value: Int): Long = value.toLong() and 0xFFFFFFFFL
|
||||||
|
|
||||||
fun zigZagEncode(value: Long): Long = (value shl 1) xor (value shr 63)
|
fun zigZagEncode(value: Long): Long = (value shl 1) xor (value shr 63)
|
||||||
fun zigZagDecode(value: Long): Long = (value shr 1) xor ((value shl 63) shr 63)
|
fun zigZagDecode(value: Long): Long = (value shr 1) xor ((value shl 63) shr 63)
|
||||||
|
|
||||||
|
fun Float.encodeRotation(byteCount: Int): Int {
|
||||||
|
if(this < 0 || this > 360) throw RotationOutOfBoundsException()
|
||||||
|
if(byteCount<0) throw IllegalArgumentException("Cannot encode rotation with a negative amount of bytes")
|
||||||
|
if(byteCount>4) throw IllegalArgumentException("Cannot encode rotation with more bytes than in the original value")
|
||||||
|
if(byteCount==4) return this.roundToInt()
|
||||||
|
return ((this/360f)*(-1 ushr (8*(4-byteCount)))).roundToInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Int.decodeRotation(byteCount: Int): Float {
|
||||||
|
if(byteCount<0) throw IllegalArgumentException("Cannot decode rotation with a negative amount of bytes")
|
||||||
|
if(byteCount>4) throw IllegalArgumentException("Cannot decode rotation with more bytes than in the original value")
|
||||||
|
if(byteCount==4) return this.toFloat()
|
||||||
|
val mask = (-1 ushr (8*(4-byteCount)))
|
||||||
|
if(this < 0 || this > mask) throw RotationOutOfBoundsException()
|
||||||
|
return (this/mask.toFloat())*360f
|
||||||
|
}
|
||||||
|
|
||||||
|
class RotationOutOfBoundsException: RuntimeException {
|
||||||
|
constructor() : super()
|
||||||
|
constructor(message: String?) : super(message)
|
||||||
|
constructor(message: String?, cause: Throwable?) : super(message, cause)
|
||||||
|
constructor(cause: Throwable?) : super(cause)
|
||||||
|
constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super(message, cause, enableSuppression, writableStackTrace)
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
package net.tofvesson.networking
|
package net.tofvesson.networking
|
||||||
|
|
||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
import java.nio.ByteBuffer
|
|
||||||
|
|
||||||
class DiffTrackedSerializer private constructor(): Serializer(arrayOf(
|
class DiffTrackedSerializer private constructor(): Serializer(arrayOf(
|
||||||
DiffTracked::class.java,
|
DiffTracked::class.java,
|
||||||
@ -9,121 +8,95 @@ class DiffTrackedSerializer private constructor(): Serializer(arrayOf(
|
|||||||
)) {
|
)) {
|
||||||
companion object {
|
companion object {
|
||||||
private val trackedField = DiffTracked::class.java.getDeclaredField("_value")
|
private val trackedField = DiffTracked::class.java.getDeclaredField("_value")
|
||||||
private val holderValue = Holder::class.java.getDeclaredField("value")
|
|
||||||
val singleton = DiffTrackedSerializer()
|
val singleton = DiffTrackedSerializer()
|
||||||
}
|
}
|
||||||
override fun computeSizeExplicit(field: Field, flags: Array<out SyncFlag>, owner: Any?, fieldType: Class<*>): Pair<Int, Int> =
|
override fun computeSizeExplicit(field: Field, flags: Array<out SyncFlag>, owner: Any?, state: WriteState, fieldType: Class<*>) {
|
||||||
when(fieldType) {
|
when (fieldType) {
|
||||||
DiffTracked::class.java -> {
|
DiffTracked::class.java -> {
|
||||||
val tracker = field.get(owner) as DiffTracked<*>
|
val tracker = field.get(owner) as DiffTracked<*>
|
||||||
val serializer = SyncHandler.getCompatibleSerializer(tracker.valueType)
|
val serializer = SyncHandler.getCompatibleSerializer(tracker.valueType)
|
||||||
if(tracker.hasChanged()){
|
state.registerHeader(1)
|
||||||
val result = serializer.computeSizeExplicit(trackedField, flags, tracker, tracker.valueType)
|
if (tracker.hasChanged())
|
||||||
Pair(result.first, result.second+1)
|
serializer.computeSizeExplicit(trackedField, flags, tracker, state, tracker.valueType)
|
||||||
}else Pair(0, 1)
|
|
||||||
}
|
|
||||||
DiffTrackedArray::class.java -> {
|
|
||||||
val tracker = field.get(owner) as DiffTrackedArray<*>
|
|
||||||
val serializer = SyncHandler.getCompatibleSerializer(tracker.elementType)
|
|
||||||
if(tracker.hasChanged()){
|
|
||||||
var bits = 0
|
|
||||||
var bytes = 0
|
|
||||||
val holder = Holder(null)
|
|
||||||
|
|
||||||
for(index in tracker.changeMap.indices)
|
|
||||||
if(tracker.changeMap[index]){
|
|
||||||
holder.value = tracker[index]
|
|
||||||
val result = serializer.computeSizeExplicit(holderValue, flags, holder, tracker.elementType)
|
|
||||||
bytes += result.first
|
|
||||||
bits += result.second
|
|
||||||
}
|
|
||||||
Pair(bytes, bits+tracker.size)
|
|
||||||
}else Pair(0, tracker.size)
|
|
||||||
}
|
|
||||||
else -> Pair(0, 0)
|
|
||||||
}
|
}
|
||||||
|
DiffTrackedArray::class.java -> {
|
||||||
override fun serializeExplicit(field: Field, flags: Array<out SyncFlag>, owner: Any?, byteBuffer: ByteBuffer, offset: Int, bitFieldOffset: Int, fieldType: Class<*>): Pair<Int, Int> =
|
val tracker = field.get(owner) as DiffTrackedArray<*>
|
||||||
when(fieldType) {
|
val serializer = SyncHandler.getCompatibleSerializer(tracker.elementType)
|
||||||
DiffTracked::class.java -> {
|
state.registerHeader(1)
|
||||||
val tracker = field.get(owner) as DiffTracked<*>
|
if (tracker.hasChanged()) {
|
||||||
val serializer = SyncHandler.getCompatibleSerializer(tracker.valueType)
|
val holder = Holder(null)
|
||||||
writeBit(tracker.hasChanged(), byteBuffer, bitFieldOffset)
|
state.registerHeader(tracker.size)
|
||||||
if(tracker.hasChanged()){
|
for (index in tracker.changeMap.indices)
|
||||||
val result = serializer.serializeExplicit(trackedField, flags, tracker, byteBuffer, offset, bitFieldOffset+1, tracker.valueType)
|
if (tracker.changeMap[index]) {
|
||||||
tracker.clearChangeState()
|
holder.value = tracker[index]
|
||||||
Pair(result.first, result.second)
|
serializer.computeSizeExplicit(Holder.valueField, flags, holder, state, tracker.elementType)
|
||||||
}else{
|
|
||||||
tracker.clearChangeState()
|
|
||||||
Pair(offset, bitFieldOffset+1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DiffTrackedArray::class.java -> {
|
|
||||||
val tracker = field.get(owner) as DiffTrackedArray<*>
|
|
||||||
val serializer = SyncHandler.getCompatibleSerializer(tracker.elementType)
|
|
||||||
if(tracker.hasChanged()){
|
|
||||||
var bits = bitFieldOffset
|
|
||||||
var bytes = offset
|
|
||||||
val holder = Holder(null)
|
|
||||||
|
|
||||||
for(index in tracker.changeMap.indices) {
|
|
||||||
writeBit(tracker.changeMap[index], byteBuffer, bits++)
|
|
||||||
if (tracker.changeMap[index]) {
|
|
||||||
holder.value = tracker[index]
|
|
||||||
val result = serializer.serializeExplicit(holderValue, flags, holder, byteBuffer, bytes, bits, tracker.elementType)
|
|
||||||
bytes = result.first
|
|
||||||
bits = result.second
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
tracker.clearChangeState()
|
|
||||||
Pair(bytes, bits)
|
|
||||||
}else{
|
|
||||||
tracker.clearChangeState()
|
|
||||||
Pair(offset, bitFieldOffset+tracker.size)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else -> Pair(0, 0)
|
|
||||||
}
|
}
|
||||||
|
else -> throwInvalidType(fieldType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun deserializeExplicit(field: Field, flags: Array<out SyncFlag>, owner: Any?, byteBuffer: ByteBuffer, offset: Int, bitFieldOffset: Int, fieldType: Class<*>): Pair<Int, Int> =
|
override fun serializeExplicit(field: Field, flags: Array<out SyncFlag>, owner: Any?, writeBuffer: WriteBuffer, fieldType: Class<*>) {
|
||||||
when(fieldType) {
|
when (fieldType) {
|
||||||
DiffTracked::class.java -> {
|
DiffTracked::class.java -> {
|
||||||
val tracker = field.get(owner) as DiffTracked<*>
|
val tracker = field.get(owner) as DiffTracked<*>
|
||||||
val serializer = SyncHandler.getCompatibleSerializer(tracker.valueType)
|
val serializer = SyncHandler.getCompatibleSerializer(tracker.valueType)
|
||||||
var bytes = offset
|
writeBuffer.writeHeader(tracker.hasChanged())
|
||||||
var bits = bitFieldOffset
|
if (tracker.hasChanged()) {
|
||||||
if(readBit(byteBuffer, bits++)){
|
serializer.serializeExplicit(trackedField, flags, tracker, writeBuffer, tracker.valueType)
|
||||||
val result = serializer.deserializeExplicit(trackedField, flags, tracker, byteBuffer, bytes, bits, tracker.valueType)
|
tracker.clearChangeState()
|
||||||
bytes = result.first
|
}
|
||||||
bits = result.second
|
}
|
||||||
|
DiffTrackedArray::class.java -> {
|
||||||
|
val tracker = field.get(owner) as DiffTrackedArray<*>
|
||||||
|
val serializer = SyncHandler.getCompatibleSerializer(tracker.elementType)
|
||||||
|
writeBuffer.writeHeader(tracker.hasChanged())
|
||||||
|
if (tracker.hasChanged()) {
|
||||||
|
val holder = Holder(null)
|
||||||
|
|
||||||
|
for (index in tracker.changeMap.indices) {
|
||||||
|
writeBuffer.writeHeader(tracker.changeMap[index])
|
||||||
|
if (tracker.changeMap[index]) {
|
||||||
|
holder.value = tracker[index]
|
||||||
|
serializer.serializeExplicit(Holder.valueField, flags, holder, writeBuffer, tracker.elementType)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
tracker.clearChangeState()
|
tracker.clearChangeState()
|
||||||
Pair(bytes, bits)
|
|
||||||
}
|
}
|
||||||
DiffTrackedArray::class.java -> {
|
}
|
||||||
val tracker = field.get(owner) as DiffTrackedArray<*>
|
else -> throwInvalidType(fieldType)
|
||||||
val serializer = SyncHandler.getCompatibleSerializer(tracker.elementType)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var bits = bitFieldOffset
|
override fun deserializeExplicit(field: Field, flags: Array<out SyncFlag>, owner: Any?, readBuffer: ReadBuffer, fieldType: Class<*>) {
|
||||||
var bytes = offset
|
when (fieldType) {
|
||||||
|
DiffTracked::class.java -> {
|
||||||
|
val tracker = field.get(owner) as DiffTracked<*>
|
||||||
|
val serializer = SyncHandler.getCompatibleSerializer(tracker.valueType)
|
||||||
|
if (readBuffer.readHeader())
|
||||||
|
serializer.deserializeExplicit(trackedField, flags, tracker, readBuffer, tracker.valueType)
|
||||||
|
tracker.clearChangeState()
|
||||||
|
}
|
||||||
|
DiffTrackedArray::class.java -> {
|
||||||
|
val tracker = field.get(owner) as DiffTrackedArray<*>
|
||||||
|
val serializer = SyncHandler.getCompatibleSerializer(tracker.elementType)
|
||||||
|
|
||||||
|
if(readBuffer.readHeader()) {
|
||||||
val holder = Holder(null)
|
val holder = Holder(null)
|
||||||
|
|
||||||
val array = tracker.values as Array<Any?>
|
val array = tracker.values as Array<Any?>
|
||||||
|
|
||||||
for(index in tracker.changeMap.indices){
|
for (index in tracker.changeMap.indices) {
|
||||||
if(readBit(byteBuffer, bits++)){
|
if (readBuffer.readHeader()) {
|
||||||
holder.value = tracker[index]
|
serializer.deserializeExplicit(Holder.valueField, flags, holder, readBuffer, tracker.elementType)
|
||||||
val result = serializer.deserializeExplicit(holderValue, flags, holder, byteBuffer, bytes, bits, tracker.elementType)
|
|
||||||
bytes = result.first
|
|
||||||
bits = result.second
|
|
||||||
array[index] = holder.value
|
array[index] = holder.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tracker.clearChangeState()
|
|
||||||
Pair(bytes, bits)
|
|
||||||
}
|
}
|
||||||
else -> Pair(0, 0)
|
tracker.clearChangeState()
|
||||||
}
|
}
|
||||||
|
else -> throwInvalidType(fieldType)
|
||||||
private data class Holder(var value: Any?)
|
}
|
||||||
|
}
|
||||||
}
|
}
|
7
src/net/tofvesson/networking/Holder.kt
Normal file
7
src/net/tofvesson/networking/Holder.kt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package net.tofvesson.networking
|
||||||
|
|
||||||
|
data class Holder(var value: Any?){
|
||||||
|
companion object {
|
||||||
|
val valueField = Holder::class.java.getDeclaredField("value")!!
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package net.tofvesson.networking
|
||||||
|
|
||||||
|
class InsufficientCapacityException: RuntimeException {
|
||||||
|
constructor() : super()
|
||||||
|
constructor(message: String?) : super(message)
|
||||||
|
constructor(message: String?, cause: Throwable?) : super(message, cause)
|
||||||
|
constructor(cause: Throwable?) : super(cause)
|
||||||
|
constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super(message, cause, enableSuppression, writableStackTrace)
|
||||||
|
}
|
9
src/net/tofvesson/networking/MismatchedFlagException.kt
Normal file
9
src/net/tofvesson/networking/MismatchedFlagException.kt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package net.tofvesson.networking
|
||||||
|
|
||||||
|
class MismatchedFlagException: RuntimeException {
|
||||||
|
constructor() : super()
|
||||||
|
constructor(message: String?) : super(message)
|
||||||
|
constructor(message: String?, cause: Throwable?) : super(message, cause)
|
||||||
|
constructor(cause: Throwable?) : super(cause)
|
||||||
|
constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super(message, cause, enableSuppression, writableStackTrace)
|
||||||
|
}
|
6
src/net/tofvesson/networking/NoUpwardCascade.kt
Normal file
6
src/net/tofvesson/networking/NoUpwardCascade.kt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package net.tofvesson.networking
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevents SyncedVar system from syncing superclasses
|
||||||
|
*/
|
||||||
|
annotation class NoUpwardCascade
|
@ -1,8 +1,8 @@
|
|||||||
package net.tofvesson.networking
|
package net.tofvesson.networking
|
||||||
|
|
||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
import java.nio.ByteBuffer
|
|
||||||
|
|
||||||
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
class PrimitiveArraySerializer private constructor(): Serializer(arrayOf(
|
class PrimitiveArraySerializer private constructor(): Serializer(arrayOf(
|
||||||
BooleanArray::class.java,
|
BooleanArray::class.java,
|
||||||
ByteArray::class.java,
|
ByteArray::class.java,
|
||||||
@ -18,166 +18,136 @@ class PrimitiveArraySerializer private constructor(): Serializer(arrayOf(
|
|||||||
val singleton = PrimitiveArraySerializer()
|
val singleton = PrimitiveArraySerializer()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun computeSizeExplicit(field: Field, flags: Array<out SyncFlag>, owner: Any?, fieldType: Class<*>): Pair<Int, Int> {
|
override fun computeSizeExplicit(field: Field, flags: Array<out SyncFlag>, owner: Any?, state: WriteState, fieldType: Class<*>) {
|
||||||
val arrayLength = java.lang.reflect.Array.getLength(field.get(owner))
|
val arrayLength = java.lang.reflect.Array.getLength(field.get(owner))
|
||||||
var byteSize = if(flags.contains(knownSize)) 0 else varIntSize(arrayLength.toLong())
|
val holder = Holder(null)
|
||||||
var bitSize = 0
|
|
||||||
when (fieldType) {
|
when (fieldType) {
|
||||||
BooleanArray::class.java -> bitSize = arrayLength
|
BooleanArray::class.java -> state.registerBits(arrayLength)
|
||||||
ByteArray::class.java -> byteSize += arrayLength
|
ByteArray::class.java -> state.registerBytes(arrayLength)
|
||||||
ShortArray::class.java ->
|
ShortArray::class.java ->
|
||||||
if(flags.contains(SyncFlag.NoCompress)) byteSize += arrayLength * 2
|
if(flags.contains(SyncFlag.NoCompress)) state.registerBytes(arrayLength * 2)
|
||||||
else
|
else {
|
||||||
for(value in field.get(owner) as ShortArray)
|
val shortSerializer = SyncHandler.getCompatibleSerializer(Short::class.java)
|
||||||
byteSize +=
|
for (value in field.get(owner) as ShortArray) {
|
||||||
varIntSize(
|
holder.value = value
|
||||||
if(flags.contains(SyncFlag.NonNegative)) value.toLong()
|
shortSerializer.computeSizeExplicit(Holder.valueField, flags, holder, state, Short::class.java)
|
||||||
else zigZagEncode(value.toLong())
|
}
|
||||||
)
|
}
|
||||||
IntArray::class.java ->
|
IntArray::class.java ->
|
||||||
if(flags.contains(SyncFlag.NoCompress)) byteSize += arrayLength * 4
|
if(flags.contains(SyncFlag.NoCompress)) state.registerBytes(arrayLength * 4)
|
||||||
else
|
else {
|
||||||
for(value in field.get(owner) as IntArray)
|
val intSerializer = SyncHandler.getCompatibleSerializer(Int::class.java)
|
||||||
byteSize +=
|
for (value in field.get(owner) as IntArray) {
|
||||||
varIntSize(
|
holder.value = value
|
||||||
if(flags.contains(SyncFlag.NonNegative)) value.toLong()
|
intSerializer.computeSizeExplicit(Holder.valueField, flags, holder, state, Int::class.java)
|
||||||
else zigZagEncode(value.toLong())
|
}
|
||||||
)
|
}
|
||||||
LongArray::class.java ->
|
LongArray::class.java ->
|
||||||
if(flags.contains(SyncFlag.NoCompress)) byteSize += arrayLength * 8
|
if(flags.contains(SyncFlag.NoCompress)) state.registerBytes(arrayLength * 8)
|
||||||
else
|
else {
|
||||||
for(value in field.get(owner) as LongArray)
|
val longSerializer = SyncHandler.getCompatibleSerializer(Long::class.java)
|
||||||
byteSize +=
|
for (value in field.get(owner) as LongArray) {
|
||||||
varIntSize(
|
holder.value = value
|
||||||
if(flags.contains(SyncFlag.NonNegative)) value
|
longSerializer.computeSizeExplicit(Holder.valueField, flags, holder, state, Long::class.java)
|
||||||
else zigZagEncode(value)
|
}
|
||||||
)
|
}
|
||||||
FloatArray::class.java ->
|
FloatArray::class.java ->
|
||||||
if(flags.contains(SyncFlag.NoCompress)) byteSize += arrayLength * 4
|
if(flags.contains(SyncFlag.NoCompress)) state.registerBytes(arrayLength * 4)
|
||||||
else
|
else {
|
||||||
for (value in field.get(owner) as LongArray)
|
val floatSerializer = SyncHandler.getCompatibleSerializer(Float::class.java)
|
||||||
byteSize +=
|
for (value in field.get(owner) as FloatArray) {
|
||||||
varIntSize(
|
holder.value = value
|
||||||
if(flags.contains(SyncFlag.FloatEndianSwap)) bitConvert(swapEndian(floatToInt(field.getFloat(owner))))
|
floatSerializer.computeSizeExplicit(Holder.valueField, flags, holder, state, Float::class.java)
|
||||||
else bitConvert(floatToInt(field.getFloat(owner)))
|
}
|
||||||
)
|
}
|
||||||
DoubleArray::class.java ->
|
DoubleArray::class.java ->
|
||||||
if(flags.contains(SyncFlag.NoCompress)) byteSize += arrayLength * 8
|
if(flags.contains(SyncFlag.NoCompress)) state.registerBytes(arrayLength * 8)
|
||||||
else
|
else {
|
||||||
for (value in field.get(owner) as LongArray)
|
val doubleSerializer = SyncHandler.getCompatibleSerializer(Double::class.java)
|
||||||
byteSize +=
|
for (value in field.get(owner) as DoubleArray) {
|
||||||
varIntSize(
|
holder.value = value
|
||||||
if(flags.contains(SyncFlag.FloatEndianSwap)) swapEndian(doubleToLong(field.getDouble(owner)))
|
doubleSerializer.computeSizeExplicit(Holder.valueField, flags, holder, state, Double::class.java)
|
||||||
else doubleToLong(field.getDouble(owner))
|
}
|
||||||
)
|
}
|
||||||
|
else -> throwInvalidType(fieldType)
|
||||||
}
|
}
|
||||||
return Pair(byteSize, bitSize)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun serializeExplicit(field: Field, flags: Array<out SyncFlag>, owner: Any?, byteBuffer: ByteBuffer, offset: Int, bitFieldOffset: Int, fieldType: Class<*>): Pair<Int, Int> {
|
override fun serializeExplicit(field: Field, flags: Array<out SyncFlag>, owner: Any?, writeBuffer: WriteBuffer, fieldType: Class<*>) {
|
||||||
val arrayLength = java.lang.reflect.Array.getLength(field.get(owner))
|
val arrayLength = java.lang.reflect.Array.getLength(field.get(owner))
|
||||||
var localByteOffset = offset
|
val holder = Holder(null)
|
||||||
var localBitOffset = bitFieldOffset
|
if(!flags.contains(knownSize)) writeBuffer.writePackedInt(arrayLength, true)
|
||||||
if(!flags.contains(knownSize)){
|
|
||||||
writeVarInt(byteBuffer, offset, arrayLength.toLong())
|
|
||||||
localByteOffset += varIntSize(arrayLength.toLong())
|
|
||||||
}
|
|
||||||
when (fieldType) {
|
when (fieldType) {
|
||||||
BooleanArray::class.java ->
|
BooleanArray::class.java ->
|
||||||
for(value in field.get(owner) as BooleanArray)
|
for(value in field.get(owner) as BooleanArray)
|
||||||
writeBit(value, byteBuffer, localBitOffset++)
|
writeBuffer.writeBit(value)
|
||||||
ByteArray::class.java ->
|
ByteArray::class.java ->
|
||||||
for(value in field.get(owner) as ByteArray)
|
for(value in field.get(owner) as ByteArray)
|
||||||
byteBuffer.put(localByteOffset++, value)
|
writeBuffer.writeByte(value)
|
||||||
ShortArray::class.java ->
|
ShortArray::class.java ->
|
||||||
if(flags.contains(SyncFlag.NoCompress))
|
if(flags.contains(SyncFlag.NoCompress))
|
||||||
for(value in field.get(owner) as ShortArray){
|
for(value in field.get(owner) as ShortArray)
|
||||||
byteBuffer.putShort(localByteOffset, value)
|
writeBuffer.writeShort(value)
|
||||||
localByteOffset += 2
|
else {
|
||||||
}
|
val shortSerializer = SyncHandler.getCompatibleSerializer(Short::class.java)
|
||||||
else
|
for (value in field.get(owner) as ShortArray) {
|
||||||
for(value in field.get(owner) as ShortArray) {
|
holder.value = value
|
||||||
val rawVal =
|
shortSerializer.serializeExplicit(Holder.valueField, flags, holder, writeBuffer, fieldType)
|
||||||
if(flags.contains(SyncFlag.NonNegative)) value.toLong()
|
|
||||||
else zigZagEncode(value.toLong())
|
|
||||||
writeVarInt(byteBuffer, localByteOffset, rawVal)
|
|
||||||
localByteOffset += varIntSize(rawVal)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
IntArray::class.java ->
|
IntArray::class.java ->
|
||||||
if(flags.contains(SyncFlag.NoCompress))
|
if(flags.contains(SyncFlag.NoCompress))
|
||||||
for(value in field.get(owner) as IntArray){
|
for(value in field.get(owner) as IntArray)
|
||||||
byteBuffer.putInt(localByteOffset, value)
|
writeBuffer.writeInt(value)
|
||||||
localByteOffset += 4
|
else {
|
||||||
}
|
val intSerializer = SyncHandler.getCompatibleSerializer(Int::class.java)
|
||||||
else
|
for (value in field.get(owner) as IntArray) {
|
||||||
for(value in field.get(owner) as IntArray) {
|
holder.value = value
|
||||||
val rawVal =
|
intSerializer.serializeExplicit(Holder.valueField, flags, holder, writeBuffer, fieldType)
|
||||||
if(flags.contains(SyncFlag.NonNegative)) value.toLong()
|
|
||||||
else zigZagEncode(value.toLong())
|
|
||||||
writeVarInt(byteBuffer, localByteOffset, rawVal)
|
|
||||||
localByteOffset += varIntSize(rawVal)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
LongArray::class.java ->
|
LongArray::class.java ->
|
||||||
if(flags.contains(SyncFlag.NoCompress))
|
if(flags.contains(SyncFlag.NoCompress))
|
||||||
for(value in field.get(owner) as LongArray){
|
for(value in field.get(owner) as LongArray)
|
||||||
byteBuffer.putLong(localByteOffset, value)
|
writeBuffer.writeLong(value)
|
||||||
localByteOffset += 8
|
else {
|
||||||
}
|
val longSerializer = SyncHandler.getCompatibleSerializer(Long::class.java)
|
||||||
else
|
for (value in field.get(owner) as LongArray) {
|
||||||
for(value in field.get(owner) as LongArray) {
|
holder.value = value
|
||||||
val rawVal =
|
longSerializer.serializeExplicit(Holder.valueField, flags, holder, writeBuffer, fieldType)
|
||||||
if(flags.contains(SyncFlag.NonNegative)) value
|
|
||||||
else zigZagEncode(value)
|
|
||||||
writeVarInt(
|
|
||||||
byteBuffer,
|
|
||||||
localByteOffset,
|
|
||||||
rawVal
|
|
||||||
)
|
|
||||||
localByteOffset += varIntSize(rawVal)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
FloatArray::class.java ->
|
FloatArray::class.java ->
|
||||||
if(flags.contains(SyncFlag.NoCompress))
|
if(flags.contains(SyncFlag.NoCompress))
|
||||||
for(value in field.get(owner) as FloatArray){
|
for(value in field.get(owner) as FloatArray)
|
||||||
byteBuffer.putFloat(localByteOffset, value)
|
writeBuffer.writeFloat(value)
|
||||||
localByteOffset += 4
|
else {
|
||||||
}
|
val floatSerializer = SyncHandler.getCompatibleSerializer(Float::class.java)
|
||||||
else
|
|
||||||
for (value in field.get(owner) as FloatArray) {
|
for (value in field.get(owner) as FloatArray) {
|
||||||
val rawVal =
|
holder.value = value
|
||||||
if(flags.contains(SyncFlag.FloatEndianSwap)) bitConvert(swapEndian(floatToInt(field.getFloat(owner))))
|
floatSerializer.serializeExplicit(Holder.valueField, flags, holder, writeBuffer, fieldType)
|
||||||
else bitConvert(floatToInt(field.getFloat(owner)))
|
|
||||||
writeVarInt(byteBuffer, localByteOffset, rawVal)
|
|
||||||
localByteOffset += varIntSize(rawVal)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
DoubleArray::class.java ->
|
DoubleArray::class.java ->
|
||||||
if(flags.contains(SyncFlag.NoCompress))
|
if(flags.contains(SyncFlag.NoCompress))
|
||||||
for(value in field.get(owner) as DoubleArray){
|
for(value in field.get(owner) as DoubleArray)
|
||||||
byteBuffer.putDouble(localByteOffset, value)
|
writeBuffer.writeDouble(value)
|
||||||
localByteOffset += 8
|
else {
|
||||||
}
|
val doubleSerializer = SyncHandler.getCompatibleSerializer(Double::class.java)
|
||||||
else
|
for (value in field.get(owner) as DoubleArray) {
|
||||||
for (value in field.get(owner) as DoubleArray){
|
holder.value = value
|
||||||
val rawVal =
|
doubleSerializer.serializeExplicit(Holder.valueField, flags, holder, writeBuffer, fieldType)
|
||||||
if(flags.contains(SyncFlag.FloatEndianSwap)) swapEndian(doubleToLong(field.getDouble(owner)))
|
|
||||||
else doubleToLong(field.getDouble(owner))
|
|
||||||
writeVarInt(byteBuffer, localByteOffset, rawVal)
|
|
||||||
localByteOffset += varIntSize(rawVal)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else -> throwInvalidType(fieldType)
|
||||||
}
|
}
|
||||||
return Pair(localByteOffset, localBitOffset)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deserializeExplicit(field: Field, flags: Array<out SyncFlag>, owner: Any?, byteBuffer: ByteBuffer, offset: Int, bitFieldOffset: Int, fieldType: Class<*>): Pair<Int, Int> {
|
override fun deserializeExplicit(field: Field, flags: Array<out SyncFlag>, owner: Any?, readBuffer: ReadBuffer, fieldType: Class<*>) {
|
||||||
var localByteOffset = offset
|
|
||||||
var localBitOffset = bitFieldOffset
|
|
||||||
val localLength = java.lang.reflect.Array.getLength(field.get(owner))
|
val localLength = java.lang.reflect.Array.getLength(field.get(owner))
|
||||||
val arrayLength =
|
val arrayLength =
|
||||||
if(flags.contains(knownSize)) localLength
|
if(flags.contains(knownSize)) localLength
|
||||||
else{
|
else readBuffer.readPackedInt(true)
|
||||||
val v = readVarInt(byteBuffer, offset)
|
|
||||||
localByteOffset += varIntSize(v)
|
|
||||||
v.toInt()
|
|
||||||
}
|
|
||||||
|
|
||||||
val target =
|
val target =
|
||||||
if(arrayLength!=localLength) java.lang.reflect.Array.newInstance(field.type.componentType, arrayLength)
|
if(arrayLength!=localLength) java.lang.reflect.Array.newInstance(field.type.componentType, arrayLength)
|
||||||
@ -187,97 +157,60 @@ class PrimitiveArraySerializer private constructor(): Serializer(arrayOf(
|
|||||||
BooleanArray::class.java -> {
|
BooleanArray::class.java -> {
|
||||||
val booleanTarget = target as BooleanArray
|
val booleanTarget = target as BooleanArray
|
||||||
for (index in 0 until arrayLength)
|
for (index in 0 until arrayLength)
|
||||||
booleanTarget[index] = readBit(byteBuffer, localBitOffset++)
|
booleanTarget[index] = readBuffer.readBit()
|
||||||
}
|
}
|
||||||
ByteArray::class.java -> {
|
ByteArray::class.java -> {
|
||||||
val byteTarget = target as ByteArray
|
val byteTarget = target as ByteArray
|
||||||
for (index in 0 until arrayLength)
|
for (index in 0 until arrayLength)
|
||||||
byteTarget[index] = byteBuffer[localByteOffset++]
|
byteTarget[index] = readBuffer.readByte()
|
||||||
}
|
}
|
||||||
ShortArray::class.java -> {
|
ShortArray::class.java -> {
|
||||||
val shortTarget = target as ShortArray
|
val shortTarget = target as ShortArray
|
||||||
if (flags.contains(SyncFlag.NoCompress))
|
if (flags.contains(SyncFlag.NoCompress))
|
||||||
for (index in 0 until arrayLength) {
|
for (index in 0 until arrayLength)
|
||||||
shortTarget[index] = byteBuffer.getShort(localByteOffset)
|
shortTarget[index] = readBuffer.readShort()
|
||||||
localByteOffset += 2
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
for (index in 0 until arrayLength) {
|
for (index in 0 until arrayLength)
|
||||||
val rawValue =
|
shortTarget[index] = readBuffer.readPackedShort(flags.contains(SyncFlag.NonNegative))
|
||||||
if(flags.contains(SyncFlag.NonNegative)) readVarInt(byteBuffer, offset)
|
|
||||||
else zigZagDecode(readVarInt(byteBuffer, offset))
|
|
||||||
shortTarget[index] = rawValue.toShort()
|
|
||||||
localByteOffset += varIntSize(rawValue)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
IntArray::class.java -> {
|
IntArray::class.java -> {
|
||||||
val intTarget = target as IntArray
|
val intTarget = target as IntArray
|
||||||
if (flags.contains(SyncFlag.NoCompress))
|
if (flags.contains(SyncFlag.NoCompress))
|
||||||
for (index in 0 until arrayLength) {
|
for (index in 0 until arrayLength)
|
||||||
intTarget[index] = byteBuffer.getInt(localByteOffset)
|
intTarget[index] = readBuffer.readInt()
|
||||||
localByteOffset += 4
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
for (index in 0 until arrayLength) {
|
for (index in 0 until arrayLength)
|
||||||
val rawValue =
|
intTarget[index] = readBuffer.readPackedInt(flags.contains(SyncFlag.NonNegative))
|
||||||
if(flags.contains(SyncFlag.NonNegative)) readVarInt(byteBuffer, offset)
|
|
||||||
else zigZagDecode(readVarInt(byteBuffer, offset))
|
|
||||||
intTarget[index] = rawValue.toInt()
|
|
||||||
localByteOffset += varIntSize(rawValue)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
LongArray::class.java -> {
|
LongArray::class.java -> {
|
||||||
val longTarget = target as LongArray
|
val longTarget = target as LongArray
|
||||||
if (flags.contains(SyncFlag.NoCompress))
|
if (flags.contains(SyncFlag.NoCompress))
|
||||||
for (index in 0 until arrayLength) {
|
for (index in 0 until arrayLength)
|
||||||
longTarget[index] = byteBuffer.getLong(localByteOffset)
|
longTarget[index] = readBuffer.readLong()
|
||||||
localByteOffset += 8
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
for (index in 0 until arrayLength) {
|
for (index in 0 until arrayLength)
|
||||||
val rawValue =
|
longTarget[index] = readBuffer.readPackedLong(flags.contains(SyncFlag.NonNegative))
|
||||||
if(flags.contains(SyncFlag.NonNegative)) readVarInt(byteBuffer, offset)
|
|
||||||
else zigZagDecode(readVarInt(byteBuffer, offset))
|
|
||||||
longTarget[index] = rawValue
|
|
||||||
localByteOffset += varIntSize(rawValue)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
FloatArray::class.java -> {
|
FloatArray::class.java -> {
|
||||||
val floatTarget = target as FloatArray
|
val floatTarget = target as FloatArray
|
||||||
if (flags.contains(SyncFlag.NoCompress))
|
if (flags.contains(SyncFlag.NoCompress))
|
||||||
for (index in 0 until arrayLength) {
|
for (index in 0 until arrayLength)
|
||||||
floatTarget[index] = byteBuffer.getFloat(localByteOffset)
|
floatTarget[index] = readBuffer.readFloat()
|
||||||
localByteOffset += 4
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
for (index in 0 until arrayLength) {
|
for (index in 0 until arrayLength)
|
||||||
val readVal = readVarInt(byteBuffer, offset)
|
floatTarget[index] = readBuffer.readPackedFloat(flags.contains(SyncFlag.NonNegative))
|
||||||
val rawValue =
|
|
||||||
if(flags.contains(SyncFlag.FloatEndianSwap)) intToFloat(swapEndian(readVal.toInt()))
|
|
||||||
else intToFloat(readVal.toInt())
|
|
||||||
floatTarget[index] = rawValue
|
|
||||||
localBitOffset += varIntSize(readVal)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
DoubleArray::class.java -> {
|
DoubleArray::class.java -> {
|
||||||
val doubleTarget = target as DoubleArray
|
val doubleTarget = target as DoubleArray
|
||||||
if (flags.contains(SyncFlag.NoCompress))
|
if (flags.contains(SyncFlag.NoCompress))
|
||||||
for (index in 0 until arrayLength) {
|
for (index in 0 until arrayLength)
|
||||||
doubleTarget[index] = byteBuffer.getDouble(localByteOffset)
|
doubleTarget[index] = readBuffer.readDouble()
|
||||||
localByteOffset += 8
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
for (index in 0 until arrayLength) {
|
for (index in 0 until arrayLength)
|
||||||
val readVal = readVarInt(byteBuffer, offset)
|
doubleTarget[index] = readBuffer.readPackedDouble(flags.contains(SyncFlag.NonNegative))
|
||||||
val rawValue =
|
|
||||||
if(flags.contains(SyncFlag.FloatEndianSwap)) longToDouble(swapEndian(readVal))
|
|
||||||
else longToDouble(readVal)
|
|
||||||
doubleTarget[index] = rawValue
|
|
||||||
localBitOffset += varIntSize(readVal)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else -> throwInvalidType(fieldType)
|
||||||
}
|
}
|
||||||
if(arrayLength!=localLength) field.set(owner, target)
|
if(arrayLength!=localLength) field.set(owner, target)
|
||||||
return Pair(localByteOffset, localBitOffset)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,7 +2,6 @@ package net.tofvesson.networking
|
|||||||
|
|
||||||
import net.tofvesson.reflect.*
|
import net.tofvesson.reflect.*
|
||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
import java.nio.ByteBuffer
|
|
||||||
|
|
||||||
class PrimitiveSerializer private constructor() : Serializer(arrayOf(
|
class PrimitiveSerializer private constructor() : Serializer(arrayOf(
|
||||||
Boolean::class.java,
|
Boolean::class.java,
|
||||||
@ -26,205 +25,100 @@ class PrimitiveSerializer private constructor() : Serializer(arrayOf(
|
|||||||
field: Field,
|
field: Field,
|
||||||
flags: Array<out SyncFlag>,
|
flags: Array<out SyncFlag>,
|
||||||
owner: Any?,
|
owner: Any?,
|
||||||
|
state: WriteState,
|
||||||
fieldType: Class<*>
|
fieldType: Class<*>
|
||||||
): Pair<Int, Int> =
|
) {
|
||||||
when(fieldType){
|
when (fieldType) {
|
||||||
java.lang.Boolean::class.java, Boolean::class.java -> Pair(0, 1)
|
java.lang.Boolean::class.java, Boolean::class.java -> state.registerBits(1)
|
||||||
java.lang.Byte::class.java, Byte::class.java -> Pair(1, 0)
|
java.lang.Byte::class.java, Byte::class.java -> state.registerBytes(1)
|
||||||
java.lang.Short::class.java, Short::class.java ->
|
java.lang.Short::class.java, Short::class.java ->
|
||||||
if(flags.contains(SyncFlag.NoCompress)) Pair(2, 0)
|
if (flags.contains(SyncFlag.NoCompress)) state.registerBytes(2)
|
||||||
else Pair(varIntSize(
|
else state.registerBytes(varIntSize(
|
||||||
if(flags.contains(SyncFlag.NonNegative)) field.getShortAdaptive(owner).toLong()
|
if (flags.contains(SyncFlag.NonNegative)) field.getShortAdaptive(owner).toLong()
|
||||||
else zigZagEncode(field.getShortAdaptive(owner).toLong())
|
else zigZagEncode(field.getShortAdaptive(owner).toLong())
|
||||||
), 0)
|
))
|
||||||
java.lang.Integer::class.java, Int::class.java ->
|
java.lang.Integer::class.java, Int::class.java ->
|
||||||
if(flags.contains(SyncFlag.NoCompress)) Pair(4, 0)
|
if (flags.contains(SyncFlag.NoCompress)) state.registerBytes(4)
|
||||||
else Pair(varIntSize(
|
else state.registerBytes(varIntSize(
|
||||||
if(flags.contains(SyncFlag.NonNegative)) field.getIntAdaptive(owner).toLong()
|
if (flags.contains(SyncFlag.NonNegative)) field.getIntAdaptive(owner).toLong()
|
||||||
else zigZagEncode(field.getIntAdaptive(owner).toLong())
|
else zigZagEncode(field.getIntAdaptive(owner).toLong())
|
||||||
), 0)
|
))
|
||||||
java.lang.Long::class.java, Long::class.java ->
|
java.lang.Long::class.java, Long::class.java ->
|
||||||
if(flags.contains(SyncFlag.NoCompress)) Pair(8, 0)
|
if (flags.contains(SyncFlag.NoCompress)) state.registerBytes(8)
|
||||||
else Pair(varIntSize(
|
else state.registerBytes(varIntSize(
|
||||||
if(flags.contains(SyncFlag.NonNegative)) field.getLongAdaptive(owner)
|
if (flags.contains(SyncFlag.NonNegative)) field.getLongAdaptive(owner)
|
||||||
else zigZagEncode(field.getLongAdaptive(owner))
|
else zigZagEncode(field.getLongAdaptive(owner))
|
||||||
), 0)
|
))
|
||||||
java.lang.Float::class.java, Float::class.java ->
|
java.lang.Float::class.java, Float::class.java ->
|
||||||
if(flags.contains(SyncFlag.NoCompress)) Pair(4, 0)
|
if (flags.contains(SyncFlag.NoCompress)) state.registerBytes(4)
|
||||||
else Pair(varIntSize(
|
else state.registerBytes(varIntSize(
|
||||||
if(flags.contains(SyncFlag.FloatEndianSwap)) bitConvert(swapEndian(floatToInt(field.getFloatAdaptive(owner))))
|
if (flags.contains(SyncFlag.FloatEndianSwap)) bitConvert(swapEndian(floatToInt(field.getFloatAdaptive(owner))))
|
||||||
else bitConvert(floatToInt(field.getFloatAdaptive(owner)))
|
else bitConvert(floatToInt(field.getFloatAdaptive(owner)))
|
||||||
), 0)
|
))
|
||||||
java.lang.Double::class.java, Double::class.java ->
|
java.lang.Double::class.java, Double::class.java ->
|
||||||
if(flags.contains(SyncFlag.NoCompress)) Pair(8, 0)
|
if (flags.contains(SyncFlag.NoCompress)) state.registerBytes(8)
|
||||||
else Pair(varIntSize(
|
else state.registerBytes(varIntSize(
|
||||||
if(flags.contains(SyncFlag.FloatEndianSwap)) swapEndian(doubleToLong(field.getDoubleAdaptive(owner)))
|
if (flags.contains(SyncFlag.FloatEndianSwap)) swapEndian(doubleToLong(field.getDoubleAdaptive(owner)))
|
||||||
else doubleToLong(field.getDoubleAdaptive(owner))
|
else doubleToLong(field.getDoubleAdaptive(owner))
|
||||||
), 0)
|
))
|
||||||
else -> Pair(0, 0)
|
else -> throwInvalidType(fieldType)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun serializeExplicit(
|
override fun serializeExplicit(
|
||||||
field: Field,
|
field: Field,
|
||||||
flags: Array<out SyncFlag>,
|
flags: Array<out SyncFlag>,
|
||||||
owner: Any?,
|
owner: Any?,
|
||||||
byteBuffer: ByteBuffer,
|
writeBuffer: WriteBuffer,
|
||||||
offset: Int,
|
|
||||||
bitFieldOffset: Int,
|
|
||||||
fieldType: Class<*>
|
fieldType: Class<*>
|
||||||
): Pair<Int, Int> =
|
){
|
||||||
when(fieldType){
|
when (fieldType) {
|
||||||
java.lang.Boolean::class.java, Boolean::class.java -> {
|
java.lang.Boolean::class.java, Boolean::class.java -> writeBuffer.writeBit(field.getBooleanAdaptive(owner))
|
||||||
writeBit(field.getBooleanAdaptive(owner), byteBuffer, bitFieldOffset)
|
java.lang.Byte::class.java, Byte::class.java -> writeBuffer.writeByte(field.getByteAdaptive(owner))
|
||||||
Pair(offset, bitFieldOffset+1)
|
java.lang.Short::class.java, Short::class.java ->
|
||||||
}
|
if (flags.contains(SyncFlag.NoCompress)) writeBuffer.writeShort(field.getShortAdaptive(owner))
|
||||||
java.lang.Byte::class.java, Byte::class.java -> {
|
else writeBuffer.writePackedShort(field.getShortAdaptive(owner), flags.contains(SyncFlag.NonNegative))
|
||||||
byteBuffer.put(offset, field.getByteAdaptive(owner))
|
java.lang.Integer::class.java, Int::class.java ->
|
||||||
Pair(offset+1, bitFieldOffset)
|
if (flags.contains(SyncFlag.NoCompress)) writeBuffer.writeInt(field.getIntAdaptive(owner))
|
||||||
}
|
else writeBuffer.writePackedInt(field.getIntAdaptive(owner), flags.contains(SyncFlag.NonNegative))
|
||||||
java.lang.Short::class.java, Short::class.java ->
|
java.lang.Long::class.java, Long::class.java ->
|
||||||
if(flags.contains(SyncFlag.NoCompress)){
|
if (flags.contains(SyncFlag.NoCompress)) writeBuffer.writeLong(field.getLongAdaptive(owner))
|
||||||
byteBuffer.putShort(offset, field.getShortAdaptive(owner))
|
else writeBuffer.writePackedLong(field.getLongAdaptive(owner), flags.contains(SyncFlag.NonNegative))
|
||||||
Pair(offset+2, bitFieldOffset)
|
java.lang.Float::class.java, Float::class.java ->
|
||||||
}
|
if (flags.contains(SyncFlag.NoCompress)) writeBuffer.writeFloat(field.getFloatAdaptive(owner))
|
||||||
else {
|
else writeBuffer.writePackedFloat(field.getFloatAdaptive(owner), flags.contains(SyncFlag.FloatEndianSwap))
|
||||||
val rawValue =
|
java.lang.Double::class.java, Double::class.java ->
|
||||||
if(flags.contains(SyncFlag.NonNegative)) field.getShortAdaptive(owner).toLong()
|
if (flags.contains(SyncFlag.NoCompress)) writeBuffer.writeDouble(field.getDoubleAdaptive(owner))
|
||||||
else zigZagEncode(field.getShortAdaptive(owner).toLong())
|
else writeBuffer.writePackedDouble(field.getDoubleAdaptive(owner), flags.contains(SyncFlag.FloatEndianSwap))
|
||||||
writeVarInt(byteBuffer, offset, rawValue)
|
else -> throwInvalidType(fieldType)
|
||||||
Pair(offset+varIntSize(rawValue), bitFieldOffset)
|
}
|
||||||
}
|
}
|
||||||
java.lang.Integer::class.java, Int::class.java ->
|
|
||||||
if(flags.contains(SyncFlag.NoCompress)){
|
|
||||||
byteBuffer.putInt(offset, field.getIntAdaptive(owner))
|
|
||||||
Pair(offset+4, bitFieldOffset)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val rawValue =
|
|
||||||
if(flags.contains(SyncFlag.NonNegative)) field.getIntAdaptive(owner).toLong()
|
|
||||||
else zigZagEncode(field.getIntAdaptive(owner).toLong())
|
|
||||||
writeVarInt(byteBuffer, offset, rawValue)
|
|
||||||
Pair(offset+varIntSize(rawValue), bitFieldOffset)
|
|
||||||
}
|
|
||||||
java.lang.Long::class.java, Long::class.java ->
|
|
||||||
if(flags.contains(SyncFlag.NoCompress)){
|
|
||||||
byteBuffer.putLong(offset, field.getLongAdaptive(owner))
|
|
||||||
Pair(offset+8, bitFieldOffset)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val rawValue =
|
|
||||||
if(flags.contains(SyncFlag.NonNegative)) field.getLongAdaptive(owner)
|
|
||||||
else zigZagEncode(field.getLongAdaptive(owner))
|
|
||||||
writeVarInt(byteBuffer, offset, rawValue)
|
|
||||||
Pair(offset+varIntSize(rawValue), bitFieldOffset)
|
|
||||||
}
|
|
||||||
java.lang.Float::class.java, Float::class.java ->
|
|
||||||
if(flags.contains(SyncFlag.NoCompress)){
|
|
||||||
byteBuffer.putFloat(offset, field.getFloatAdaptive(owner))
|
|
||||||
Pair(offset+4, bitFieldOffset)
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
val rawValue =
|
|
||||||
if(flags.contains(SyncFlag.FloatEndianSwap)) bitConvert(swapEndian(floatToInt(field.getFloatAdaptive(owner))))
|
|
||||||
else bitConvert(floatToInt(field.getFloatAdaptive(owner)))
|
|
||||||
writeVarInt(byteBuffer, offset, rawValue)
|
|
||||||
Pair(offset+varIntSize(rawValue), bitFieldOffset)
|
|
||||||
}
|
|
||||||
java.lang.Double::class.java, Double::class.java ->
|
|
||||||
if(flags.contains(SyncFlag.NoCompress)){
|
|
||||||
byteBuffer.putDouble(offset, field.getDoubleAdaptive(owner))
|
|
||||||
Pair(offset+8, bitFieldOffset)
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
val rawValue =
|
|
||||||
if(flags.contains(SyncFlag.FloatEndianSwap)) swapEndian(doubleToLong(field.getDoubleAdaptive(owner)))
|
|
||||||
else doubleToLong(field.getDoubleAdaptive(owner))
|
|
||||||
writeVarInt(byteBuffer, offset, rawValue)
|
|
||||||
Pair(offset+varIntSize(rawValue), bitFieldOffset)
|
|
||||||
}
|
|
||||||
else -> Pair(offset, bitFieldOffset)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun deserializeExplicit(
|
override fun deserializeExplicit(
|
||||||
field: Field,
|
field: Field,
|
||||||
flags: Array<out SyncFlag>,
|
flags: Array<out SyncFlag>,
|
||||||
owner: Any?,
|
owner: Any?,
|
||||||
byteBuffer: ByteBuffer,
|
readBuffer: ReadBuffer,
|
||||||
offset: Int,
|
|
||||||
bitFieldOffset: Int,
|
|
||||||
fieldType: Class<*>
|
fieldType: Class<*>
|
||||||
): Pair<Int, Int> =
|
) =
|
||||||
when(fieldType){
|
when(fieldType){
|
||||||
java.lang.Boolean::class.java, Boolean::class.java -> {
|
java.lang.Boolean::class.java, Boolean::class.java -> field.setBooleanAdaptive(owner, readBuffer.readBit())
|
||||||
field.setBooleanAdaptive(owner, readBit(byteBuffer, bitFieldOffset))
|
java.lang.Byte::class.java, Byte::class.java -> field.setByteAdaptive(owner, readBuffer.readByte())
|
||||||
Pair(offset, bitFieldOffset+1)
|
|
||||||
}
|
|
||||||
java.lang.Byte::class.java, Byte::class.java -> {
|
|
||||||
field.setByteAdaptive(owner, byteBuffer.get(offset))
|
|
||||||
Pair(offset+1, bitFieldOffset)
|
|
||||||
}
|
|
||||||
java.lang.Short::class.java, Short::class.java ->
|
java.lang.Short::class.java, Short::class.java ->
|
||||||
if(flags.contains(SyncFlag.NoCompress)){
|
if(flags.contains(SyncFlag.NoCompress)) field.setShortAdaptive(owner, readBuffer.readShort())
|
||||||
field.setShortAdaptive(owner, byteBuffer.getShort(offset))
|
else field.setShortAdaptive(owner, readBuffer.readPackedShort(flags.contains(SyncFlag.NonNegative)))
|
||||||
Pair(offset+2, bitFieldOffset)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val rawValue =
|
|
||||||
if(flags.contains(SyncFlag.NonNegative)) readVarInt(byteBuffer, offset)
|
|
||||||
else zigZagDecode(readVarInt(byteBuffer, offset))
|
|
||||||
field.setShortAdaptive(owner, rawValue.toShort())
|
|
||||||
Pair(offset+varIntSize(rawValue), bitFieldOffset)
|
|
||||||
}
|
|
||||||
java.lang.Integer::class.java, Int::class.java ->
|
java.lang.Integer::class.java, Int::class.java ->
|
||||||
if(flags.contains(SyncFlag.NoCompress)){
|
if(flags.contains(SyncFlag.NoCompress)) field.setIntAdaptive(owner, readBuffer.readInt())
|
||||||
field.setIntAdaptive(owner, byteBuffer.getInt(offset))
|
else field.setIntAdaptive(owner, readBuffer.readPackedInt(flags.contains(SyncFlag.NonNegative)))
|
||||||
Pair(offset+4, bitFieldOffset)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val rawValue =
|
|
||||||
if(flags.contains(SyncFlag.NonNegative)) readVarInt(byteBuffer, offset)
|
|
||||||
else zigZagDecode(readVarInt(byteBuffer, offset))
|
|
||||||
field.setIntAdaptive(owner, rawValue.toInt())
|
|
||||||
Pair(offset+varIntSize(rawValue), bitFieldOffset)
|
|
||||||
}
|
|
||||||
java.lang.Long::class.java, Long::class.java ->
|
java.lang.Long::class.java, Long::class.java ->
|
||||||
if(flags.contains(SyncFlag.NoCompress)){
|
if(flags.contains(SyncFlag.NoCompress)) field.setLongAdaptive(owner, readBuffer.readLong())
|
||||||
field.setLongAdaptive(owner, byteBuffer.getLong(offset))
|
else field.setLongAdaptive(owner, readBuffer.readPackedLong(flags.contains(SyncFlag.NonNegative)))
|
||||||
Pair(offset+8, bitFieldOffset)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val rawValue =
|
|
||||||
if(flags.contains(SyncFlag.NonNegative)) readVarInt(byteBuffer, offset)
|
|
||||||
else zigZagDecode(readVarInt(byteBuffer, offset))
|
|
||||||
field.setLongAdaptive(owner, rawValue)
|
|
||||||
Pair(offset+varIntSize(rawValue), bitFieldOffset)
|
|
||||||
}
|
|
||||||
java.lang.Float::class.java, Float::class.java ->
|
java.lang.Float::class.java, Float::class.java ->
|
||||||
if(flags.contains(SyncFlag.NoCompress)){
|
if(flags.contains(SyncFlag.NoCompress)) field.setFloatAdaptive(owner, readBuffer.readFloat())
|
||||||
field.setFloatAdaptive(owner, byteBuffer.getFloat(offset))
|
else field.setFloatAdaptive(owner, readBuffer.readPackedFloat(flags.contains(SyncFlag.FloatEndianSwap)))
|
||||||
Pair(offset+4, bitFieldOffset)
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
val readVal = readVarInt(byteBuffer, offset)
|
|
||||||
val rawValue =
|
|
||||||
if(flags.contains(SyncFlag.FloatEndianSwap)) intToFloat(swapEndian(readVal.toInt()))
|
|
||||||
else intToFloat(readVal.toInt())
|
|
||||||
field.setFloatAdaptive(owner, rawValue)
|
|
||||||
Pair(offset+varIntSize(readVal), bitFieldOffset)
|
|
||||||
}
|
|
||||||
java.lang.Double::class.java, Double::class.java ->
|
java.lang.Double::class.java, Double::class.java ->
|
||||||
if(flags.contains(SyncFlag.NoCompress)){
|
if(flags.contains(SyncFlag.NoCompress)) field.setDoubleAdaptive(owner, readBuffer.readDouble())
|
||||||
field.setDoubleAdaptive(owner, byteBuffer.getDouble(offset))
|
else field.setDoubleAdaptive(owner, readBuffer.readPackedDouble(flags.contains(SyncFlag.FloatEndianSwap)))
|
||||||
Pair(offset+8, bitFieldOffset)
|
else -> throwInvalidType(fieldType)
|
||||||
}
|
|
||||||
else{
|
|
||||||
val readVal = readVarInt(byteBuffer, offset)
|
|
||||||
val rawValue =
|
|
||||||
if(flags.contains(SyncFlag.FloatEndianSwap)) longToDouble(swapEndian(readVal))
|
|
||||||
else longToDouble(readVal)
|
|
||||||
field.setDoubleAdaptive(owner, rawValue)
|
|
||||||
Pair(offset+varIntSize(readVal), bitFieldOffset)
|
|
||||||
}
|
|
||||||
else -> Pair(offset, bitFieldOffset)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
129
src/net/tofvesson/networking/ReadBuffer.kt
Normal file
129
src/net/tofvesson/networking/ReadBuffer.kt
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
package net.tofvesson.networking
|
||||||
|
|
||||||
|
import net.tofvesson.math.collapseLowerByte
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
|
@Suppress("MemberVisibilityCanBePrivate", "unused")
|
||||||
|
class ReadBuffer(state: WriteState, private val _buffer: ByteBuffer, bufferBitOffset: Int = 0) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
Buffer layout:
|
||||||
|
{BufferBitOffset}[Header][Bits]{BitOffset}[Bytes]
|
||||||
|
BufferBitOffset is optional and usually set to 0
|
||||||
|
BitOffset is between 0 and 7 bits long (to realign data to the next free byte)
|
||||||
|
*/
|
||||||
|
private var headerIndex = bufferBitOffset
|
||||||
|
private var bitOffset = bufferBitOffset + state.header
|
||||||
|
private var byteIndex = state.computeBitFieldOffset(bufferBitOffset)
|
||||||
|
|
||||||
|
// Maximum size for the above values
|
||||||
|
private val maxHeaderSize = bufferBitOffset + state.header
|
||||||
|
private val maxBitOffset: Int
|
||||||
|
private val maxByteOffset = _buffer.capacity()
|
||||||
|
|
||||||
|
val buffer: ByteBuffer
|
||||||
|
get() = this._buffer
|
||||||
|
|
||||||
|
init {
|
||||||
|
if(_buffer.capacity() - (bufferBitOffset ushr 3) - bufferBitOffset.collapseLowerByte() < state.computeBitFieldOffset())
|
||||||
|
throw InsufficientCapacityException()
|
||||||
|
byteIndex = readPackedInt(true)
|
||||||
|
maxBitOffset = bufferBitOffset + byteIndex*8
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun readBit(head: Boolean): Boolean {
|
||||||
|
if(head && headerIndex >= maxHeaderSize)
|
||||||
|
throw IndexOutOfBoundsException("Attempt to read more headers than available space permits!")
|
||||||
|
|
||||||
|
val index = (if(head) headerIndex else bitOffset) ushr 3
|
||||||
|
val shift = (if(head) headerIndex else bitOffset) and 7
|
||||||
|
if(head) ++headerIndex
|
||||||
|
else ++bitOffset
|
||||||
|
return (_buffer[index].toInt() and (1 shl shift)) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readHeader() = readBit(true)
|
||||||
|
fun readBit() = readBit(false)
|
||||||
|
|
||||||
|
fun readByte(): Byte {
|
||||||
|
//doBoundaryCheck(1)
|
||||||
|
return _buffer.get(byteIndex++)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readShort(): Short {
|
||||||
|
//doBoundaryCheck(2)
|
||||||
|
val res = _buffer.getShort(byteIndex)
|
||||||
|
byteIndex += 2
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readInt(): Int {
|
||||||
|
//doBoundaryCheck(4)
|
||||||
|
val res = _buffer.getInt(byteIndex)
|
||||||
|
byteIndex += 4
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readLong(): Long {
|
||||||
|
//doBoundaryCheck(8)
|
||||||
|
val res = _buffer.getLong(byteIndex)
|
||||||
|
byteIndex += 8
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readFloat(): Float {
|
||||||
|
//doBoundaryCheck(4)
|
||||||
|
val res = _buffer.getFloat(byteIndex)
|
||||||
|
byteIndex += 4
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readDouble(): Double {
|
||||||
|
//doBoundaryCheck(8)
|
||||||
|
val res = _buffer.getDouble(byteIndex)
|
||||||
|
byteIndex += 8
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readPackedShort(noZigZag: Boolean = false) = readPackedLong(noZigZag).toShort()
|
||||||
|
fun readPackedInt(noZigZag: Boolean = false) = readPackedLong(noZigZag).toInt()
|
||||||
|
fun readPackedLong(noZigZag: Boolean = false): Long {
|
||||||
|
//doBoundaryCheck(1)
|
||||||
|
val header: Long = buffer[byteIndex++].toLong() and 0xFF
|
||||||
|
if (header <= 240L) return if(noZigZag) header else zigZagDecode(header)
|
||||||
|
if (header <= 248L){
|
||||||
|
//doBoundaryCheck(2)
|
||||||
|
val res = 240L + ((header - 241L).shl(8)) + (buffer[byteIndex++].toLong() and 0xFF)
|
||||||
|
return if(noZigZag) res else zigZagDecode(res)
|
||||||
|
}
|
||||||
|
if (header == 249L){
|
||||||
|
//doBoundaryCheck(3)
|
||||||
|
val res = 2288 + ((buffer[byteIndex++].toLong() and 0xFF).shl(8)) + (buffer[byteIndex++].toLong() and 0xFF)
|
||||||
|
return if(noZigZag) res else zigZagDecode(res)
|
||||||
|
}
|
||||||
|
val hdr = header - 247
|
||||||
|
//doBoundaryCheck(hdr.toInt())
|
||||||
|
var res = (buffer[byteIndex++].toLong() and 0xFF).or(((buffer[byteIndex++].toLong() and 0xFF).shl(8)).or((buffer[byteIndex++].toLong() and 0xFF).shl(16)))
|
||||||
|
var cmp = 2
|
||||||
|
while (hdr > ++cmp)
|
||||||
|
res = res.or((buffer[byteIndex++].toLong() and 0xFF).shl(cmp.shl(3)))
|
||||||
|
|
||||||
|
return if(noZigZag) res else zigZagDecode(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readPackedFloat(noSwapEndian: Boolean = false): Float {
|
||||||
|
val readVal = readPackedInt(true)
|
||||||
|
return if(noSwapEndian) intToFloat(readVal) else intToFloat(swapEndian(readVal))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readPackedDouble(noSwapEndian: Boolean = false): Double {
|
||||||
|
val readVal = readPackedLong(true)
|
||||||
|
return if(noSwapEndian) longToDouble(readVal) else longToDouble(swapEndian(readVal))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doBoundaryCheck(byteCount: Int) {
|
||||||
|
if(byteIndex + byteCount > maxByteOffset)
|
||||||
|
throw IndexOutOfBoundsException("Attempt to read value past maximum range!")
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
package net.tofvesson.networking
|
package net.tofvesson.networking
|
||||||
|
|
||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
import java.nio.ByteBuffer
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
abstract class Serializer(registeredTypes: Array<Class<*>>) {
|
abstract class Serializer(registeredTypes: Array<Class<*>>) {
|
||||||
@ -9,20 +8,21 @@ abstract class Serializer(registeredTypes: Array<Class<*>>) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute the size in bits that a field will occupy
|
* Compute the size in bits that a field will occupy
|
||||||
* @return Size in the byteField (first) and bitField (second) that need to be allocated
|
|
||||||
*/
|
*/
|
||||||
fun computeSize(
|
fun computeSize(
|
||||||
field: Field,
|
field: Field,
|
||||||
flags: Array<out SyncFlag>,
|
flags: Array<out SyncFlag>,
|
||||||
owner: Any?
|
owner: Any?,
|
||||||
): Pair<Int, Int> = computeSizeExplicit(field, flags, owner, field.type)
|
state: WriteState
|
||||||
|
) = computeSizeExplicit(field, flags, owner, state, field.type)
|
||||||
|
|
||||||
abstract fun computeSizeExplicit(
|
abstract fun computeSizeExplicit(
|
||||||
field: Field,
|
field: Field,
|
||||||
flags: Array<out SyncFlag>,
|
flags: Array<out SyncFlag>,
|
||||||
owner: Any?,
|
owner: Any?,
|
||||||
|
state: WriteState,
|
||||||
fieldType: Class<*>
|
fieldType: Class<*>
|
||||||
): Pair<Int, Int>
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialize a field to the buffer
|
* Serialize a field to the buffer
|
||||||
@ -32,20 +32,16 @@ abstract class Serializer(registeredTypes: Array<Class<*>>) {
|
|||||||
field: Field,
|
field: Field,
|
||||||
flags: Array<out SyncFlag>,
|
flags: Array<out SyncFlag>,
|
||||||
owner: Any?,
|
owner: Any?,
|
||||||
byteBuffer: ByteBuffer,
|
writeBuffer: WriteBuffer
|
||||||
offset: Int,
|
) = serializeExplicit(field, flags, owner, writeBuffer, field.type)
|
||||||
bitFieldOffset: Int
|
|
||||||
): Pair<Int, Int> = serializeExplicit(field, flags, owner, byteBuffer, offset, bitFieldOffset, field.type)
|
|
||||||
|
|
||||||
abstract fun serializeExplicit(
|
abstract fun serializeExplicit(
|
||||||
field: Field,
|
field: Field,
|
||||||
flags: Array<out SyncFlag>,
|
flags: Array<out SyncFlag>,
|
||||||
owner: Any?,
|
owner: Any?,
|
||||||
byteBuffer: ByteBuffer,
|
writeBuffer: WriteBuffer,
|
||||||
offset: Int,
|
|
||||||
bitFieldOffset: Int,
|
|
||||||
fieldType: Class<*>
|
fieldType: Class<*>
|
||||||
): Pair<Int, Int>
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize a field from the buffer
|
* Deserialize a field from the buffer
|
||||||
@ -55,22 +51,20 @@ abstract class Serializer(registeredTypes: Array<Class<*>>) {
|
|||||||
field: Field,
|
field: Field,
|
||||||
flags: Array<out SyncFlag>,
|
flags: Array<out SyncFlag>,
|
||||||
owner: Any?,
|
owner: Any?,
|
||||||
byteBuffer: ByteBuffer,
|
readBuffer: ReadBuffer
|
||||||
offset: Int,
|
) = deserializeExplicit(field, flags, owner, readBuffer, field.type)
|
||||||
bitFieldOffset: Int
|
|
||||||
): Pair<Int, Int> = deserializeExplicit(field, flags, owner, byteBuffer, offset, bitFieldOffset, field.type)
|
|
||||||
|
|
||||||
abstract fun deserializeExplicit(
|
abstract fun deserializeExplicit(
|
||||||
field: Field,
|
field: Field,
|
||||||
flags: Array<out SyncFlag>,
|
flags: Array<out SyncFlag>,
|
||||||
owner: Any?,
|
owner: Any?,
|
||||||
byteBuffer: ByteBuffer,
|
readBuffer: ReadBuffer,
|
||||||
offset: Int,
|
|
||||||
bitFieldOffset: Int,
|
|
||||||
fieldType: Class<*>
|
fieldType: Class<*>
|
||||||
): Pair<Int, Int>
|
)
|
||||||
|
|
||||||
fun getRegisteredTypes(): Array<Class<*>> = Arrays.copyOf(registeredTypes, registeredTypes.size)
|
fun getRegisteredTypes(): Array<Class<*>> = Arrays.copyOf(registeredTypes, registeredTypes.size)
|
||||||
fun canSerialize(field: Field): Boolean = registeredTypes.contains(field.type)
|
fun canSerialize(field: Field): Boolean = registeredTypes.contains(field.type)
|
||||||
fun canSerialize(type: Class<*>): Boolean = registeredTypes.contains(type)
|
fun canSerialize(type: Class<*>): Boolean = registeredTypes.contains(type)
|
||||||
|
|
||||||
|
protected fun throwInvalidType(type: Class<*>): Nothing = throw UnsupportedTypeException("Type ${this.javaClass} cannot serialize $type")
|
||||||
}
|
}
|
@ -1,9 +1,12 @@
|
|||||||
package net.tofvesson.networking
|
package net.tofvesson.networking
|
||||||
|
|
||||||
|
import net.tofvesson.reflect.access
|
||||||
|
import java.lang.reflect.Field
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.security.NoSuchAlgorithmException
|
import java.security.NoSuchAlgorithmException
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
|
|
||||||
|
@Suppress("MemberVisibilityCanBePrivate", "unused")
|
||||||
/**
|
/**
|
||||||
* @param permissiveMismatchCheck This should essentially never be set to true aside from some *very* odd edge cases
|
* @param permissiveMismatchCheck This should essentially never be set to true aside from some *very* odd edge cases
|
||||||
*/
|
*/
|
||||||
@ -16,6 +19,7 @@ class SyncHandler(private val permissiveMismatchCheck: Boolean = false) {
|
|||||||
// Standard serializers
|
// Standard serializers
|
||||||
serializers.add(PrimitiveSerializer.singleton)
|
serializers.add(PrimitiveSerializer.singleton)
|
||||||
serializers.add(PrimitiveArraySerializer.singleton)
|
serializers.add(PrimitiveArraySerializer.singleton)
|
||||||
|
serializers.add(DiffTrackedSerializer.singleton)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun registerSerializer(serializer: Serializer) {
|
fun registerSerializer(serializer: Serializer) {
|
||||||
@ -35,6 +39,21 @@ class SyncHandler(private val permissiveMismatchCheck: Boolean = false) {
|
|||||||
return serializer
|
return serializer
|
||||||
throw UnsupportedTypeException("Cannot find a compatible serializer for $type")
|
throw UnsupportedTypeException("Cannot find a compatible serializer for $type")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun collectSyncable(fieldType: Class<*>, collectStatic: Boolean): List<Field>{
|
||||||
|
var type = fieldType
|
||||||
|
val collect = ArrayList<Field>()
|
||||||
|
|
||||||
|
while(type!=Object::class.java && !type.isPrimitive && type.getAnnotation(NoUpwardCascade::class.java)==null){
|
||||||
|
for(field in type.declaredFields)
|
||||||
|
if(field.getAnnotation(SyncedVar::class.java)!=null && ((collectStatic && field.modifiers and 8 != 0) || (!collectStatic && field.modifiers and 8 == 0)))
|
||||||
|
collect.add(field)
|
||||||
|
type = type.superclass
|
||||||
|
}
|
||||||
|
|
||||||
|
return collect
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun registerSyncObject(value: Any){
|
fun registerSyncObject(value: Any){
|
||||||
@ -45,44 +64,28 @@ class SyncHandler(private val permissiveMismatchCheck: Boolean = false) {
|
|||||||
toSync.remove(value)
|
toSync.remove(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun serialize(): ByteArray{
|
fun serialize(): ByteBuffer {
|
||||||
var headerSize = 0
|
val writeState = WriteState(0, 0, 0)
|
||||||
var totalSize = 0
|
for(entry in toSync)
|
||||||
for(entry in toSync){
|
if(entry is Class<*>) computeClassSize(entry, writeState)
|
||||||
val result = if(entry is Class<*>) computeClassSize(entry) else computeObjectSize(entry)
|
else computeObjectSize(entry, writeState)
|
||||||
totalSize += result.first
|
val writeBuffer = WriteBuffer(writeState)
|
||||||
headerSize += result.second
|
for(entry in toSync)
|
||||||
}
|
if(entry is Class<*>) readClass(entry, writeBuffer)
|
||||||
|
else readObject(entry, writeBuffer)
|
||||||
var headerIndex = 0
|
return writeBuffer.buffer
|
||||||
var dataIndex = (headerSize shr 3) + (if(headerSize and 7 != 0) 1 else 0)
|
|
||||||
|
|
||||||
totalSize += dataIndex
|
|
||||||
|
|
||||||
val buffer = ByteArray(totalSize)
|
|
||||||
for(entry in toSync){
|
|
||||||
val result =
|
|
||||||
if(entry is Class<*>) readClass(entry, buffer, dataIndex, headerIndex)
|
|
||||||
else readObject(entry, buffer, dataIndex, headerIndex)
|
|
||||||
dataIndex = result.first
|
|
||||||
headerIndex = result.second
|
|
||||||
}
|
|
||||||
return buffer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deserialize(syncData: ByteArray){
|
fun deserialize(syncData: ByteArray) = deserialize(syncData, 0)
|
||||||
var headerSize = 0
|
fun deserialize(syncData: ByteArray, bitOffset: Int){
|
||||||
|
val writeState = WriteState(0, 0, 0)
|
||||||
for(entry in toSync)
|
for(entry in toSync)
|
||||||
headerSize += (if(entry is Class<*>) computeClassSize(entry) else computeObjectSize(entry)).second
|
if(entry is Class<*>) computeClassSize(entry, writeState)
|
||||||
var headerIndex = 0
|
else computeObjectSize(entry, writeState)
|
||||||
var dataIndex = (headerSize shr 3) + (if(headerSize and 7 != 0) 1 else 0)
|
val readBuffer = ReadBuffer(writeState, ByteBuffer.wrap(syncData), bitOffset)
|
||||||
for(entry in toSync){
|
for(entry in toSync)
|
||||||
val result =
|
if(entry is Class<*>) writeClass(entry, readBuffer)
|
||||||
if(entry is Class<*>) writeClass(entry, syncData, dataIndex, headerIndex)
|
else writeObject(entry, readBuffer)
|
||||||
else writeObject(entry, syncData, dataIndex, headerIndex)
|
|
||||||
dataIndex = result.first
|
|
||||||
headerIndex = result.second
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generateMismatchCheck(): ByteArray {
|
fun generateMismatchCheck(): ByteArray {
|
||||||
@ -115,57 +118,27 @@ class SyncHandler(private val permissiveMismatchCheck: Boolean = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun computeObjectSize(value: Any) = computeTypeSize(value.javaClass, value)
|
private fun computeObjectSize(value: Any, writeState: WriteState) = computeTypeSize(value.javaClass, value, writeState)
|
||||||
private fun computeClassSize(value: Class<*>) = computeTypeSize(value, null)
|
private fun computeClassSize(value: Class<*>, writeState: WriteState) = computeTypeSize(value, null, writeState)
|
||||||
private fun computeTypeSize(type: Class<*>, value: Any?): Pair<Int, Int> {
|
private fun computeTypeSize(type: Class<*>, value: Any?, writeState: WriteState) {
|
||||||
var byteSize = 0
|
for(field in collectSyncable(type, value==null))
|
||||||
var bitSize = 0
|
getCompatibleSerializer(field.access().type)
|
||||||
for(field in type.declaredFields){
|
.computeSize(field, SyncFlag.parse(field.getAnnotation(SyncedVar::class.java).value), value, writeState)
|
||||||
if((value == null && field.modifiers and 8 == 0) || (value != null && field.modifiers and 8 != 0)) continue
|
|
||||||
val annotation = field.getAnnotation(SyncedVar::class.java)
|
|
||||||
field.trySetAccessible()
|
|
||||||
if(annotation!=null){
|
|
||||||
val result = getCompatibleSerializer(field.type).computeSize(field, SyncFlag.parse(annotation.value), value)
|
|
||||||
byteSize += result.first
|
|
||||||
bitSize += result.second
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Pair(byteSize, bitSize)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readObject(value: Any, buffer: ByteArray, offset: Int, bitOffset: Int) = readType(value.javaClass, value, buffer, offset, bitOffset)
|
private fun readObject(value: Any, writeBuffer: WriteBuffer) = readType(value.javaClass, value, writeBuffer)
|
||||||
private fun readClass(value: Class<*>, buffer: ByteArray, offset: Int, bitOffset: Int) = readType(value, null, buffer, offset, bitOffset)
|
private fun readClass(value: Class<*>, writeBuffer: WriteBuffer) = readType(value, null, writeBuffer)
|
||||||
private fun readType(type: Class<*>, value: Any?, buffer: ByteArray, offset: Int, bitOffset: Int): Pair<Int, Int> {
|
private fun readType(type: Class<*>, value: Any?, writeBuffer: WriteBuffer) {
|
||||||
val byteBuffer = ByteBuffer.wrap(buffer)
|
for(field in collectSyncable(type, value==null))
|
||||||
var localOffset = offset
|
getCompatibleSerializer(field.type)
|
||||||
var localBitOffset = bitOffset
|
.serialize(field, SyncFlag.parse(field.getAnnotation(SyncedVar::class.java).value), value, writeBuffer)
|
||||||
for(field in type.declaredFields){
|
|
||||||
if((value == null && field.modifiers and 8 == 0) || (value != null && field.modifiers and 8 != 0)) continue
|
|
||||||
val annotation = field.getAnnotation(SyncedVar::class.java)
|
|
||||||
if(annotation != null){
|
|
||||||
val result = getCompatibleSerializer(field.type).serialize(field, SyncFlag.parse(annotation.value), value, byteBuffer, localOffset, localBitOffset)
|
|
||||||
localOffset = result.first
|
|
||||||
localBitOffset = result.second
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Pair(localOffset, localBitOffset)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun writeObject(value: Any, buffer: ByteArray, offset: Int, bitOffset: Int) = writeType(value.javaClass, value, buffer, offset, bitOffset)
|
private fun writeObject(value: Any, readBuffer: ReadBuffer) = writeType(value.javaClass, value, readBuffer)
|
||||||
private fun writeClass(value: Class<*>, buffer: ByteArray, offset: Int, bitOffset: Int) = writeType(value, null, buffer, offset, bitOffset)
|
private fun writeClass(value: Class<*>, readBuffer: ReadBuffer) = writeType(value, null, readBuffer)
|
||||||
private fun writeType(type: Class<*>, value: Any?, buffer: ByteArray, offset: Int, bitOffset: Int): Pair<Int, Int> {
|
private fun writeType(type: Class<*>, value: Any?, readBuffer: ReadBuffer) {
|
||||||
val byteBuffer = ByteBuffer.wrap(buffer)
|
for(field in collectSyncable(type, value==null))
|
||||||
var localOffset = offset
|
getCompatibleSerializer(field.type)
|
||||||
var localBitOffset = bitOffset
|
.deserialize(field, SyncFlag.parse(field.getAnnotation(SyncedVar::class.java).value), value, readBuffer)
|
||||||
for(field in type.declaredFields){
|
|
||||||
if((value == null && field.modifiers and 8 == 0) || (value != null && field.modifiers and 8 != 0)) continue
|
|
||||||
val annotation = field.getAnnotation(SyncedVar::class.java)
|
|
||||||
if(annotation != null){
|
|
||||||
val result = getCompatibleSerializer(field.type).deserialize(field, SyncFlag.parse(annotation.value), value, byteBuffer, localOffset, localBitOffset)
|
|
||||||
localOffset = result.first
|
|
||||||
localBitOffset = result.second
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Pair(localOffset, localBitOffset)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
154
src/net/tofvesson/networking/WriteBuffer.kt
Normal file
154
src/net/tofvesson/networking/WriteBuffer.kt
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
package net.tofvesson.networking
|
||||||
|
|
||||||
|
import net.tofvesson.math.collapseLowerByte
|
||||||
|
import net.tofvesson.math.toNumber
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
import kotlin.experimental.and
|
||||||
|
import kotlin.experimental.or
|
||||||
|
|
||||||
|
@Suppress("MemberVisibilityCanBePrivate", "unused")
|
||||||
|
class WriteBuffer(state: WriteState, _buffer: ByteBuffer? = null, bufferBitOffset: Int = 0) {
|
||||||
|
/*
|
||||||
|
Buffer layout:
|
||||||
|
{BufferBitOffset}[Header][Bits]{BitOffset}[Bytes]
|
||||||
|
BufferBitOffset is optional and usually set to 0
|
||||||
|
BitOffset is between 0 and 7 bits long (to realign data to the next free byte)
|
||||||
|
*/
|
||||||
|
private var headerIndex = bufferBitOffset
|
||||||
|
private var bitOffset = bufferBitOffset + state.header
|
||||||
|
private var byteIndex = state.computeBitFieldOffset(bufferBitOffset)
|
||||||
|
|
||||||
|
// Maximum size for the above values
|
||||||
|
private val maxHeaderSize = bufferBitOffset + state.header
|
||||||
|
private val maxBitOffset = bufferBitOffset + state.header + state.bits
|
||||||
|
private val maxByteOffset = state.computeRequiredBytes(0, bufferBitOffset)
|
||||||
|
|
||||||
|
val buffer: ByteBuffer
|
||||||
|
|
||||||
|
init{
|
||||||
|
buffer = _buffer ?: ByteBuffer.allocate(state.computeRequiredBytes() + varIntSize(byteIndex.toLong()))
|
||||||
|
if(buffer.capacity() + (bufferBitOffset ushr 3) + bufferBitOffset.collapseLowerByte() + varIntSize(byteIndex.toLong()) < state.computeRequiredBytes())
|
||||||
|
throw InsufficientCapacityException()
|
||||||
|
writePackedInt(byteIndex + varIntSize(byteIndex.toLong()), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun writeBit(bit: Boolean, head: Boolean){
|
||||||
|
if((head && headerIndex >= maxHeaderSize) || (!head && bitOffset >= maxBitOffset))
|
||||||
|
throw IndexOutOfBoundsException("Attempt to write more ${if(head)"headers" else "bits"} than available space permits!")
|
||||||
|
|
||||||
|
val index = (if(head) headerIndex else bitOffset) ushr 3
|
||||||
|
val shift = (if(head) headerIndex else bitOffset) and 7
|
||||||
|
buffer.put(index, (buffer[index] and (1 shl shift).inv().toByte()) or (bit.toNumber() shl shift).toByte())
|
||||||
|
if(head) ++headerIndex
|
||||||
|
else ++bitOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeHeader(bit: Boolean): WriteBuffer {
|
||||||
|
writeBit(bit, true)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeBit(bit: Boolean): WriteBuffer {
|
||||||
|
writeBit(bit, false)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeByte(value: Byte): WriteBuffer {
|
||||||
|
doBoundaryCheck(1)
|
||||||
|
buffer.put(byteIndex, value)
|
||||||
|
++byteIndex
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeShort(value: Short): WriteBuffer {
|
||||||
|
doBoundaryCheck(2)
|
||||||
|
buffer.putShort(byteIndex, value)
|
||||||
|
byteIndex += 2
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeInt(value: Int): WriteBuffer {
|
||||||
|
doBoundaryCheck(4)
|
||||||
|
buffer.putInt(byteIndex, value)
|
||||||
|
byteIndex += 4
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeLong(value: Long): WriteBuffer {
|
||||||
|
doBoundaryCheck(8)
|
||||||
|
buffer.putLong(byteIndex, value)
|
||||||
|
byteIndex += 8
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeFloat(value: Float): WriteBuffer {
|
||||||
|
doBoundaryCheck(4)
|
||||||
|
buffer.putFloat(byteIndex, value)
|
||||||
|
byteIndex += 4
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeDouble(value: Double): WriteBuffer {
|
||||||
|
doBoundaryCheck(8)
|
||||||
|
buffer.putDouble(byteIndex, value)
|
||||||
|
byteIndex += 8
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writePackedShort(value: Short, noZigZag: Boolean = false) = writePackedLong(value.toLong(), noZigZag)
|
||||||
|
fun writePackedInt(value: Int, noZigZag: Boolean = false) = writePackedLong(value.toLong(), noZigZag)
|
||||||
|
fun writePackedLong(value: Long, noZigZag: Boolean = false): WriteBuffer {
|
||||||
|
val toWrite = if(noZigZag) value else zigZagEncode(value)
|
||||||
|
val size = varIntSize(toWrite)
|
||||||
|
|
||||||
|
doBoundaryCheck(size)
|
||||||
|
|
||||||
|
when(toWrite) {
|
||||||
|
in 0..240 -> buffer.put(byteIndex, toWrite.toByte())
|
||||||
|
in 241..2287 -> {
|
||||||
|
buffer.put(byteIndex, (((toWrite - 240) shr 8) + 241).toByte())
|
||||||
|
buffer.put(byteIndex + 1, (toWrite - 240).toByte())
|
||||||
|
}
|
||||||
|
in 2288..67823 -> {
|
||||||
|
buffer.put(byteIndex, 249.toByte())
|
||||||
|
buffer.put(byteIndex + 1, ((toWrite - 2288) shr 8).toByte())
|
||||||
|
buffer.put(byteIndex + 2, (toWrite - 2288).toByte())
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
var header = 255
|
||||||
|
var match = 0x00FF_FFFF_FFFF_FFFFL
|
||||||
|
while (toWrite in 67824..match) {
|
||||||
|
--header
|
||||||
|
match = match shr 8
|
||||||
|
}
|
||||||
|
buffer.put(byteIndex, header.toByte())
|
||||||
|
val max = header - 247
|
||||||
|
for (i in 0 until max)
|
||||||
|
buffer.put(byteIndex + 1 + i, (toWrite shr (i shl 3)).toByte())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
byteIndex += size
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writePackedFloat(value: Float, noSwapEndian: Boolean = false): WriteBuffer {
|
||||||
|
val write =
|
||||||
|
if(noSwapEndian) bitConvert(floatToInt(value))
|
||||||
|
else bitConvert(swapEndian(floatToInt(value)))
|
||||||
|
return writePackedLong(write, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writePackedDouble(value: Double, noSwapEndian: Boolean = false): WriteBuffer {
|
||||||
|
val write =
|
||||||
|
if(noSwapEndian) doubleToLong(value)
|
||||||
|
else swapEndian(doubleToLong(value))
|
||||||
|
return writePackedLong(write, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doBoundaryCheck(byteCount: Int) {
|
||||||
|
if(byteIndex + byteCount > maxByteOffset)
|
||||||
|
throw IndexOutOfBoundsException("Attempt to write value past maximum range!")
|
||||||
|
}
|
||||||
|
}
|
22
src/net/tofvesson/networking/WriteState.kt
Normal file
22
src/net/tofvesson/networking/WriteState.kt
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package net.tofvesson.networking
|
||||||
|
|
||||||
|
import net.tofvesson.math.collapseLowerByte
|
||||||
|
|
||||||
|
data class WriteState(private var _bytes: Int, private var _bits: Int, private var _header: Int){
|
||||||
|
var bytes: Int = _bytes
|
||||||
|
private set
|
||||||
|
var bits: Int = _bits
|
||||||
|
private set
|
||||||
|
var header: Int = _header
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun registerBytes(bytes: Int): WriteState { this.bytes += bytes; return this }
|
||||||
|
fun registerBits(bits: Int) : WriteState { this.bits += bits; return this }
|
||||||
|
fun registerHeader(header: Int): WriteState{ this.header += header; return this }
|
||||||
|
|
||||||
|
fun computeRequiredBytes(additionalBytes: Int = 0, additionalBits: Int = 0) =
|
||||||
|
bytes + additionalBytes + computeBitFieldOffset(additionalBits)
|
||||||
|
fun computeBitFieldOffset(additionalBits: Int = 0) = roundUpBitsToBytes(bits + header + additionalBits)
|
||||||
|
|
||||||
|
private fun roundUpBitsToBytes(bits: Int) = (bits ushr 3) + bits.collapseLowerByte()
|
||||||
|
}
|
3
src/net/tofvesson/support/Pair.kt
Normal file
3
src/net/tofvesson/support/Pair.kt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package net.tofvesson.support
|
||||||
|
|
||||||
|
operator fun Pair<Int, Int>.plus(other: Pair<Int, Int>) = Pair(first + other.first, second + other.second)
|
Loading…
x
Reference in New Issue
Block a user