KStarbound/src/main/kotlin/ru/dbotthepony/kstarbound/defs/WorldID.kt

161 lines
4.4 KiB
Kotlin

package ru.dbotthepony.kstarbound.defs
import com.google.gson.Gson
import com.google.gson.TypeAdapter
import com.google.gson.annotations.JsonAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
import ru.dbotthepony.kommons.io.readUUID
import ru.dbotthepony.kommons.io.writeBinaryString
import ru.dbotthepony.kommons.io.writeUUID
import ru.dbotthepony.kstarbound.io.readInternedString
import ru.dbotthepony.kstarbound.network.syncher.legacyCodec
import ru.dbotthepony.kstarbound.network.syncher.nativeCodec
import ru.dbotthepony.kstarbound.util.toStarboundString
import ru.dbotthepony.kstarbound.util.uuidFromStarboundString
import ru.dbotthepony.kstarbound.world.UniversePos
import java.io.DataInputStream
import java.io.DataOutputStream
import java.util.UUID
@JsonAdapter(WorldID.Adapter::class)
sealed class WorldID {
abstract fun write(stream: DataOutputStream, isLegacy: Boolean)
val isLimbo: Boolean get() = this is Limbo
object Limbo : WorldID() {
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeByte(0)
}
override fun toString(): String {
return "Nowhere"
}
}
data class Celestial(val pos: UniversePos) : WorldID() {
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeByte(1)
pos.write(stream, isLegacy)
}
override fun toString(): String {
return "CelestialWorld:$pos"
}
}
data class ShipWorld(val uuid: UUID) : WorldID() {
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeByte(2)
stream.writeUUID(uuid)
}
override fun toString(): String {
return "ClientShipWorld:${uuid.toStarboundString()}"
}
}
data class Instance(val name: String, val uuid: UUID? = null, val threatLevel: Double? = null) : WorldID() {
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeByte(3)
stream.writeBinaryString(name)
stream.writeBoolean(uuid != null)
if (uuid != null) stream.writeUUID(uuid)
stream.writeBoolean(threatLevel != null)
if (threatLevel != null) {
if (isLegacy) {
stream.writeFloat(threatLevel.toFloat())
} else {
stream.writeDouble(threatLevel)
}
}
}
override fun toString(): String {
return "InstanceWorld:$name:${uuid?.toStarboundString() ?: "-"}:${threatLevel ?: "-"}"
}
}
class Adapter(gson: Gson) : TypeAdapter<WorldID>() {
override fun write(out: JsonWriter, value: WorldID) {
out.value(value.toString())
}
override fun read(`in`: JsonReader): WorldID {
return parse(`in`.nextString())
}
}
companion object {
val CODEC = nativeCodec(::read, WorldID::write)
val LEGACY_CODEC = legacyCodec(::read, WorldID::write)
fun parse(value: String): WorldID {
if (value.isBlank())
return Limbo
val parts = value.split(':')
return when (val type = parts[0].lowercase()) {
"nowhere" -> Limbo
"instanceworld" -> {
val rest = parts[1].split(':')
if (rest.isEmpty() || rest.size > 3) {
throw IllegalArgumentException("Malformed InstanceWorld string: $value")
}
val name = rest[0]
var uuid: UUID? = null
var threatLevel: Double? = null
if (rest.size > 1) {
uuid = if (rest[1] == "-") null else uuidFromStarboundString(rest[1])
if (rest.size > 2) {
threatLevel = if (rest[2] == "-") null else rest[2].toDouble()
if (threatLevel != null && threatLevel < 0.0)
throw IllegalArgumentException("InstanceWorld threat level is negative: $value")
}
}
Instance(name, uuid, threatLevel)
}
"celestialworld" -> Celestial(UniversePos.parse(parts[1]))
"clientshipworld" -> ShipWorld(uuidFromStarboundString(parts[1]))
else -> throw IllegalArgumentException("Invalid WorldID type: $type (input: $value)")
}
}
fun read(stream: DataInputStream, isLegacy: Boolean): WorldID {
return when (val type = stream.readUnsignedByte()) {
0 -> Limbo
1 -> Celestial(UniversePos(stream, isLegacy))
2 -> ShipWorld(stream.readUUID())
3 -> {
val name = stream.readInternedString()
val uuid = if (stream.readBoolean()) stream.readUUID() else null
val level: Double?
if (stream.readBoolean()) {
if (isLegacy) {
level = stream.readFloat().toDouble()
} else {
level = stream.readDouble()
}
} else {
level = null
}
Instance(name, uuid, level)
}
else -> throw IllegalArgumentException("Unknown WorldID type $type!")
}
}
}
}