PathController, PathFinder Actor movement controller Lua bindings Game loading no longer block Universe thread, more efficient registry population synchronization Environmental status effects now can be stat modifiers
342 lines
13 KiB
Kotlin
342 lines
13 KiB
Kotlin
package ru.dbotthepony.kstarbound.io
|
|
|
|
import it.unimi.dsi.fastutil.bytes.ByteArrayList
|
|
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
|
|
import ru.dbotthepony.kommons.io.DelegateSyncher
|
|
import ru.dbotthepony.kommons.io.StreamCodec
|
|
import ru.dbotthepony.kommons.io.readBinaryString
|
|
import ru.dbotthepony.kommons.io.readDouble
|
|
import ru.dbotthepony.kommons.io.readFloat
|
|
import ru.dbotthepony.kommons.io.readInt
|
|
import ru.dbotthepony.kommons.io.readSignedVarInt
|
|
import ru.dbotthepony.kommons.io.readSignedVarLong
|
|
import ru.dbotthepony.kommons.io.readVarInt
|
|
import ru.dbotthepony.kommons.io.writeBinaryString
|
|
import ru.dbotthepony.kommons.io.writeByteArray
|
|
import ru.dbotthepony.kommons.io.writeDouble
|
|
import ru.dbotthepony.kommons.io.writeFloat
|
|
import ru.dbotthepony.kommons.io.writeInt
|
|
import ru.dbotthepony.kommons.io.writeSignedVarInt
|
|
import ru.dbotthepony.kommons.io.writeSignedVarLong
|
|
import ru.dbotthepony.kommons.io.writeStruct2d
|
|
import ru.dbotthepony.kommons.io.writeStruct2f
|
|
import ru.dbotthepony.kommons.io.writeStruct2i
|
|
import ru.dbotthepony.kommons.io.writeStruct3d
|
|
import ru.dbotthepony.kommons.io.writeStruct3f
|
|
import ru.dbotthepony.kommons.io.writeStruct3i
|
|
import ru.dbotthepony.kommons.io.writeStruct4d
|
|
import ru.dbotthepony.kommons.io.writeStruct4f
|
|
import ru.dbotthepony.kommons.io.writeStruct4i
|
|
import ru.dbotthepony.kommons.math.RGBAColor
|
|
import ru.dbotthepony.kommons.util.DelegateGetter
|
|
import ru.dbotthepony.kommons.util.DelegateSetter
|
|
import ru.dbotthepony.kommons.util.Either
|
|
import ru.dbotthepony.kommons.util.IStruct2d
|
|
import ru.dbotthepony.kommons.util.KOptional
|
|
import ru.dbotthepony.kommons.util.ListenableDelegate
|
|
import ru.dbotthepony.kstarbound.Starbound
|
|
import ru.dbotthepony.kstarbound.math.AABB
|
|
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
|
import ru.dbotthepony.kstarbound.math.vector.Vector2f
|
|
import ru.dbotthepony.kstarbound.math.vector.Vector2i
|
|
import ru.dbotthepony.kstarbound.math.vector.Vector3d
|
|
import ru.dbotthepony.kstarbound.math.vector.Vector3f
|
|
import ru.dbotthepony.kstarbound.math.vector.Vector3i
|
|
import ru.dbotthepony.kstarbound.math.vector.Vector4d
|
|
import ru.dbotthepony.kstarbound.math.vector.Vector4f
|
|
import ru.dbotthepony.kstarbound.math.vector.Vector4i
|
|
import ru.dbotthepony.kstarbound.world.ChunkPos
|
|
import java.io.DataInputStream
|
|
import java.io.DataOutputStream
|
|
import java.io.EOFException
|
|
import java.io.InputStream
|
|
import java.io.OutputStream
|
|
|
|
fun InputStream.readByteChar(): Char {
|
|
return read().toChar()
|
|
}
|
|
|
|
fun InputStream.readHeader(header: String) {
|
|
for ((i, char) in header.withIndex()) {
|
|
val read = readByteChar()
|
|
|
|
if (read != char) {
|
|
throw IllegalArgumentException("Malformed header at byte $i: expected $char (${char.code}) got $read (${char.code})")
|
|
}
|
|
}
|
|
}
|
|
|
|
fun InputStream.readChunkPos(): ChunkPos {
|
|
return ChunkPos(readSignedVarInt(), readSignedVarInt())
|
|
}
|
|
|
|
fun OutputStream.writeColor(color: RGBAColor) {
|
|
writeFloat(color.red)
|
|
writeFloat(color.green)
|
|
writeFloat(color.blue)
|
|
writeFloat(color.alpha)
|
|
}
|
|
|
|
fun InputStream.readColor(): RGBAColor {
|
|
return RGBAColor(readFloat(), readFloat(), readFloat(), readFloat())
|
|
}
|
|
|
|
fun InputStream.readInternedString(): String = Starbound.STRINGS.intern(readBinaryString())
|
|
|
|
fun InputStream.readAABBLegacy(): AABB {
|
|
val mins = readVector2f()
|
|
val maxs = readVector2f()
|
|
return AABB(mins.toDoubleVector(), maxs.toDoubleVector())
|
|
}
|
|
|
|
fun <S : InputStream, L, R> S.readEither(isLegacy: Boolean, left: S.() -> L, right: S.() -> R): Either<L, R> {
|
|
var type = readUnsignedByte()
|
|
|
|
if (isLegacy)
|
|
type--
|
|
|
|
return when (type) {
|
|
0 -> Either.left(left(this))
|
|
1 -> Either.right(right(this))
|
|
else -> throw IllegalArgumentException("Unexpected either type $type")
|
|
}
|
|
}
|
|
|
|
fun <S : OutputStream, L, R> S.writeEither(value: Either<L, R>, isLegacy: Boolean, left: S.(L) -> Unit, right: S.(R) -> Unit) {
|
|
if (value.isLeft) {
|
|
if (isLegacy) write(1) else write(0)
|
|
left(value.left())
|
|
} else {
|
|
if (isLegacy) write(2) else write(1)
|
|
right(value.right())
|
|
}
|
|
}
|
|
|
|
fun InputStream.readAABBLegacyOptional(): KOptional<AABB> {
|
|
val mins = readVector2f()
|
|
val maxs = readVector2f()
|
|
|
|
// what the fuck are you doing?!
|
|
if (
|
|
mins.x == Float.MAX_VALUE && mins.y == Float.MAX_VALUE &&
|
|
maxs.x == -Float.MAX_VALUE && maxs.y == -Float.MAX_VALUE
|
|
) {
|
|
return KOptional()
|
|
}
|
|
|
|
return KOptional(AABB(mins.toDoubleVector(), maxs.toDoubleVector()))
|
|
}
|
|
|
|
fun InputStream.readAABB(): AABB {
|
|
return AABB(readVector2d(), readVector2d())
|
|
}
|
|
|
|
fun InputStream.readAABB(isLegacy: Boolean): AABB {
|
|
if (isLegacy)
|
|
return readAABBLegacy()
|
|
else
|
|
return readAABB()
|
|
}
|
|
|
|
fun OutputStream.writeAABBLegacy(value: AABB) {
|
|
writeStruct2d(value.mins, true)
|
|
writeStruct2d(value.maxs, true)
|
|
}
|
|
|
|
fun OutputStream.writeAABBLegacyOptional(value: KOptional<AABB>) {
|
|
value.ifPresent {
|
|
writeStruct2f(it.mins.toFloatVector())
|
|
writeStruct2f(it.maxs.toFloatVector())
|
|
}.ifNotPresent {
|
|
writeFloat(Float.MAX_VALUE)
|
|
writeFloat(Float.MAX_VALUE)
|
|
writeFloat(-Float.MAX_VALUE)
|
|
writeFloat(-Float.MAX_VALUE)
|
|
}
|
|
}
|
|
|
|
fun OutputStream.writeAABB(value: AABB) {
|
|
writeStruct2d(value.mins)
|
|
writeStruct2d(value.maxs)
|
|
}
|
|
|
|
fun OutputStream.writeAABB(value: AABB, isLegacy: Boolean) {
|
|
writeStruct2d(value.mins, isLegacy)
|
|
writeStruct2d(value.maxs, isLegacy)
|
|
}
|
|
|
|
private fun InputStream.readBoolean(): Boolean {
|
|
val read = read()
|
|
|
|
if (read == -1)
|
|
throw EOFException("End of stream reached")
|
|
|
|
return read != 0
|
|
}
|
|
|
|
private fun InputStream.readUnsignedByte(): Int {
|
|
val read = read()
|
|
|
|
if (read == -1)
|
|
throw EOFException("End of stream reached")
|
|
|
|
return read
|
|
}
|
|
|
|
fun <S : InputStream, T> S.readNullable(reader: S.() -> T): T? {
|
|
if (readBoolean())
|
|
return reader(this)
|
|
else
|
|
return null
|
|
}
|
|
|
|
fun <S : OutputStream, T> S.writeNullable(value: T?, writer: S.(T) -> Unit) {
|
|
if (value == null)
|
|
write(0)
|
|
else {
|
|
write(1)
|
|
writer(value)
|
|
}
|
|
}
|
|
|
|
fun <S : InputStream, L, R> S.readMVariant2(left: S.() -> L, right: S.() -> R): Either<L, R>? {
|
|
return when (val type = readUnsignedByte()) {
|
|
0 -> null
|
|
1 -> Either.left(left())
|
|
2 -> Either.right(right())
|
|
else -> throw IllegalArgumentException("Unknown variant type $type")
|
|
}
|
|
}
|
|
|
|
fun <S : OutputStream, L, R> S.writeMVariant2(value: Either<L, R>?, left: S.(L) -> Unit, right: S.(R) -> Unit) {
|
|
write(if (value == null) 0 else if (value.isLeft) 1 else 2)
|
|
value?.map({ left(it) }, { right(it) })
|
|
}
|
|
|
|
fun InputStream.readNullableString() = readNullable { readInternedString() }
|
|
fun InputStream.readNullableFloat() = readNullable { readFloat() }
|
|
fun InputStream.readNullableDouble() = readNullable { readDouble() }
|
|
fun InputStream.readNullableDouble(isLegacy: Boolean) = readNullable { if (isLegacy) readFloat().toDouble() else readDouble() }
|
|
fun InputStream.readDouble(isLegacy: Boolean) = if (isLegacy) readFloat().toDouble() else readDouble()
|
|
fun InputStream.readVector2d(isLegacy: Boolean) = if (isLegacy) readVector2f().toDoubleVector() else readVector2d()
|
|
|
|
fun OutputStream.writeNullableString(value: String?) = writeNullable(value) { writeBinaryString(it) }
|
|
fun OutputStream.writeNullableFloat(value: Float?) = writeNullable(value) { writeFloat(it) }
|
|
fun OutputStream.writeNullableDouble(value: Double?) = writeNullable(value) { writeDouble(it) }
|
|
fun OutputStream.writeNullableDouble(value: Double?, isLegacy: Boolean) = writeNullable(value) { if (isLegacy) writeFloat(it.toFloat()) else writeDouble(it) }
|
|
fun OutputStream.writeDouble(value: Double, isLegacy: Boolean) = if (isLegacy) writeFloat(value.toFloat()) else writeDouble(value)
|
|
fun OutputStream.writeStruct2d(value: IStruct2d, isLegacy: Boolean) = if (isLegacy) { writeFloat(value.component1().toFloat()); writeFloat(value.component2().toFloat()) } else { writeDouble(value.component1()); writeDouble(value.component2()) }
|
|
|
|
private inline fun <T, I> InputStream.read2(factory: (I, I) -> T, reader: (InputStream) -> I): T {
|
|
return factory(reader(this), reader(this))
|
|
}
|
|
|
|
private inline fun <T, I> InputStream.read3(factory: (I, I, I) -> T, reader: (InputStream) -> I): T {
|
|
return factory(reader(this), reader(this), reader(this))
|
|
}
|
|
|
|
private inline fun <T, I> InputStream.read4(factory: (I, I, I, I) -> T, reader: (InputStream) -> I): T {
|
|
return factory(reader(this), reader(this), reader(this), reader(this))
|
|
}
|
|
|
|
fun InputStream.readVector2i() = read2(::Vector2i) { it.readInt() }
|
|
fun InputStream.readVector2d() = read2(::Vector2d) { it.readDouble() }
|
|
fun InputStream.readVector2f() = read2(::Vector2f) { it.readFloat() }
|
|
|
|
fun InputStream.readVector3i() = read3(::Vector3i) { it.readInt() }
|
|
fun InputStream.readVector3d() = read3(::Vector3d) { it.readDouble() }
|
|
fun InputStream.readVector3f() = read3(::Vector3f) { it.readFloat() }
|
|
|
|
fun InputStream.readVector4i() = read4(::Vector4i) { it.readInt() }
|
|
fun InputStream.readVector4d() = read4(::Vector4d) { it.readDouble() }
|
|
fun InputStream.readVector4f() = read4(::Vector4f) { it.readFloat() }
|
|
|
|
val Vector2iCodec = StreamCodec.Impl(DataInputStream::readVector2i, DataOutputStream::writeStruct2i)
|
|
val Vector2dCodec = StreamCodec.Impl(DataInputStream::readVector2d, DataOutputStream::writeStruct2d)
|
|
val Vector2fCodec = StreamCodec.Impl(DataInputStream::readVector2f, DataOutputStream::writeStruct2f)
|
|
|
|
val Vector3iCodec = StreamCodec.Impl(DataInputStream::readVector3i, DataOutputStream::writeStruct3i)
|
|
val Vector3dCodec = StreamCodec.Impl(DataInputStream::readVector3d, DataOutputStream::writeStruct3d)
|
|
val Vector3fCodec = StreamCodec.Impl(DataInputStream::readVector3f, DataOutputStream::writeStruct3f)
|
|
|
|
val Vector4iCodec = StreamCodec.Impl(DataInputStream::readVector4i, DataOutputStream::writeStruct4i)
|
|
val Vector4dCodec = StreamCodec.Impl(DataInputStream::readVector4d, DataOutputStream::writeStruct4d)
|
|
val Vector4fCodec = StreamCodec.Impl(DataInputStream::readVector4f, DataOutputStream::writeStruct4f)
|
|
|
|
fun DelegateSyncher.vec2i(value: Vector2i, setter: DelegateSetter<Vector2i> = DelegateSetter.passthrough(), getter: DelegateGetter<Vector2i> = DelegateGetter.passthrough()) = Slot(
|
|
ListenableDelegate.maskSmart(value, getter, setter), Vector2iCodec)
|
|
fun DelegateSyncher.vec2d(value: Vector2d, setter: DelegateSetter<Vector2d> = DelegateSetter.passthrough(), getter: DelegateGetter<Vector2d> = DelegateGetter.passthrough()) = Slot(
|
|
ListenableDelegate.maskSmart(value, getter, setter), Vector2dCodec)
|
|
fun DelegateSyncher.vec2f(value: Vector2f, setter: DelegateSetter<Vector2f> = DelegateSetter.passthrough(), getter: DelegateGetter<Vector2f> = DelegateGetter.passthrough()) = Slot(
|
|
ListenableDelegate.maskSmart(value, getter, setter), Vector2fCodec)
|
|
|
|
fun DelegateSyncher.vec3i(value: Vector3i, setter: DelegateSetter<Vector3i> = DelegateSetter.passthrough(), getter: DelegateGetter<Vector3i> = DelegateGetter.passthrough()) = Slot(
|
|
ListenableDelegate.maskSmart(value, getter, setter), Vector3iCodec)
|
|
fun DelegateSyncher.vec3d(value: Vector3d, setter: DelegateSetter<Vector3d> = DelegateSetter.passthrough(), getter: DelegateGetter<Vector3d> = DelegateGetter.passthrough()) = Slot(
|
|
ListenableDelegate.maskSmart(value, getter, setter), Vector3dCodec)
|
|
fun DelegateSyncher.vec3f(value: Vector3f, setter: DelegateSetter<Vector3f> = DelegateSetter.passthrough(), getter: DelegateGetter<Vector3f> = DelegateGetter.passthrough()) = Slot(
|
|
ListenableDelegate.maskSmart(value, getter, setter), Vector3fCodec)
|
|
|
|
fun DelegateSyncher.vec4i(value: Vector4i, setter: DelegateSetter<Vector4i> = DelegateSetter.passthrough(), getter: DelegateGetter<Vector4i> = DelegateGetter.passthrough()) = Slot(
|
|
ListenableDelegate.maskSmart(value, getter, setter), Vector4iCodec)
|
|
fun DelegateSyncher.vec4d(value: Vector4d, setter: DelegateSetter<Vector4d> = DelegateSetter.passthrough(), getter: DelegateGetter<Vector4d> = DelegateGetter.passthrough()) = Slot(
|
|
ListenableDelegate.maskSmart(value, getter, setter), Vector4dCodec)
|
|
fun DelegateSyncher.vec4f(value: Vector4f, setter: DelegateSetter<Vector4f> = DelegateSetter.passthrough(), getter: DelegateGetter<Vector4f> = DelegateGetter.passthrough()) = Slot(
|
|
ListenableDelegate.maskSmart(value, getter, setter), Vector4fCodec)
|
|
|
|
fun OutputStream.writeEnumStupid(index: Int, isLegacy: Boolean) {
|
|
if (isLegacy) writeInt(index) else write(index)
|
|
}
|
|
|
|
fun InputStream.readEnumStupid(isLegacy: Boolean): Int {
|
|
return if (isLegacy) readInt() else readUnsignedByte()
|
|
}
|
|
|
|
fun OutputStream.writeIntStupid(index: Int, isLegacy: Boolean) {
|
|
if (isLegacy) writeInt(index) else writeSignedVarInt(index)
|
|
}
|
|
|
|
fun InputStream.readIntStupid(isLegacy: Boolean): Int {
|
|
return if (isLegacy) readInt() else readSignedVarInt()
|
|
}
|
|
|
|
fun OutputStream.writeByteArray(array: ByteArrayList) {
|
|
writeByteArray(array.elements(), 0, array.size)
|
|
}
|
|
|
|
fun OutputStream.writeByteArray(array: FastByteArrayOutputStream) {
|
|
writeByteArray(array.array, 0, array.length)
|
|
}
|
|
|
|
fun OutputStream.writeDouble(value: Double, precision: Double, isLegacy: Boolean) {
|
|
if (isLegacy) {
|
|
writeSignedVarLong((value / precision).toLong())
|
|
} else {
|
|
writeDouble(value)
|
|
}
|
|
}
|
|
|
|
fun InputStream.readDouble(precision: Double, isLegacy: Boolean): Double {
|
|
if (isLegacy) {
|
|
return readSignedVarLong() * precision
|
|
} else {
|
|
return readDouble()
|
|
}
|
|
}
|
|
|
|
fun InputStream.readByteKey(): ByteKey {
|
|
return ByteKey(*ByteArray(readVarInt()).also { read(it) })
|
|
}
|
|
|
|
fun InputStream.readByteKeyRaw(size: Int): ByteKey {
|
|
return ByteKey(*ByteArray(size).also { read(it) })
|
|
}
|
|
|
|
fun OutputStream.writeByteKey(key: ByteKey) {
|
|
key.write(this)
|
|
}
|
|
|
|
fun OutputStream.writeRawByteKey(key: ByteKey) {
|
|
key.writeRaw(this)
|
|
}
|