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

217 lines
6.2 KiB
Kotlin

package ru.dbotthepony.kstarbound.defs
import ru.dbotthepony.kommons.io.StreamCodec
import ru.dbotthepony.kommons.io.readUUID
import ru.dbotthepony.kommons.io.readVector2d
import ru.dbotthepony.kommons.io.readVector2f
import ru.dbotthepony.kommons.io.writeBinaryString
import ru.dbotthepony.kommons.io.writeStruct2d
import ru.dbotthepony.kommons.io.writeStruct2f
import ru.dbotthepony.kommons.io.writeUUID
import ru.dbotthepony.kommons.vector.Vector2d
import ru.dbotthepony.kstarbound.io.readInternedString
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
import ru.dbotthepony.kstarbound.network.syncher.legacyCodec
import ru.dbotthepony.kstarbound.network.syncher.nativeCodec
import ru.dbotthepony.kstarbound.server.ServerConnection
import ru.dbotthepony.kstarbound.server.world.ServerWorld
import java.io.DataInputStream
import java.io.DataOutputStream
import java.util.UUID
// original game has MVariant here
// MVariant prepends InvalidValue to Variant<> template
// Variant<> itself works with LookupTypeIndex<MatchType, 0, FirstType, RestTypes...>
// 0 is responsible for holding current template comparison check
// typedef MVariant<WarpToWorld, WarpToPlayer, WarpAlias> WarpAction;
// -> Variant<InvalidType, WarpToWorld, WarpToPlayer, WarpAlias> WarpAction
// hence WarpToWorld has index 1, WarpToPlayer 2, WarpAlias 3
sealed class SpawnTarget {
abstract fun write(stream: DataOutputStream, isLegacy: Boolean)
abstract fun resolve(world: ServerWorld): Vector2d?
object Whatever : SpawnTarget() {
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeByte(0)
}
override fun resolve(world: ServerWorld): Vector2d {
return world.playerSpawnPosition
}
override fun toString(): String {
return "SpawnTarget.SpawnTarget"
}
}
data class Entity(val id: String) : SpawnTarget() {
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeByte(1)
stream.writeBinaryString(id)
}
override fun resolve(world: ServerWorld): Vector2d? {
return world.entities.values.firstOrNull { it.uniqueID == id }?.position
}
override fun toString(): String {
return "SpawnTarget.Entity[$id]"
}
}
data class Position(val position: Vector2d) : SpawnTarget() {
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeByte(2)
if (isLegacy) {
stream.writeStruct2f(position.toFloatVector())
} else {
stream.writeStruct2d(position)
}
}
override fun toString(): String {
return "SpawnTarget.Position[$position]"
}
override fun resolve(world: ServerWorld): Vector2d {
return position
}
}
data class X(val position: Double) : SpawnTarget() {
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeByte(3)
if (isLegacy) {
stream.writeFloat(position.toFloat())
} else {
stream.writeDouble(position)
}
}
override fun toString(): String {
return "SpawnTarget.X[$position]"
}
override fun resolve(world: ServerWorld): Vector2d {
TODO("Not yet implemented")
}
}
companion object {
fun read(stream: DataInputStream, isLegacy: Boolean): SpawnTarget {
return when (val type = stream.readUnsignedByte()) {
0 -> Whatever
1 -> Entity(stream.readInternedString())
2 -> Position(if (isLegacy) stream.readVector2f().toDoubleVector() else stream.readVector2d())
3 -> X(if (isLegacy) stream.readFloat().toDouble() else stream.readDouble())
else -> throw IllegalArgumentException("Unknown SpawnTarget type $type!")
}
}
}
}
sealed class WarpAction {
abstract fun write(stream: DataOutputStream, isLegacy: Boolean)
abstract fun resolve(connection: ServerConnection): WorldID
data class World(val worldID: WorldID, val target: SpawnTarget) : WarpAction() {
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeByte(1)
worldID.write(stream, isLegacy)
target.write(stream, isLegacy)
}
override fun resolve(connection: ServerConnection): WorldID {
TODO("Not yet implemented")
}
}
data class Player(val uuid: UUID) : WarpAction() {
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeByte(2)
stream.writeUUID(uuid)
}
override fun resolve(connection: ServerConnection): WorldID {
if (connection.uuid == uuid)
return connection.world?.worldID ?: WorldID.Limbo
return connection.server.clientByUUID(uuid)?.world?.worldID ?: WorldID.Limbo
}
}
companion object {
fun read(stream: DataInputStream, isLegacy: Boolean): WarpAction {
return when (val type = stream.readUnsignedByte()) {
1 -> World(WorldID.read(stream, isLegacy), SpawnTarget.read(stream, isLegacy))
2 -> Player(stream.readUUID())
3 -> {
when (val type2 = stream.readInt()) {
0 -> WarpAlias.Return
1 -> WarpAlias.OrbitedWorld
2 -> WarpAlias.OwnShip
else -> throw IllegalArgumentException("Unknown WarpAlias type $type2!")
}
}
else -> throw IllegalArgumentException("Unknown WarpAction type $type!")
}
}
val CODEC = nativeCodec(::read, WarpAction::write)
val LEGACY_CODEC = legacyCodec(::read, WarpAction::write)
}
}
sealed class WarpAlias(val index: Int) : WarpAction() {
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.write(3)
// because it is defined as enum class WarpAlias, without specifying uint8_t as type
stream.writeInt(index)
}
object Return : WarpAlias(0) {
override fun resolve(connection: ServerConnection): WorldID {
TODO("Not yet implemented")
}
override fun toString(): String {
return "WarpAlias.Return"
}
}
object OrbitedWorld : WarpAlias(1) {
override fun resolve(connection: ServerConnection): WorldID {
TODO("Not yet implemented")
}
override fun toString(): String {
return "WarpAlias.OrbitedWorld"
}
}
object OwnShip : WarpAlias(2) {
override fun resolve(connection: ServerConnection): WorldID {
return connection.shipWorld.worldID
}
override fun toString(): String {
return "WarpAlias.OwnShip"
}
}
}
enum class WarpMode(override val jsonName: String) : IStringSerializable {
NONE("None"),
BEAM_ONLY("BeamOnly"),
DEPLOY_ONLY("DeployOnly"),
BEAM_OR_DEPLOY("BeamOrDeploy");
companion object {
val CODEC = StreamCodec.Enum(WarpMode::class.java)
}
}