193 lines
5.8 KiB
Kotlin
193 lines
5.8 KiB
Kotlin
package ru.dbotthepony.kstarbound.world
|
|
|
|
import com.google.gson.Gson
|
|
import com.google.gson.JsonSyntaxException
|
|
import com.google.gson.TypeAdapter
|
|
import com.google.gson.annotations.JsonAdapter
|
|
import com.google.gson.stream.JsonReader
|
|
import com.google.gson.stream.JsonToken
|
|
import com.google.gson.stream.JsonWriter
|
|
import ru.dbotthepony.kommons.gson.get
|
|
import ru.dbotthepony.kstarbound.math.vector.Vector3i
|
|
import ru.dbotthepony.kommons.io.readVarInt
|
|
import ru.dbotthepony.kstarbound.io.readVector3i
|
|
import ru.dbotthepony.kommons.io.writeStruct3i
|
|
import ru.dbotthepony.kommons.io.writeVarInt
|
|
import ru.dbotthepony.kstarbound.Starbound
|
|
import ru.dbotthepony.kstarbound.network.syncher.legacyCodec
|
|
import ru.dbotthepony.kstarbound.network.syncher.nativeCodec
|
|
import java.io.DataInputStream
|
|
import java.io.DataOutputStream
|
|
|
|
/**
|
|
* Specifies coordinates to either a planetary system, a planetary body, or a
|
|
* satellite around such a planetary body. The terms here are meant to be very
|
|
* generic, a "planetary body" could be an asteroid field, or a ship, or
|
|
* anything in orbit around the center of mass of a specific planetary system.
|
|
* The terms are really simply meant as a hierarchy of orbits.
|
|
*
|
|
* No validity checking is done here, any coordinate to any body whether it
|
|
* exists in a specific universe or not can be expressed.
|
|
*/
|
|
@JsonAdapter(UniversePos.Adapter::class)
|
|
data class UniversePos(val location: Vector3i = Vector3i.ZERO, val planetOrbit: Int = 0, val satelliteOrbit: Int = 0) : Comparable<UniversePos> {
|
|
constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readVector3i(), if (isLegacy) stream.readInt() else stream.readVarInt(), if (isLegacy) stream.readInt() else stream.readVarInt())
|
|
|
|
init {
|
|
require(planetOrbit >= 0) { "Negative planetOrbit: $planetOrbit" }
|
|
require(satelliteOrbit >= 0) { "Negative satelliteOrbit: $satelliteOrbit" }
|
|
}
|
|
|
|
override fun toString(): String {
|
|
if (planetOrbit == 0 && satelliteOrbit == 0)
|
|
return "${location.x}:${location.y}:${location.z}"
|
|
else if (satelliteOrbit == 0)
|
|
return "${location.x}:${location.y}:${location.z}:$planetOrbit"
|
|
else
|
|
return "${location.x}:${location.y}:${location.z}:$planetOrbit:$satelliteOrbit"
|
|
}
|
|
|
|
override fun compareTo(other: UniversePos): Int {
|
|
var cmp = location.x.compareTo(other.location.x)
|
|
if (cmp != 0) cmp = location.y.compareTo(other.location.y)
|
|
if (cmp != 0) cmp = location.z.compareTo(other.location.z)
|
|
if (cmp != 0) cmp = planetOrbit.compareTo(other.planetOrbit)
|
|
if (cmp != 0) cmp = satelliteOrbit.compareTo(other.satelliteOrbit)
|
|
return cmp
|
|
}
|
|
|
|
val isSystem: Boolean
|
|
get() = planetOrbit == 0
|
|
|
|
val isPlanet: Boolean
|
|
get() = planetOrbit != 0 && satelliteOrbit == 0
|
|
|
|
val isSatellite: Boolean
|
|
get() = planetOrbit != 0 && satelliteOrbit != 0
|
|
|
|
val orbitNumber: Int
|
|
get() = if (isSatellite) satelliteOrbit else if (isPlanet) planetOrbit else 0
|
|
|
|
fun system(): UniversePos {
|
|
if (planetOrbit == 0 && satelliteOrbit == 0)
|
|
return this
|
|
|
|
return UniversePos(location)
|
|
}
|
|
|
|
fun planet(): UniversePos {
|
|
if (satelliteOrbit == 0)
|
|
return this
|
|
|
|
return UniversePos(location, planetOrbit)
|
|
}
|
|
|
|
fun satellite(): UniversePos {
|
|
return this
|
|
}
|
|
|
|
fun parent(): UniversePos {
|
|
if (isSatellite)
|
|
return planet()
|
|
else if (isPlanet)
|
|
return system()
|
|
else
|
|
return this
|
|
}
|
|
|
|
fun child(orbit: Int): UniversePos {
|
|
if (isSatellite)
|
|
throw IllegalStateException("Satellite can't have children!")
|
|
else if (isPlanet)
|
|
return UniversePos(location, planetOrbit, orbit)
|
|
else
|
|
return UniversePos(location, orbit)
|
|
}
|
|
|
|
fun write(stream: DataOutputStream, isLegacy: Boolean) {
|
|
stream.writeStruct3i(location)
|
|
|
|
if (isLegacy) {
|
|
stream.writeInt(planetOrbit)
|
|
stream.writeInt(satelliteOrbit)
|
|
} else {
|
|
stream.writeVarInt(planetOrbit)
|
|
stream.writeVarInt(satelliteOrbit)
|
|
}
|
|
}
|
|
|
|
class Adapter(gson: Gson) : TypeAdapter<UniversePos>() {
|
|
private val vectors = gson.getAdapter(Vector3i::class.java)
|
|
|
|
override fun write(out: JsonWriter, value: UniversePos) {
|
|
out.beginObject()
|
|
|
|
out.name("location")
|
|
vectors.write(out, value.location)
|
|
|
|
out.name("planet")
|
|
out.value(value.planetOrbit)
|
|
|
|
out.name("satellite")
|
|
out.value(value.satelliteOrbit)
|
|
|
|
out.endObject()
|
|
}
|
|
|
|
override fun read(`in`: JsonReader): UniversePos {
|
|
if (`in`.peek() == JsonToken.BEGIN_OBJECT) {
|
|
val values = Starbound.ELEMENTS_ADAPTER.objects.read(`in`)!!
|
|
val location = values.get("location", vectors)
|
|
val planet = values.get("planet", 0)
|
|
val satellite = values.get("satellite", 0)
|
|
return UniversePos(location, planet, satellite)
|
|
}
|
|
|
|
if (`in`.peek() == JsonToken.STRING) {
|
|
val read = `in`.nextString().trim()
|
|
|
|
if (read == "" || read.lowercase() == "")
|
|
return ZERO
|
|
else {
|
|
try {
|
|
return parse(read)
|
|
} catch (err: Throwable) {
|
|
throw JsonSyntaxException("Error parsing UniversePos from string", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
throw JsonSyntaxException("Invalid data type for UniversePos: ${`in`.peek()}")
|
|
}
|
|
}
|
|
|
|
companion object {
|
|
val CODEC = nativeCodec(::UniversePos, UniversePos::write)
|
|
val LEGACY_CODEC = legacyCodec(::UniversePos, UniversePos::write)
|
|
|
|
private val splitter = Regex("[ _:]")
|
|
val ZERO = UniversePos()
|
|
|
|
fun parse(value: String): UniversePos {
|
|
if (value.isBlank())
|
|
return ZERO
|
|
|
|
val split = value.split(splitter)
|
|
val x = split[0].toInt()
|
|
val y = split[1].toInt()
|
|
val z = split[2].toInt()
|
|
|
|
val planet = if (split.size > 3) split[3].toInt() else 0
|
|
val orbit = if (split.size > 4) split[4].toInt() else 0
|
|
|
|
if (planet <= 0) // TODO: ??? Determine, if this is a bug in original code
|
|
throw IndexOutOfBoundsException("Non-positive planetary orbit: $planet (in $value)")
|
|
|
|
if (orbit < 0)
|
|
throw IndexOutOfBoundsException("Negative satellite orbit: $orbit (in $value)")
|
|
|
|
return UniversePos(Vector3i(x, y, z), planet, orbit)
|
|
}
|
|
}
|
|
}
|