Minor cleanup, VersionedAdapter
This commit is contained in:
parent
24fdf7ab70
commit
66a3f0304a
src/main/kotlin/ru/dbotthepony/kstarbound
Starbound.ktVersionRegistry.kt
defs
json
math
server
world
@ -162,27 +162,67 @@ object Starbound : ISBFileLocator {
|
||||
// immeasurably lazy and fragile solution, too bad!
|
||||
// While having four separate Gson instances look like a (much) better solution (and it indeed could have been!),
|
||||
// we must not forget the fact that 'Starbound' and 'Consistent data format' are opposites,
|
||||
// and there are cases of where discStore() calls toJson() on children data, despite it having discStore() too.
|
||||
var IS_WRITING_LEGACY_JSON: Boolean by ThreadLocal.withInitial { false }
|
||||
// and there are cases of where discStore() calls toJson() on children data, despite it having its own discStore() too.
|
||||
var IS_LEGACY_JSON: Boolean by ThreadLocal.withInitial { false }
|
||||
private set
|
||||
var IS_WRITING_STORE_JSON: Boolean by ThreadLocal.withInitial { false }
|
||||
var IS_STORE_JSON: Boolean by ThreadLocal.withInitial { false }
|
||||
private set
|
||||
|
||||
fun writeLegacyJson(data: Any): JsonElement {
|
||||
fun legacyJson(data: Any): JsonElement {
|
||||
try {
|
||||
IS_WRITING_LEGACY_JSON = true
|
||||
IS_LEGACY_JSON = true
|
||||
return gson.toJsonTree(data)
|
||||
} finally {
|
||||
IS_WRITING_LEGACY_JSON = false
|
||||
IS_LEGACY_JSON = false
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> writeLegacyJson(block: () -> T): T {
|
||||
fun storeJson(data: Any): JsonElement {
|
||||
try {
|
||||
IS_WRITING_LEGACY_JSON = true
|
||||
IS_STORE_JSON = true
|
||||
return gson.toJsonTree(data)
|
||||
} finally {
|
||||
IS_STORE_JSON = false
|
||||
}
|
||||
}
|
||||
|
||||
fun legacyStoreJson(data: Any): JsonElement {
|
||||
try {
|
||||
IS_STORE_JSON = true
|
||||
IS_LEGACY_JSON = true
|
||||
return gson.toJsonTree(data)
|
||||
} finally {
|
||||
IS_STORE_JSON = false
|
||||
IS_LEGACY_JSON = false
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> legacyJson(block: () -> T): T {
|
||||
try {
|
||||
IS_LEGACY_JSON = true
|
||||
return block.invoke()
|
||||
} finally {
|
||||
IS_WRITING_LEGACY_JSON = false
|
||||
IS_LEGACY_JSON = false
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> storeJson(block: () -> T): T {
|
||||
try {
|
||||
IS_STORE_JSON = true
|
||||
return block.invoke()
|
||||
} finally {
|
||||
IS_STORE_JSON = false
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> legacyStoreJson(block: () -> T): T {
|
||||
try {
|
||||
IS_STORE_JSON = true
|
||||
IS_LEGACY_JSON = true
|
||||
return block.invoke()
|
||||
} finally {
|
||||
IS_STORE_JSON = false
|
||||
IS_LEGACY_JSON = false
|
||||
}
|
||||
}
|
||||
|
||||
@ -242,11 +282,6 @@ object Starbound : ISBFileLocator {
|
||||
|
||||
registerTypeAdapter(RGBAColorTypeAdapter)
|
||||
|
||||
registerTypeAdapter(Drawable::Adapter)
|
||||
registerTypeAdapter(ObjectOrientation::Adapter)
|
||||
registerTypeAdapter(ObjectDefinition::Adapter)
|
||||
registerTypeAdapter(StatModifier::Adapter)
|
||||
|
||||
registerTypeAdapterFactory(NativeLegacy.Companion)
|
||||
|
||||
// математические классы
|
||||
@ -261,8 +296,6 @@ object Starbound : ISBFileLocator {
|
||||
registerTypeAdapter(Vector4iTypeAdapter.nullSafe())
|
||||
registerTypeAdapter(Vector4dTypeAdapter.nullSafe())
|
||||
registerTypeAdapter(Vector4fTypeAdapter.nullSafe())
|
||||
registerTypeAdapterFactory(Line2d.Companion)
|
||||
registerTypeAdapterFactory(UniversePos.Companion)
|
||||
registerTypeAdapterFactory(AbstractPerlinNoise.Companion)
|
||||
registerTypeAdapterFactory(WeightedList.Companion)
|
||||
|
||||
@ -291,7 +324,6 @@ object Starbound : ISBFileLocator {
|
||||
|
||||
registerTypeAdapter(ItemStack.Adapter(this@Starbound))
|
||||
|
||||
registerTypeAdapter(ItemDescriptor::Adapter)
|
||||
registerTypeAdapterFactory(TreasurePoolDefinition.Companion)
|
||||
|
||||
registerTypeAdapterFactory(UniverseChunk.Companion)
|
||||
|
31
src/main/kotlin/ru/dbotthepony/kstarbound/VersionRegistry.kt
Normal file
31
src/main/kotlin/ru/dbotthepony/kstarbound/VersionRegistry.kt
Normal file
@ -0,0 +1,31 @@
|
||||
package ru.dbotthepony.kstarbound
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.TypeAdapter
|
||||
import com.google.gson.stream.JsonReader
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap
|
||||
import ru.dbotthepony.kstarbound.json.VersionedJson
|
||||
|
||||
object VersionRegistry {
|
||||
private val versions = Object2IntOpenHashMap<String>()
|
||||
|
||||
fun currentVersion(name: String): Int {
|
||||
return versions.getInt(name)
|
||||
}
|
||||
|
||||
fun make(name: String, contents: JsonElement): VersionedJson {
|
||||
return VersionedJson(name, currentVersion(name), contents)
|
||||
}
|
||||
|
||||
fun <T> load(name: String, reader: JsonReader, adapter: TypeAdapter<T>): T {
|
||||
val read = this.adapter.read(reader) ?: throw NullPointerException("Expected versioned json $name, but found null")
|
||||
|
||||
if (read.version != currentVersion(name)) {
|
||||
throw IllegalStateException("NYI: Migrating $name from ${read.version} to ${currentVersion(name)}")
|
||||
}
|
||||
|
||||
return adapter.fromJsonTree(read.content)
|
||||
}
|
||||
|
||||
private val adapter by lazy { Starbound.gson.getAdapter(VersionedJson::class.java) }
|
||||
}
|
@ -31,6 +31,7 @@ import ru.dbotthepony.kstarbound.json.builder.FactoryAdapter
|
||||
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||
import ru.dbotthepony.kstarbound.json.getAdapter
|
||||
import ru.dbotthepony.kstarbound.math.Line2d
|
||||
import ru.dbotthepony.kstarbound.network.syncher.legacyCodec
|
||||
import ru.dbotthepony.kstarbound.network.syncher.nativeCodec
|
||||
import ru.dbotthepony.kstarbound.world.physics.Poly
|
||||
@ -111,7 +112,7 @@ data class TouchDamage(
|
||||
@JsonAdapter(DamageSource.Adapter::class)
|
||||
data class DamageSource(
|
||||
val damageType: DamageType,
|
||||
val damageArea: Either<Poly, Pair<Vector2d, Vector2d>>,
|
||||
val damageArea: Either<Poly, Line2d>,
|
||||
val damage: Double,
|
||||
val trackSourceEntity: Boolean,
|
||||
val sourceEntityId: Int = 0,
|
||||
@ -125,7 +126,7 @@ data class DamageSource(
|
||||
) {
|
||||
constructor(stream: DataInputStream, isLegacy: Boolean) : this(
|
||||
DamageType.entries[stream.readUnsignedByte()],
|
||||
stream.readMVariant2({ Poly.read(this, isLegacy) }, { stream.readVector2d(isLegacy) to stream.readVector2d(isLegacy) }) ?: throw IllegalArgumentException("Empty MVariant damageArea"),
|
||||
stream.readMVariant2({ Poly.read(this, isLegacy) }, { Line2d(stream, isLegacy) }) ?: throw IllegalArgumentException("Empty MVariant damageArea"),
|
||||
stream.readDouble(isLegacy),
|
||||
stream.readBoolean(),
|
||||
stream.readInt(),
|
||||
@ -140,7 +141,7 @@ data class DamageSource(
|
||||
|
||||
data class JsonData(
|
||||
val poly: Poly? = null,
|
||||
val line: Pair<Vector2d, Vector2d>? = null,
|
||||
val line: Line2d? = null,
|
||||
val damage: Double,
|
||||
val damageType: DamageType = DamageType.DAMAGE,
|
||||
val trackSourceEntity: Boolean = true,
|
||||
@ -158,7 +159,7 @@ data class DamageSource(
|
||||
|
||||
fun write(stream: DataOutputStream, isLegacy: Boolean) {
|
||||
stream.writeByte(damageType.ordinal)
|
||||
stream.writeMVariant2(damageArea, { it.write(stream, isLegacy) }, { stream.writeStruct2d(it.first, isLegacy); stream.writeStruct2d(it.second, isLegacy) })
|
||||
stream.writeMVariant2(damageArea, { it.write(stream, isLegacy) }, { it.write(stream, isLegacy) })
|
||||
stream.writeDouble(damage, isLegacy)
|
||||
stream.writeBoolean(trackSourceEntity)
|
||||
stream.writeInt(sourceEntityId)
|
||||
|
@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableList
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.TypeAdapter
|
||||
import com.google.gson.annotations.JsonAdapter
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.google.gson.stream.JsonReader
|
||||
import com.google.gson.stream.JsonWriter
|
||||
@ -23,6 +24,7 @@ import ru.dbotthepony.kommons.gson.consumeNull
|
||||
import ru.dbotthepony.kommons.gson.contains
|
||||
import ru.dbotthepony.kstarbound.math.Line2d
|
||||
|
||||
@JsonAdapter(Drawable.Adapter::class)
|
||||
sealed class Drawable(val position: Vector2f, val color: RGBAColor, val fullbright: Boolean) {
|
||||
@JsonFactory
|
||||
data class Transformations(val centered: Boolean = false, val rotation: Float = 0f, val mirrored: Boolean = false, val scale: Either<Float, Vector2f> = Either.left(1f))
|
||||
|
@ -4,6 +4,7 @@ import com.google.gson.Gson
|
||||
import com.google.gson.JsonObject
|
||||
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.JsonWriter
|
||||
import ru.dbotthepony.kommons.gson.consumeNull
|
||||
@ -17,6 +18,7 @@ import ru.dbotthepony.kstarbound.network.syncher.nativeCodec
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutputStream
|
||||
|
||||
@JsonAdapter(StatModifier.Adapter::class)
|
||||
data class StatModifier(val stat: String, val value: Double, val type: StatModifierType) {
|
||||
class Adapter(gson: Gson) : TypeAdapter<StatModifier>() {
|
||||
private val objects = gson.getAdapter(JsonObject::class.java)
|
||||
|
@ -53,7 +53,7 @@ class Image private constructor(
|
||||
val width: Int,
|
||||
val height: Int,
|
||||
val amountOfChannels: Int,
|
||||
sprites: List<DataSprite>?
|
||||
spritesData: Pair<List<DataSprite>, IStarboundFile>?
|
||||
) {
|
||||
init {
|
||||
check(width >= 0) { "Invalid width $width" }
|
||||
@ -61,22 +61,45 @@ class Image private constructor(
|
||||
check(amountOfChannels in 1 .. 4) { "Unknown number of channels $amountOfChannels" }
|
||||
}
|
||||
|
||||
private val spritesInternal = Object2ObjectLinkedOpenHashMap<String, Sprite>()
|
||||
private val spritesInternal = LinkedHashMap<String, Sprite>()
|
||||
private var dataRef: WeakReference<ByteBuffer>? = null
|
||||
private val lock = ReentrantLock()
|
||||
//private val _texture = ThreadLocal<WeakReference<GLTexture2D>>()
|
||||
|
||||
init {
|
||||
if (sprites == null) {
|
||||
if (spritesData == null) {
|
||||
this.spritesInternal["default"] = Sprite("default", 0, 0, width, height)
|
||||
} else {
|
||||
val (sprites, origin) = spritesData
|
||||
|
||||
for (data in sprites) {
|
||||
this.spritesInternal[data.name] = Sprite(
|
||||
data.name,
|
||||
data.coordinates.x,
|
||||
data.coordinates.y,
|
||||
data.coordinates.z - data.coordinates.x,
|
||||
data.coordinates.w - data.coordinates.y)
|
||||
var sX = data.coordinates.x % width
|
||||
var sY = data.coordinates.y % height
|
||||
|
||||
if (sX !in 0 .. width) {
|
||||
//LOGGER.warn("Sprite X offset ${data.name} is out of bounds: $sX, clamping to 0 .. $width. (image: $source; frames: $origin)")
|
||||
sX = sX.coerceIn(0, width)
|
||||
}
|
||||
|
||||
if (sY !in 0 .. height) {
|
||||
//LOGGER.warn("Sprite Y offset ${data.name} is out of bounds: $sY, clamping to 0 .. $height. (image: $source; frames: $origin)")
|
||||
sY = sY.coerceIn(0, height)
|
||||
}
|
||||
|
||||
var sWidth = data.coordinates.z - sX
|
||||
var sHeight = data.coordinates.w - sY
|
||||
|
||||
if (sWidth !in 0 .. width) {
|
||||
//LOGGER.warn("Sprite width ${data.name} is out of bounds: $sWidth, clamping to 0 .. $width. (image: $source; frames: $origin)")
|
||||
sWidth = sWidth.coerceIn(0, width)
|
||||
}
|
||||
|
||||
if (sHeight !in 0 .. height) {
|
||||
//LOGGER.warn("Sprite height ${data.name} is out of bounds: $sHeight, clamping to 0 .. $height. (image: $source; frames: $origin)")
|
||||
sHeight = sHeight.coerceIn(0, height)
|
||||
}
|
||||
|
||||
this.spritesInternal[data.name] = Sprite(data.name, sX, sY, sWidth, sHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -273,7 +296,7 @@ class Image private constructor(
|
||||
for (y in 0 until PIXELS_IN_STARBOUND_UNITi) {
|
||||
val ypixel = (yspace * PIXELS_IN_STARBOUND_UNITi + y - pixelOffset.y)
|
||||
|
||||
if (ypixel !in 0 until width)
|
||||
if (ypixel !in 0 until height)
|
||||
continue
|
||||
|
||||
for (x in 0 until PIXELS_IN_STARBOUND_UNITi) {
|
||||
@ -300,12 +323,12 @@ class Image private constructor(
|
||||
}
|
||||
|
||||
companion object : TypeAdapter<Image>() {
|
||||
const val FILL_RATIO = 1 / (PIXELS_IN_STARBOUND_UNIT * PIXELS_IN_STARBOUND_UNIT)
|
||||
private val LOGGER = LogManager.getLogger()
|
||||
|
||||
private val objects by lazy { Starbound.gson.getAdapter(JsonObject::class.java) }
|
||||
private val vectors by lazy { Starbound.gson.getAdapter(Vector4i::class.java) }
|
||||
private val vectors2 by lazy { Starbound.gson.getAdapter(Vector2i::class.java) }
|
||||
private val configCache = ConcurrentHashMap<String, Optional<List<DataSprite>>>()
|
||||
private val configCache = ConcurrentHashMap<String, Optional<Pair<List<DataSprite>, IStarboundFile>>>()
|
||||
private val imageCache = ConcurrentHashMap<String, Optional<Image>>()
|
||||
private val logger = LogManager.getLogger()
|
||||
|
||||
@ -494,17 +517,17 @@ class Image private constructor(
|
||||
return ImmutableList.copyOf(sprites.values)
|
||||
}
|
||||
|
||||
private fun compute(it: String): Optional<List<DataSprite>> {
|
||||
private fun compute(it: String): Optional<Pair<List<DataSprite>, IStarboundFile>> {
|
||||
val find = Starbound.locate("$it.frames")
|
||||
|
||||
if (!find.exists) {
|
||||
return Optional.empty()
|
||||
} else {
|
||||
return Optional.of(parseFrames(objects.read(JsonReader(find.reader()).also { it.isLenient = true })))
|
||||
return Optional.of(parseFrames(objects.read(JsonReader(find.reader()).also { it.isLenient = true })) to find)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getConfig(path: String): List<DataSprite>? {
|
||||
private fun getConfig(path: String): Pair<List<DataSprite>, IStarboundFile>? {
|
||||
var folder = path.substringBefore(':').substringBeforeLast('/')
|
||||
val name = path.substringBefore(':').substringAfterLast('/').substringBefore('.')
|
||||
|
||||
|
@ -17,7 +17,7 @@ data class InventoryIcon(
|
||||
override val image: SpriteReference
|
||||
) : IInventoryIcon {
|
||||
companion object : TypeAdapter<InventoryIcon>() {
|
||||
private val adapter by lazy { FactoryAdapter.createFor(InventoryIcon::class, JsonFactory(), Starbound.gson) }
|
||||
private val adapter by lazy { FactoryAdapter.createFor(InventoryIcon::class, Starbound.gson) }
|
||||
private val images by lazy { Starbound.gson.getAdapter(SpriteReference::class.java) }
|
||||
|
||||
override fun write(out: JsonWriter, value: InventoryIcon?) {
|
||||
|
@ -8,6 +8,7 @@ import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonPrimitive
|
||||
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.JsonWriter
|
||||
import org.classdump.luna.ByteString
|
||||
@ -28,6 +29,8 @@ import ru.dbotthepony.kommons.io.writeBinaryString
|
||||
import ru.dbotthepony.kommons.io.writeVarLong
|
||||
import ru.dbotthepony.kstarbound.Registries
|
||||
import ru.dbotthepony.kstarbound.Registry
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.VersionRegistry
|
||||
import ru.dbotthepony.kstarbound.defs.item.api.IItemDefinition
|
||||
import ru.dbotthepony.kstarbound.io.readInternedString
|
||||
import ru.dbotthepony.kstarbound.item.ItemStack
|
||||
@ -105,6 +108,7 @@ fun ItemDescriptor(stream: DataInputStream): ItemDescriptor {
|
||||
* [parameters] is considered to be immutable and should not be modified
|
||||
* directly (must be copied for mutable context)
|
||||
*/
|
||||
@JsonAdapter(ItemDescriptor.Adapter::class)
|
||||
data class ItemDescriptor(
|
||||
val name: String,
|
||||
val count: Long,
|
||||
@ -123,15 +127,21 @@ data class ItemDescriptor(
|
||||
return ItemStack.create(this)
|
||||
}
|
||||
|
||||
fun toJson(): JsonObject? {
|
||||
if (isEmpty) {
|
||||
return null
|
||||
}
|
||||
private fun toJsonStruct() = JsonObject().also {
|
||||
it.add("name", JsonPrimitive(name))
|
||||
it.add("count", JsonPrimitive(count))
|
||||
it.add("parameters", parameters.deepCopy())
|
||||
}
|
||||
|
||||
return JsonObject().also {
|
||||
it.add("name", JsonPrimitive(name))
|
||||
it.add("count", JsonPrimitive(count))
|
||||
it.add("parameters", parameters.deepCopy())
|
||||
fun toJson(): JsonObject? {
|
||||
if (Starbound.IS_STORE_JSON) {
|
||||
return VersionRegistry.make("Item", toJsonStruct()).toJson()
|
||||
} else {
|
||||
if (isEmpty) {
|
||||
return null
|
||||
}
|
||||
|
||||
return toJsonStruct()
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,19 +166,14 @@ data class ItemDescriptor(
|
||||
class Adapter(gson: Gson) : TypeAdapter<ItemDescriptor>() {
|
||||
private val elements = gson.getAdapter(JsonElement::class.java)
|
||||
|
||||
override fun write(out: JsonWriter, value: ItemDescriptor?) {
|
||||
if (value == null)
|
||||
out.nullValue()
|
||||
else if (value.isEmpty)
|
||||
override fun write(out: JsonWriter, value: ItemDescriptor) {
|
||||
if (value.isEmpty)
|
||||
out.nullValue()
|
||||
else
|
||||
out.value(value.toJson())
|
||||
}
|
||||
|
||||
override fun read(`in`: JsonReader): ItemDescriptor {
|
||||
if (`in`.consumeNull())
|
||||
return EMPTY
|
||||
|
||||
return ItemDescriptor(elements.read(`in`))
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ interface IArmorItemDefinition : ILeveledItemDefinition, IScriptableItemDefiniti
|
||||
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
|
||||
if (type.rawType == Frames::class.java) {
|
||||
return object : TypeAdapter<Frames>() {
|
||||
private val adapter = FactoryAdapter.createFor(Frames::class, JsonFactory(), gson)
|
||||
private val adapter = FactoryAdapter.createFor(Frames::class, gson)
|
||||
|
||||
private val frames = gson.getAdapter(SpriteReference::class.java)
|
||||
|
||||
|
@ -9,6 +9,7 @@ import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonPrimitive
|
||||
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.JsonWriter
|
||||
import ru.dbotthepony.kommons.util.Either
|
||||
@ -32,6 +33,7 @@ import ru.dbotthepony.kstarbound.defs.AssetReference
|
||||
import ru.dbotthepony.kstarbound.defs.animation.AnimationDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.item.ItemDescriptor
|
||||
|
||||
@JsonAdapter(ObjectDefinition.Adapter::class)
|
||||
data class ObjectDefinition(
|
||||
val objectName: String,
|
||||
val objectType: ObjectType = ObjectType.OBJECT,
|
||||
@ -79,10 +81,6 @@ data class ObjectDefinition(
|
||||
val flickerPeriod: PeriodicFunction? = null,
|
||||
val orientations: ImmutableList<ObjectOrientation>,
|
||||
) {
|
||||
companion object {
|
||||
|
||||
}
|
||||
|
||||
class Adapter(gson: Gson) : TypeAdapter<ObjectDefinition>() {
|
||||
@JsonFactory(logMisses = false)
|
||||
data class PlainData(
|
||||
|
@ -7,6 +7,7 @@ import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonSyntaxException
|
||||
import com.google.gson.TypeAdapter
|
||||
import com.google.gson.annotations.JsonAdapter
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.google.gson.stream.JsonReader
|
||||
import com.google.gson.stream.JsonWriter
|
||||
@ -33,6 +34,7 @@ import ru.dbotthepony.kstarbound.defs.tile.TileDefinition
|
||||
import ru.dbotthepony.kstarbound.world.Side
|
||||
import kotlin.math.PI
|
||||
|
||||
@JsonAdapter(ObjectOrientation.Adapter::class)
|
||||
data class ObjectOrientation(
|
||||
val json: JsonObject,
|
||||
val flipImages: Boolean = false,
|
||||
@ -173,10 +175,23 @@ data class ObjectOrientation(
|
||||
}
|
||||
}
|
||||
|
||||
val minX = occupySpaces.minOf { it.x }
|
||||
val minY = occupySpaces.minOf { it.y }
|
||||
val maxX = occupySpaces.maxOf { it.x }
|
||||
val maxY = occupySpaces.maxOf { it.y }
|
||||
val minX: Int
|
||||
val minY: Int
|
||||
val maxX: Int
|
||||
val maxY: Int
|
||||
|
||||
if (occupySpaces.isNotEmpty()) {
|
||||
minX = occupySpaces.minOf { it.x }
|
||||
minY = occupySpaces.minOf { it.y }
|
||||
maxX = occupySpaces.maxOf { it.x }
|
||||
maxY = occupySpaces.maxOf { it.y }
|
||||
} else {
|
||||
minX = 0
|
||||
minY = 0
|
||||
maxX = 0
|
||||
maxY = 0
|
||||
}
|
||||
|
||||
val boundingBox = AABBi(Vector2i(minX, minY), Vector2i(maxX, maxY))
|
||||
|
||||
val metaBoundBox = obj["metaBoundBox"]?.let { aabbs.fromJsonTree(it) }
|
||||
|
@ -1,17 +1,28 @@
|
||||
package ru.dbotthepony.kstarbound.defs.quest
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
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.readCollection
|
||||
import ru.dbotthepony.kommons.io.writeBinaryString
|
||||
import ru.dbotthepony.kommons.io.writeCollection
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.VersionRegistry
|
||||
import ru.dbotthepony.kstarbound.io.readInternedString
|
||||
import ru.dbotthepony.kstarbound.json.VersionedAdapter
|
||||
import ru.dbotthepony.kstarbound.json.VersionedJson
|
||||
import ru.dbotthepony.kstarbound.json.builder.FactoryAdapter
|
||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||
import ru.dbotthepony.kstarbound.json.getAdapter
|
||||
import ru.dbotthepony.kstarbound.network.syncher.legacyCodec
|
||||
import ru.dbotthepony.kstarbound.network.syncher.nativeCodec
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutputStream
|
||||
|
||||
@JsonFactory
|
||||
@JsonAdapter(QuestArcDescriptor.Adapter::class)
|
||||
data class QuestArcDescriptor(
|
||||
val quests: ImmutableList<QuestDescriptor> = ImmutableList.of(),
|
||||
val stagehandUniqueId: String? = null,
|
||||
@ -27,6 +38,8 @@ data class QuestArcDescriptor(
|
||||
if (stagehandUniqueId != null) stream.writeBinaryString(stagehandUniqueId)
|
||||
}
|
||||
|
||||
class Adapter(gson: Gson) : VersionedAdapter<QuestArcDescriptor>("QuestArcDescriptor", FactoryAdapter.createFor(QuestArcDescriptor::class, gson))
|
||||
|
||||
companion object {
|
||||
val CODEC = nativeCodec(::QuestArcDescriptor, QuestArcDescriptor::write)
|
||||
val LEGACY_CODEC = legacyCodec(::QuestArcDescriptor, QuestArcDescriptor::write)
|
||||
|
@ -1,18 +1,23 @@
|
||||
package ru.dbotthepony.kstarbound.defs.quest
|
||||
|
||||
import com.google.common.collect.ImmutableMap
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.TypeAdapter
|
||||
import com.google.gson.annotations.JsonAdapter
|
||||
import ru.dbotthepony.kommons.io.readMap
|
||||
import ru.dbotthepony.kommons.io.writeBinaryString
|
||||
import ru.dbotthepony.kommons.io.writeMap
|
||||
import ru.dbotthepony.kstarbound.io.readInternedString
|
||||
import ru.dbotthepony.kstarbound.json.VersionedAdapter
|
||||
import ru.dbotthepony.kstarbound.json.builder.FactoryAdapter
|
||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||
import ru.dbotthepony.kstarbound.network.syncher.legacyCodec
|
||||
import ru.dbotthepony.kstarbound.network.syncher.nativeCodec
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutputStream
|
||||
|
||||
@JsonFactory
|
||||
@JsonAdapter(QuestDescriptor.Adapter::class)
|
||||
data class QuestDescriptor(
|
||||
val questId: String,
|
||||
val templateId: String = questId,
|
||||
@ -33,6 +38,8 @@ data class QuestDescriptor(
|
||||
stream.writeLong(seed)
|
||||
}
|
||||
|
||||
class Adapter(gson: Gson) : VersionedAdapter<QuestDescriptor>("QuestDescriptor", FactoryAdapter.createFor(QuestDescriptor::class, gson))
|
||||
|
||||
companion object {
|
||||
val CODEC = nativeCodec(::QuestDescriptor, QuestDescriptor::write)
|
||||
val LEGACY_CODEC = legacyCodec(::QuestDescriptor, QuestDescriptor::write)
|
||||
|
@ -140,7 +140,7 @@ abstract class VisitableWorldParameters {
|
||||
this.weatherPool = read.weatherPool
|
||||
}
|
||||
|
||||
open fun toJson(data: JsonObject, isLegacy: Boolean = Starbound.IS_WRITING_LEGACY_JSON) {
|
||||
open fun toJson(data: JsonObject, isLegacy: Boolean = Starbound.IS_LEGACY_JSON) {
|
||||
val store = StoreData(
|
||||
threatLevel,
|
||||
typeName,
|
||||
@ -164,7 +164,7 @@ abstract class VisitableWorldParameters {
|
||||
data["type"] = type.jsonName
|
||||
}
|
||||
|
||||
fun toJson(isLegacy: Boolean = Starbound.IS_WRITING_LEGACY_JSON): JsonObject {
|
||||
fun toJson(isLegacy: Boolean = Starbound.IS_LEGACY_JSON): JsonObject {
|
||||
val data = JsonObject()
|
||||
toJson(data, isLegacy)
|
||||
return data
|
||||
|
@ -211,7 +211,7 @@ class WorldLayout {
|
||||
return Starbound.gson.toJsonTree(SerializedForm(
|
||||
worldSize, regionBlending, blockNoise, blendNoise,
|
||||
playerStartSearchRegions, biomes.list, terrainSelectors.list,
|
||||
layers = layers.stream().map { it.toJson(Starbound.IS_WRITING_LEGACY_JSON) }.collect(JsonArrayCollector)
|
||||
layers = layers.stream().map { it.toJson(Starbound.IS_LEGACY_JSON) }.collect(JsonArrayCollector)
|
||||
)) as JsonObject
|
||||
}
|
||||
|
||||
|
@ -1,18 +1,13 @@
|
||||
package ru.dbotthepony.kstarbound.defs.world
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonNull
|
||||
import com.google.gson.JsonObject
|
||||
import ru.dbotthepony.kommons.gson.set
|
||||
import ru.dbotthepony.kommons.util.Either
|
||||
import ru.dbotthepony.kommons.vector.Vector2i
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.fromJson
|
||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||
import ru.dbotthepony.kstarbound.world.Universe
|
||||
import ru.dbotthepony.kstarbound.world.UniversePos
|
||||
import ru.dbotthepony.kstarbound.world.WorldGeometry
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
class WorldTemplate(val geometry: WorldGeometry) {
|
||||
var seed: Long = 0L
|
||||
@ -58,7 +53,7 @@ class WorldTemplate(val geometry: WorldGeometry) {
|
||||
fun toJson(): JsonObject {
|
||||
val data = Starbound.gson.toJsonTree(SerializedForm(
|
||||
celestialParameters, worldParameters, skyParameters, seed,
|
||||
if (Starbound.IS_WRITING_LEGACY_JSON) Either.right(geometry.size) else Either.left(geometry),
|
||||
if (Starbound.IS_LEGACY_JSON) Either.right(geometry.size) else Either.left(geometry),
|
||||
worldLayout
|
||||
)) as JsonObject
|
||||
|
||||
|
@ -106,7 +106,7 @@ abstract class NativeLegacy<NATIVE, LEGACY> {
|
||||
override fun write(out: JsonWriter, value: NativeLegacy<L, R>?) {
|
||||
if (value == null)
|
||||
out.nullValue()
|
||||
else if (Starbound.IS_WRITING_LEGACY_JSON || value.nativeValue.isEmpty)
|
||||
else if (Starbound.IS_LEGACY_JSON || value.nativeValue.isEmpty)
|
||||
right.write(out, value.legacy)
|
||||
else
|
||||
left.write(out, value.native)
|
||||
|
@ -0,0 +1,25 @@
|
||||
package ru.dbotthepony.kstarbound.json
|
||||
|
||||
import com.google.gson.TypeAdapter
|
||||
import com.google.gson.stream.JsonReader
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.VersionRegistry
|
||||
|
||||
abstract class VersionedAdapter<T>(val name: String, val parent: TypeAdapter<T>) : TypeAdapter<T>() {
|
||||
override fun write(out: JsonWriter, value: T) {
|
||||
if (Starbound.IS_STORE_JSON) {
|
||||
VersionRegistry.make(name, parent.toJsonTree(value)).toJson(out)
|
||||
} else {
|
||||
parent.write(out, value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun read(`in`: JsonReader): T {
|
||||
if (Starbound.IS_STORE_JSON) {
|
||||
return VersionRegistry.load(name, `in`, parent)
|
||||
} else {
|
||||
return parent.read(`in`)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,29 @@
|
||||
package ru.dbotthepony.kstarbound.json
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import ru.dbotthepony.kommons.io.readBinaryString
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||
import java.io.DataInputStream
|
||||
|
||||
@JsonFactory
|
||||
data class VersionedJson(val identifier: String, val version: Int?, val content: JsonElement) {
|
||||
constructor(data: DataInputStream) : this(
|
||||
data.readBinaryString(),
|
||||
data.read().let { if (it > 0) data.readInt() else null },
|
||||
data.readJsonElement())
|
||||
|
||||
fun toJson(): JsonObject {
|
||||
return adapter.toJsonTree(this) as JsonObject
|
||||
}
|
||||
|
||||
fun toJson(writer: JsonWriter) {
|
||||
adapter.write(writer, this)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val adapter by lazy { Starbound.gson.getAdapter<VersionedJson>() }
|
||||
}
|
||||
}
|
||||
|
@ -484,7 +484,7 @@ class FactoryAdapter<T : Any> private constructor(
|
||||
companion object {
|
||||
private val LOGGER = LogManager.getLogger()
|
||||
|
||||
fun <T : Any> createFor(kclass: KClass<T>, config: JsonFactory = JsonFactory(), gson: Gson, stringInterner: Interner<String> = Starbound.STRINGS): TypeAdapter<T> {
|
||||
fun <T : Any> createFor(kclass: KClass<T>, gson: Gson, config: JsonFactory = JsonFactory(), stringInterner: Interner<String> = Starbound.STRINGS): TypeAdapter<T> {
|
||||
val builder = Builder(kclass)
|
||||
val properties = kclass.declaredMembers.filterIsInstance<KProperty1<T, *>>()
|
||||
|
||||
@ -537,7 +537,7 @@ class FactoryAdapter<T : Any> private constructor(
|
||||
|
||||
val bconfig = first[0] as JsonFactory
|
||||
val kclass = raw.kotlin as KClass<T>
|
||||
return createFor(kclass, bconfig, gson, stringInterner)
|
||||
return createFor(kclass, gson, bconfig, stringInterner)
|
||||
}
|
||||
|
||||
return null
|
||||
|
@ -3,12 +3,19 @@ package ru.dbotthepony.kstarbound.math
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.TypeAdapter
|
||||
import com.google.gson.TypeAdapterFactory
|
||||
import com.google.gson.annotations.JsonAdapter
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.google.gson.stream.JsonReader
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import ru.dbotthepony.kommons.gson.consumeNull
|
||||
import ru.dbotthepony.kommons.util.KOptional
|
||||
import ru.dbotthepony.kommons.vector.Vector2d
|
||||
import ru.dbotthepony.kstarbound.io.readVector2d
|
||||
import ru.dbotthepony.kstarbound.io.writeDouble
|
||||
import ru.dbotthepony.kstarbound.io.writeStruct2d
|
||||
import ru.dbotthepony.kstarbound.json.getAdapter
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutputStream
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
private operator fun Vector2d.compareTo(other: Vector2d): Int {
|
||||
@ -17,7 +24,10 @@ private operator fun Vector2d.compareTo(other: Vector2d): Int {
|
||||
return cmp
|
||||
}
|
||||
|
||||
@JsonAdapter(Line2d.Adapter::class)
|
||||
data class Line2d(val a: Vector2d, val b: Vector2d) {
|
||||
constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readVector2d(isLegacy), stream.readVector2d(isLegacy))
|
||||
|
||||
data class Intersection(val intersects: Boolean, val point: KOptional<Vector2d>, val t: KOptional<Double>, val coincides: Boolean, val glances: Boolean) {
|
||||
companion object {
|
||||
val EMPTY = Intersection(false, KOptional(), KOptional(), false, false)
|
||||
@ -32,6 +42,11 @@ data class Line2d(val a: Vector2d, val b: Vector2d) {
|
||||
return Line2d(b, a)
|
||||
}
|
||||
|
||||
fun write(stream: DataOutputStream, isLegacy: Boolean) {
|
||||
stream.writeStruct2d(a, isLegacy)
|
||||
stream.writeStruct2d(b, isLegacy)
|
||||
}
|
||||
|
||||
// original source of this intersection algorithm:
|
||||
// https://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
|
||||
// article "Intersection of two lines in three-space" by Ronald Goldman, published in Graphics Gems, page 304
|
||||
@ -110,33 +125,21 @@ data class Line2d(val a: Vector2d, val b: Vector2d) {
|
||||
return (other - a + difference() * proj).length
|
||||
}
|
||||
|
||||
companion object : TypeAdapterFactory {
|
||||
const val NEAR_ZERO = Double.MIN_VALUE * 2.0
|
||||
const val NEAR_ONE = 1.0 - NEAR_ZERO
|
||||
class Adapter(gson: Gson) : TypeAdapter<Line2d>() {
|
||||
private val pair = gson.getAdapter<Pair<Vector2d, Vector2d>>()
|
||||
|
||||
override fun <T : Any> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
|
||||
if (type.rawType == Line2d::class.java) {
|
||||
val pair = gson.getAdapter(TypeToken.getParameterized(Vector2d::class.java, Vector2d::class.java)) as TypeAdapter<Pair<Vector2d, Vector2d>>
|
||||
override fun write(out: JsonWriter, value: Line2d) {
|
||||
pair.write(out, value.a to value.b)
|
||||
}
|
||||
|
||||
return object : TypeAdapter<Line2d>() {
|
||||
override fun write(out: JsonWriter, value: Line2d?) {
|
||||
if (value == null)
|
||||
out.nullValue()
|
||||
else
|
||||
pair.write(out, value.a to value.b)
|
||||
}
|
||||
|
||||
override fun read(`in`: JsonReader): Line2d? {
|
||||
if (`in`.consumeNull())
|
||||
return null
|
||||
|
||||
val (a, b) = pair.read(`in`)
|
||||
return Line2d(a, b)
|
||||
}
|
||||
} as TypeAdapter<T>
|
||||
}
|
||||
|
||||
return null
|
||||
override fun read(`in`: JsonReader): Line2d {
|
||||
val (a, b) = pair.read(`in`)
|
||||
return Line2d(a, b)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val NEAR_ZERO = Double.MIN_VALUE * 2.0
|
||||
const val NEAR_ONE = 1.0 - NEAR_ZERO
|
||||
}
|
||||
}
|
||||
|
@ -145,17 +145,18 @@ class ServerConnection(val server: StarboundServer, type: ConnectionType) : Conn
|
||||
|
||||
if (world == null) {
|
||||
send(PlayerWarpResultPacket(false, request, false))
|
||||
} else {
|
||||
try {
|
||||
world.acceptClient(this, request).await()
|
||||
} catch (err: Throwable) {
|
||||
send(PlayerWarpResultPacket(false, request, false))
|
||||
continue
|
||||
}
|
||||
|
||||
if (world == shipWorld) {
|
||||
disconnect("ShipWorld refused to accept its owner: $err")
|
||||
} else {
|
||||
enqueueWarp(returnWarp ?: WarpAlias.OwnShip)
|
||||
}
|
||||
try {
|
||||
world.acceptClient(this, request).await()
|
||||
} catch (err: Throwable) {
|
||||
send(PlayerWarpResultPacket(false, request, false))
|
||||
|
||||
if (world == shipWorld) {
|
||||
disconnect("ShipWorld refused to accept its owner: $err")
|
||||
} else {
|
||||
enqueueWarp(returnWarp ?: WarpAlias.OwnShip)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,6 @@ import ru.dbotthepony.kstarbound.world.TileHealth
|
||||
import ru.dbotthepony.kstarbound.world.api.ImmutableCell
|
||||
import ru.dbotthepony.kstarbound.world.entities.AbstractEntity
|
||||
import ru.dbotthepony.kstarbound.world.entities.player.PlayerEntity
|
||||
import ru.dbotthepony.kstarbound.world.entities.tile.WorldObject
|
||||
import java.io.DataOutputStream
|
||||
import java.util.HashMap
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
@ -91,7 +90,7 @@ class ServerWorldTracker(val world: ServerWorld, val client: ServerConnection, p
|
||||
|
||||
if (client.isLegacy) {
|
||||
client.send(WorldStartPacket(
|
||||
templateData = Starbound.writeLegacyJson { world.template.toJson() },
|
||||
templateData = Starbound.legacyJson { world.template.toJson() },
|
||||
skyData = skyData.toByteArray(),
|
||||
weatherData = ByteArray(0),
|
||||
playerStart = playerStart,
|
||||
@ -105,7 +104,7 @@ class ServerWorldTracker(val world: ServerWorld, val client: ServerConnection, p
|
||||
localInterpolationMode = false,
|
||||
))
|
||||
|
||||
Starbound.writeLegacyJson {
|
||||
Starbound.legacyJson {
|
||||
client.sendAndFlush(CentralStructureUpdatePacket(Starbound.gson.toJsonTree(world.centralStructure)))
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonSyntaxException
|
||||
import com.google.gson.TypeAdapter
|
||||
import com.google.gson.TypeAdapterFactory
|
||||
import com.google.gson.annotations.JsonAdapter
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.google.gson.stream.JsonReader
|
||||
import com.google.gson.stream.JsonToken
|
||||
@ -32,6 +33,7 @@ import java.io.DataOutputStream
|
||||
* 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) {
|
||||
constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readVector3i(), if (isLegacy) stream.readInt() else stream.readVarInt(), if (isLegacy) stream.readInt() else stream.readVarInt())
|
||||
|
||||
@ -95,84 +97,71 @@ data class UniversePos(val location: Vector3i = Vector3i.ZERO, val planetOrbit:
|
||||
}
|
||||
}
|
||||
|
||||
companion object : TypeAdapterFactory {
|
||||
class Adapter(gson: Gson) : TypeAdapter<UniversePos>() {
|
||||
private val vectors = gson.getAdapter(Vector3i::class.java)
|
||||
private val objects = gson.getAdapter(JsonObject::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 = objects.read(`in`)!!
|
||||
val location = values.get("location", vectors)
|
||||
val planet = values.get("planet", 0)
|
||||
val orbit = values.get("orbit", 0)
|
||||
return UniversePos(location, planet, orbit)
|
||||
}
|
||||
|
||||
if (`in`.peek() == JsonToken.STRING) {
|
||||
val read = `in`.nextString().trim()
|
||||
|
||||
if (read == "" || read.lowercase() == "")
|
||||
return ZERO
|
||||
else {
|
||||
try {
|
||||
val split = read.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("Planetary orbit: $planet")
|
||||
|
||||
if (orbit < 0)
|
||||
throw IndexOutOfBoundsException("Satellite orbit: $orbit")
|
||||
|
||||
return UniversePos(Vector3i(x, y, z), planet, orbit)
|
||||
} 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()
|
||||
|
||||
override fun <T : Any> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
|
||||
if (type.rawType == UniversePos::class.java) {
|
||||
val vectors = gson.getAdapter(Vector3i::class.java)
|
||||
val objects = gson.getAdapter(JsonObject::class.java)
|
||||
|
||||
return object : TypeAdapter<UniversePos>() {
|
||||
override fun write(out: JsonWriter, value: UniversePos?) {
|
||||
if (value == null)
|
||||
out.nullValue()
|
||||
else {
|
||||
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`.consumeNull())
|
||||
return null
|
||||
|
||||
if (`in`.peek() == JsonToken.BEGIN_OBJECT) {
|
||||
val values = objects.read(`in`)!!
|
||||
val location = values.get("location", vectors)
|
||||
val planet = values.get("planet", 0)
|
||||
val orbit = values.get("orbit", 0)
|
||||
return UniversePos(location, planet, orbit)
|
||||
}
|
||||
|
||||
if (`in`.peek() == JsonToken.STRING) {
|
||||
val read = `in`.nextString().trim()
|
||||
|
||||
if (read == "" || read.lowercase() == "")
|
||||
return ZERO
|
||||
else {
|
||||
try {
|
||||
val split = read.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("Planetary orbit: $planet")
|
||||
|
||||
if (orbit < 0)
|
||||
throw IndexOutOfBoundsException("Satellite orbit: $orbit")
|
||||
|
||||
return UniversePos(Vector3i(x, y, z), planet, orbit)
|
||||
} catch (err: Throwable) {
|
||||
throw JsonSyntaxException("Error parsing UniversePos from string", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw JsonSyntaxException("Invalid data type for UniversePos: ${`in`.peek()}")
|
||||
}
|
||||
} as TypeAdapter<T>
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user