Minor cleanup, VersionedAdapter
This commit is contained in:
parent
24fdf7ab70
commit
66a3f0304a
@ -162,27 +162,67 @@ object Starbound : ISBFileLocator {
|
|||||||
// immeasurably lazy and fragile solution, too bad!
|
// 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!),
|
// 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,
|
// 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.
|
// and there are cases of where discStore() calls toJson() on children data, despite it having its own discStore() too.
|
||||||
var IS_WRITING_LEGACY_JSON: Boolean by ThreadLocal.withInitial { false }
|
var IS_LEGACY_JSON: Boolean by ThreadLocal.withInitial { false }
|
||||||
private set
|
private set
|
||||||
var IS_WRITING_STORE_JSON: Boolean by ThreadLocal.withInitial { false }
|
var IS_STORE_JSON: Boolean by ThreadLocal.withInitial { false }
|
||||||
private set
|
private set
|
||||||
|
|
||||||
fun writeLegacyJson(data: Any): JsonElement {
|
fun legacyJson(data: Any): JsonElement {
|
||||||
try {
|
try {
|
||||||
IS_WRITING_LEGACY_JSON = true
|
IS_LEGACY_JSON = true
|
||||||
return gson.toJsonTree(data)
|
return gson.toJsonTree(data)
|
||||||
} finally {
|
} finally {
|
||||||
IS_WRITING_LEGACY_JSON = false
|
IS_LEGACY_JSON = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> writeLegacyJson(block: () -> T): T {
|
fun storeJson(data: Any): JsonElement {
|
||||||
try {
|
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()
|
return block.invoke()
|
||||||
} finally {
|
} 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(RGBAColorTypeAdapter)
|
||||||
|
|
||||||
registerTypeAdapter(Drawable::Adapter)
|
|
||||||
registerTypeAdapter(ObjectOrientation::Adapter)
|
|
||||||
registerTypeAdapter(ObjectDefinition::Adapter)
|
|
||||||
registerTypeAdapter(StatModifier::Adapter)
|
|
||||||
|
|
||||||
registerTypeAdapterFactory(NativeLegacy.Companion)
|
registerTypeAdapterFactory(NativeLegacy.Companion)
|
||||||
|
|
||||||
// математические классы
|
// математические классы
|
||||||
@ -261,8 +296,6 @@ object Starbound : ISBFileLocator {
|
|||||||
registerTypeAdapter(Vector4iTypeAdapter.nullSafe())
|
registerTypeAdapter(Vector4iTypeAdapter.nullSafe())
|
||||||
registerTypeAdapter(Vector4dTypeAdapter.nullSafe())
|
registerTypeAdapter(Vector4dTypeAdapter.nullSafe())
|
||||||
registerTypeAdapter(Vector4fTypeAdapter.nullSafe())
|
registerTypeAdapter(Vector4fTypeAdapter.nullSafe())
|
||||||
registerTypeAdapterFactory(Line2d.Companion)
|
|
||||||
registerTypeAdapterFactory(UniversePos.Companion)
|
|
||||||
registerTypeAdapterFactory(AbstractPerlinNoise.Companion)
|
registerTypeAdapterFactory(AbstractPerlinNoise.Companion)
|
||||||
registerTypeAdapterFactory(WeightedList.Companion)
|
registerTypeAdapterFactory(WeightedList.Companion)
|
||||||
|
|
||||||
@ -291,7 +324,6 @@ object Starbound : ISBFileLocator {
|
|||||||
|
|
||||||
registerTypeAdapter(ItemStack.Adapter(this@Starbound))
|
registerTypeAdapter(ItemStack.Adapter(this@Starbound))
|
||||||
|
|
||||||
registerTypeAdapter(ItemDescriptor::Adapter)
|
|
||||||
registerTypeAdapterFactory(TreasurePoolDefinition.Companion)
|
registerTypeAdapterFactory(TreasurePoolDefinition.Companion)
|
||||||
|
|
||||||
registerTypeAdapterFactory(UniverseChunk.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.IStringSerializable
|
||||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||||
import ru.dbotthepony.kstarbound.json.getAdapter
|
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.legacyCodec
|
||||||
import ru.dbotthepony.kstarbound.network.syncher.nativeCodec
|
import ru.dbotthepony.kstarbound.network.syncher.nativeCodec
|
||||||
import ru.dbotthepony.kstarbound.world.physics.Poly
|
import ru.dbotthepony.kstarbound.world.physics.Poly
|
||||||
@ -111,7 +112,7 @@ data class TouchDamage(
|
|||||||
@JsonAdapter(DamageSource.Adapter::class)
|
@JsonAdapter(DamageSource.Adapter::class)
|
||||||
data class DamageSource(
|
data class DamageSource(
|
||||||
val damageType: DamageType,
|
val damageType: DamageType,
|
||||||
val damageArea: Either<Poly, Pair<Vector2d, Vector2d>>,
|
val damageArea: Either<Poly, Line2d>,
|
||||||
val damage: Double,
|
val damage: Double,
|
||||||
val trackSourceEntity: Boolean,
|
val trackSourceEntity: Boolean,
|
||||||
val sourceEntityId: Int = 0,
|
val sourceEntityId: Int = 0,
|
||||||
@ -125,7 +126,7 @@ data class DamageSource(
|
|||||||
) {
|
) {
|
||||||
constructor(stream: DataInputStream, isLegacy: Boolean) : this(
|
constructor(stream: DataInputStream, isLegacy: Boolean) : this(
|
||||||
DamageType.entries[stream.readUnsignedByte()],
|
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.readDouble(isLegacy),
|
||||||
stream.readBoolean(),
|
stream.readBoolean(),
|
||||||
stream.readInt(),
|
stream.readInt(),
|
||||||
@ -140,7 +141,7 @@ data class DamageSource(
|
|||||||
|
|
||||||
data class JsonData(
|
data class JsonData(
|
||||||
val poly: Poly? = null,
|
val poly: Poly? = null,
|
||||||
val line: Pair<Vector2d, Vector2d>? = null,
|
val line: Line2d? = null,
|
||||||
val damage: Double,
|
val damage: Double,
|
||||||
val damageType: DamageType = DamageType.DAMAGE,
|
val damageType: DamageType = DamageType.DAMAGE,
|
||||||
val trackSourceEntity: Boolean = true,
|
val trackSourceEntity: Boolean = true,
|
||||||
@ -158,7 +159,7 @@ data class DamageSource(
|
|||||||
|
|
||||||
fun write(stream: DataOutputStream, isLegacy: Boolean) {
|
fun write(stream: DataOutputStream, isLegacy: Boolean) {
|
||||||
stream.writeByte(damageType.ordinal)
|
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.writeDouble(damage, isLegacy)
|
||||||
stream.writeBoolean(trackSourceEntity)
|
stream.writeBoolean(trackSourceEntity)
|
||||||
stream.writeInt(sourceEntityId)
|
stream.writeInt(sourceEntityId)
|
||||||
|
@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableList
|
|||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import com.google.gson.TypeAdapter
|
import com.google.gson.TypeAdapter
|
||||||
|
import com.google.gson.annotations.JsonAdapter
|
||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
import com.google.gson.stream.JsonReader
|
import com.google.gson.stream.JsonReader
|
||||||
import com.google.gson.stream.JsonWriter
|
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.kommons.gson.contains
|
||||||
import ru.dbotthepony.kstarbound.math.Line2d
|
import ru.dbotthepony.kstarbound.math.Line2d
|
||||||
|
|
||||||
|
@JsonAdapter(Drawable.Adapter::class)
|
||||||
sealed class Drawable(val position: Vector2f, val color: RGBAColor, val fullbright: Boolean) {
|
sealed class Drawable(val position: Vector2f, val color: RGBAColor, val fullbright: Boolean) {
|
||||||
@JsonFactory
|
@JsonFactory
|
||||||
data class Transformations(val centered: Boolean = false, val rotation: Float = 0f, val mirrored: Boolean = false, val scale: Either<Float, Vector2f> = Either.left(1f))
|
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.JsonObject
|
||||||
import com.google.gson.JsonSyntaxException
|
import com.google.gson.JsonSyntaxException
|
||||||
import com.google.gson.TypeAdapter
|
import com.google.gson.TypeAdapter
|
||||||
|
import com.google.gson.annotations.JsonAdapter
|
||||||
import com.google.gson.stream.JsonReader
|
import com.google.gson.stream.JsonReader
|
||||||
import com.google.gson.stream.JsonWriter
|
import com.google.gson.stream.JsonWriter
|
||||||
import ru.dbotthepony.kommons.gson.consumeNull
|
import ru.dbotthepony.kommons.gson.consumeNull
|
||||||
@ -17,6 +18,7 @@ import ru.dbotthepony.kstarbound.network.syncher.nativeCodec
|
|||||||
import java.io.DataInputStream
|
import java.io.DataInputStream
|
||||||
import java.io.DataOutputStream
|
import java.io.DataOutputStream
|
||||||
|
|
||||||
|
@JsonAdapter(StatModifier.Adapter::class)
|
||||||
data class StatModifier(val stat: String, val value: Double, val type: StatModifierType) {
|
data class StatModifier(val stat: String, val value: Double, val type: StatModifierType) {
|
||||||
class Adapter(gson: Gson) : TypeAdapter<StatModifier>() {
|
class Adapter(gson: Gson) : TypeAdapter<StatModifier>() {
|
||||||
private val objects = gson.getAdapter(JsonObject::class.java)
|
private val objects = gson.getAdapter(JsonObject::class.java)
|
||||||
|
@ -53,7 +53,7 @@ class Image private constructor(
|
|||||||
val width: Int,
|
val width: Int,
|
||||||
val height: Int,
|
val height: Int,
|
||||||
val amountOfChannels: Int,
|
val amountOfChannels: Int,
|
||||||
sprites: List<DataSprite>?
|
spritesData: Pair<List<DataSprite>, IStarboundFile>?
|
||||||
) {
|
) {
|
||||||
init {
|
init {
|
||||||
check(width >= 0) { "Invalid width $width" }
|
check(width >= 0) { "Invalid width $width" }
|
||||||
@ -61,22 +61,45 @@ class Image private constructor(
|
|||||||
check(amountOfChannels in 1 .. 4) { "Unknown number of channels $amountOfChannels" }
|
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 var dataRef: WeakReference<ByteBuffer>? = null
|
||||||
private val lock = ReentrantLock()
|
private val lock = ReentrantLock()
|
||||||
//private val _texture = ThreadLocal<WeakReference<GLTexture2D>>()
|
//private val _texture = ThreadLocal<WeakReference<GLTexture2D>>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (sprites == null) {
|
if (spritesData == null) {
|
||||||
this.spritesInternal["default"] = Sprite("default", 0, 0, width, height)
|
this.spritesInternal["default"] = Sprite("default", 0, 0, width, height)
|
||||||
} else {
|
} else {
|
||||||
|
val (sprites, origin) = spritesData
|
||||||
|
|
||||||
for (data in sprites) {
|
for (data in sprites) {
|
||||||
this.spritesInternal[data.name] = Sprite(
|
var sX = data.coordinates.x % width
|
||||||
data.name,
|
var sY = data.coordinates.y % height
|
||||||
data.coordinates.x,
|
|
||||||
data.coordinates.y,
|
if (sX !in 0 .. width) {
|
||||||
data.coordinates.z - data.coordinates.x,
|
//LOGGER.warn("Sprite X offset ${data.name} is out of bounds: $sX, clamping to 0 .. $width. (image: $source; frames: $origin)")
|
||||||
data.coordinates.w - data.coordinates.y)
|
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) {
|
for (y in 0 until PIXELS_IN_STARBOUND_UNITi) {
|
||||||
val ypixel = (yspace * PIXELS_IN_STARBOUND_UNITi + y - pixelOffset.y)
|
val ypixel = (yspace * PIXELS_IN_STARBOUND_UNITi + y - pixelOffset.y)
|
||||||
|
|
||||||
if (ypixel !in 0 until width)
|
if (ypixel !in 0 until height)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for (x in 0 until PIXELS_IN_STARBOUND_UNITi) {
|
for (x in 0 until PIXELS_IN_STARBOUND_UNITi) {
|
||||||
@ -300,12 +323,12 @@ class Image private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object : TypeAdapter<Image>() {
|
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 objects by lazy { Starbound.gson.getAdapter(JsonObject::class.java) }
|
||||||
private val vectors by lazy { Starbound.gson.getAdapter(Vector4i::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 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 imageCache = ConcurrentHashMap<String, Optional<Image>>()
|
||||||
private val logger = LogManager.getLogger()
|
private val logger = LogManager.getLogger()
|
||||||
|
|
||||||
@ -494,17 +517,17 @@ class Image private constructor(
|
|||||||
return ImmutableList.copyOf(sprites.values)
|
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")
|
val find = Starbound.locate("$it.frames")
|
||||||
|
|
||||||
if (!find.exists) {
|
if (!find.exists) {
|
||||||
return Optional.empty()
|
return Optional.empty()
|
||||||
} else {
|
} 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('/')
|
var folder = path.substringBefore(':').substringBeforeLast('/')
|
||||||
val name = path.substringBefore(':').substringAfterLast('/').substringBefore('.')
|
val name = path.substringBefore(':').substringAfterLast('/').substringBefore('.')
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ data class InventoryIcon(
|
|||||||
override val image: SpriteReference
|
override val image: SpriteReference
|
||||||
) : IInventoryIcon {
|
) : IInventoryIcon {
|
||||||
companion object : TypeAdapter<InventoryIcon>() {
|
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) }
|
private val images by lazy { Starbound.gson.getAdapter(SpriteReference::class.java) }
|
||||||
|
|
||||||
override fun write(out: JsonWriter, value: InventoryIcon?) {
|
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.JsonPrimitive
|
||||||
import com.google.gson.JsonSyntaxException
|
import com.google.gson.JsonSyntaxException
|
||||||
import com.google.gson.TypeAdapter
|
import com.google.gson.TypeAdapter
|
||||||
|
import com.google.gson.annotations.JsonAdapter
|
||||||
import com.google.gson.stream.JsonReader
|
import com.google.gson.stream.JsonReader
|
||||||
import com.google.gson.stream.JsonWriter
|
import com.google.gson.stream.JsonWriter
|
||||||
import org.classdump.luna.ByteString
|
import org.classdump.luna.ByteString
|
||||||
@ -28,6 +29,8 @@ import ru.dbotthepony.kommons.io.writeBinaryString
|
|||||||
import ru.dbotthepony.kommons.io.writeVarLong
|
import ru.dbotthepony.kommons.io.writeVarLong
|
||||||
import ru.dbotthepony.kstarbound.Registries
|
import ru.dbotthepony.kstarbound.Registries
|
||||||
import ru.dbotthepony.kstarbound.Registry
|
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.defs.item.api.IItemDefinition
|
||||||
import ru.dbotthepony.kstarbound.io.readInternedString
|
import ru.dbotthepony.kstarbound.io.readInternedString
|
||||||
import ru.dbotthepony.kstarbound.item.ItemStack
|
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
|
* [parameters] is considered to be immutable and should not be modified
|
||||||
* directly (must be copied for mutable context)
|
* directly (must be copied for mutable context)
|
||||||
*/
|
*/
|
||||||
|
@JsonAdapter(ItemDescriptor.Adapter::class)
|
||||||
data class ItemDescriptor(
|
data class ItemDescriptor(
|
||||||
val name: String,
|
val name: String,
|
||||||
val count: Long,
|
val count: Long,
|
||||||
@ -123,15 +127,21 @@ data class ItemDescriptor(
|
|||||||
return ItemStack.create(this)
|
return ItemStack.create(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toJson(): JsonObject? {
|
private fun toJsonStruct() = JsonObject().also {
|
||||||
if (isEmpty) {
|
it.add("name", JsonPrimitive(name))
|
||||||
return null
|
it.add("count", JsonPrimitive(count))
|
||||||
}
|
it.add("parameters", parameters.deepCopy())
|
||||||
|
}
|
||||||
|
|
||||||
return JsonObject().also {
|
fun toJson(): JsonObject? {
|
||||||
it.add("name", JsonPrimitive(name))
|
if (Starbound.IS_STORE_JSON) {
|
||||||
it.add("count", JsonPrimitive(count))
|
return VersionRegistry.make("Item", toJsonStruct()).toJson()
|
||||||
it.add("parameters", parameters.deepCopy())
|
} else {
|
||||||
|
if (isEmpty) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return toJsonStruct()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,19 +166,14 @@ data class ItemDescriptor(
|
|||||||
class Adapter(gson: Gson) : TypeAdapter<ItemDescriptor>() {
|
class Adapter(gson: Gson) : TypeAdapter<ItemDescriptor>() {
|
||||||
private val elements = gson.getAdapter(JsonElement::class.java)
|
private val elements = gson.getAdapter(JsonElement::class.java)
|
||||||
|
|
||||||
override fun write(out: JsonWriter, value: ItemDescriptor?) {
|
override fun write(out: JsonWriter, value: ItemDescriptor) {
|
||||||
if (value == null)
|
if (value.isEmpty)
|
||||||
out.nullValue()
|
|
||||||
else if (value.isEmpty)
|
|
||||||
out.nullValue()
|
out.nullValue()
|
||||||
else
|
else
|
||||||
out.value(value.toJson())
|
out.value(value.toJson())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun read(`in`: JsonReader): ItemDescriptor {
|
override fun read(`in`: JsonReader): ItemDescriptor {
|
||||||
if (`in`.consumeNull())
|
|
||||||
return EMPTY
|
|
||||||
|
|
||||||
return ItemDescriptor(elements.read(`in`))
|
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>? {
|
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
|
||||||
if (type.rawType == Frames::class.java) {
|
if (type.rawType == Frames::class.java) {
|
||||||
return object : TypeAdapter<Frames>() {
|
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)
|
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.JsonPrimitive
|
||||||
import com.google.gson.JsonSyntaxException
|
import com.google.gson.JsonSyntaxException
|
||||||
import com.google.gson.TypeAdapter
|
import com.google.gson.TypeAdapter
|
||||||
|
import com.google.gson.annotations.JsonAdapter
|
||||||
import com.google.gson.stream.JsonReader
|
import com.google.gson.stream.JsonReader
|
||||||
import com.google.gson.stream.JsonWriter
|
import com.google.gson.stream.JsonWriter
|
||||||
import ru.dbotthepony.kommons.util.Either
|
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.animation.AnimationDefinition
|
||||||
import ru.dbotthepony.kstarbound.defs.item.ItemDescriptor
|
import ru.dbotthepony.kstarbound.defs.item.ItemDescriptor
|
||||||
|
|
||||||
|
@JsonAdapter(ObjectDefinition.Adapter::class)
|
||||||
data class ObjectDefinition(
|
data class ObjectDefinition(
|
||||||
val objectName: String,
|
val objectName: String,
|
||||||
val objectType: ObjectType = ObjectType.OBJECT,
|
val objectType: ObjectType = ObjectType.OBJECT,
|
||||||
@ -79,10 +81,6 @@ data class ObjectDefinition(
|
|||||||
val flickerPeriod: PeriodicFunction? = null,
|
val flickerPeriod: PeriodicFunction? = null,
|
||||||
val orientations: ImmutableList<ObjectOrientation>,
|
val orientations: ImmutableList<ObjectOrientation>,
|
||||||
) {
|
) {
|
||||||
companion object {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class Adapter(gson: Gson) : TypeAdapter<ObjectDefinition>() {
|
class Adapter(gson: Gson) : TypeAdapter<ObjectDefinition>() {
|
||||||
@JsonFactory(logMisses = false)
|
@JsonFactory(logMisses = false)
|
||||||
data class PlainData(
|
data class PlainData(
|
||||||
|
@ -7,6 +7,7 @@ import com.google.gson.JsonArray
|
|||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import com.google.gson.JsonSyntaxException
|
import com.google.gson.JsonSyntaxException
|
||||||
import com.google.gson.TypeAdapter
|
import com.google.gson.TypeAdapter
|
||||||
|
import com.google.gson.annotations.JsonAdapter
|
||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
import com.google.gson.stream.JsonReader
|
import com.google.gson.stream.JsonReader
|
||||||
import com.google.gson.stream.JsonWriter
|
import com.google.gson.stream.JsonWriter
|
||||||
@ -33,6 +34,7 @@ import ru.dbotthepony.kstarbound.defs.tile.TileDefinition
|
|||||||
import ru.dbotthepony.kstarbound.world.Side
|
import ru.dbotthepony.kstarbound.world.Side
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
|
|
||||||
|
@JsonAdapter(ObjectOrientation.Adapter::class)
|
||||||
data class ObjectOrientation(
|
data class ObjectOrientation(
|
||||||
val json: JsonObject,
|
val json: JsonObject,
|
||||||
val flipImages: Boolean = false,
|
val flipImages: Boolean = false,
|
||||||
@ -173,10 +175,23 @@ data class ObjectOrientation(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val minX = occupySpaces.minOf { it.x }
|
val minX: Int
|
||||||
val minY = occupySpaces.minOf { it.y }
|
val minY: Int
|
||||||
val maxX = occupySpaces.maxOf { it.x }
|
val maxX: Int
|
||||||
val maxY = occupySpaces.maxOf { it.y }
|
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 boundingBox = AABBi(Vector2i(minX, minY), Vector2i(maxX, maxY))
|
||||||
|
|
||||||
val metaBoundBox = obj["metaBoundBox"]?.let { aabbs.fromJsonTree(it) }
|
val metaBoundBox = obj["metaBoundBox"]?.let { aabbs.fromJsonTree(it) }
|
||||||
|
@ -1,17 +1,28 @@
|
|||||||
package ru.dbotthepony.kstarbound.defs.quest
|
package ru.dbotthepony.kstarbound.defs.quest
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList
|
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.readCollection
|
||||||
import ru.dbotthepony.kommons.io.writeBinaryString
|
import ru.dbotthepony.kommons.io.writeBinaryString
|
||||||
import ru.dbotthepony.kommons.io.writeCollection
|
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.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.builder.JsonFactory
|
||||||
|
import ru.dbotthepony.kstarbound.json.getAdapter
|
||||||
import ru.dbotthepony.kstarbound.network.syncher.legacyCodec
|
import ru.dbotthepony.kstarbound.network.syncher.legacyCodec
|
||||||
import ru.dbotthepony.kstarbound.network.syncher.nativeCodec
|
import ru.dbotthepony.kstarbound.network.syncher.nativeCodec
|
||||||
import java.io.DataInputStream
|
import java.io.DataInputStream
|
||||||
import java.io.DataOutputStream
|
import java.io.DataOutputStream
|
||||||
|
|
||||||
@JsonFactory
|
@JsonAdapter(QuestArcDescriptor.Adapter::class)
|
||||||
data class QuestArcDescriptor(
|
data class QuestArcDescriptor(
|
||||||
val quests: ImmutableList<QuestDescriptor> = ImmutableList.of(),
|
val quests: ImmutableList<QuestDescriptor> = ImmutableList.of(),
|
||||||
val stagehandUniqueId: String? = null,
|
val stagehandUniqueId: String? = null,
|
||||||
@ -27,6 +38,8 @@ data class QuestArcDescriptor(
|
|||||||
if (stagehandUniqueId != null) stream.writeBinaryString(stagehandUniqueId)
|
if (stagehandUniqueId != null) stream.writeBinaryString(stagehandUniqueId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Adapter(gson: Gson) : VersionedAdapter<QuestArcDescriptor>("QuestArcDescriptor", FactoryAdapter.createFor(QuestArcDescriptor::class, gson))
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val CODEC = nativeCodec(::QuestArcDescriptor, QuestArcDescriptor::write)
|
val CODEC = nativeCodec(::QuestArcDescriptor, QuestArcDescriptor::write)
|
||||||
val LEGACY_CODEC = legacyCodec(::QuestArcDescriptor, QuestArcDescriptor::write)
|
val LEGACY_CODEC = legacyCodec(::QuestArcDescriptor, QuestArcDescriptor::write)
|
||||||
|
@ -1,18 +1,23 @@
|
|||||||
package ru.dbotthepony.kstarbound.defs.quest
|
package ru.dbotthepony.kstarbound.defs.quest
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap
|
import com.google.common.collect.ImmutableMap
|
||||||
|
import com.google.gson.Gson
|
||||||
import com.google.gson.JsonObject
|
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.readMap
|
||||||
import ru.dbotthepony.kommons.io.writeBinaryString
|
import ru.dbotthepony.kommons.io.writeBinaryString
|
||||||
import ru.dbotthepony.kommons.io.writeMap
|
import ru.dbotthepony.kommons.io.writeMap
|
||||||
import ru.dbotthepony.kstarbound.io.readInternedString
|
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.json.builder.JsonFactory
|
||||||
import ru.dbotthepony.kstarbound.network.syncher.legacyCodec
|
import ru.dbotthepony.kstarbound.network.syncher.legacyCodec
|
||||||
import ru.dbotthepony.kstarbound.network.syncher.nativeCodec
|
import ru.dbotthepony.kstarbound.network.syncher.nativeCodec
|
||||||
import java.io.DataInputStream
|
import java.io.DataInputStream
|
||||||
import java.io.DataOutputStream
|
import java.io.DataOutputStream
|
||||||
|
|
||||||
@JsonFactory
|
@JsonAdapter(QuestDescriptor.Adapter::class)
|
||||||
data class QuestDescriptor(
|
data class QuestDescriptor(
|
||||||
val questId: String,
|
val questId: String,
|
||||||
val templateId: String = questId,
|
val templateId: String = questId,
|
||||||
@ -33,6 +38,8 @@ data class QuestDescriptor(
|
|||||||
stream.writeLong(seed)
|
stream.writeLong(seed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Adapter(gson: Gson) : VersionedAdapter<QuestDescriptor>("QuestDescriptor", FactoryAdapter.createFor(QuestDescriptor::class, gson))
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val CODEC = nativeCodec(::QuestDescriptor, QuestDescriptor::write)
|
val CODEC = nativeCodec(::QuestDescriptor, QuestDescriptor::write)
|
||||||
val LEGACY_CODEC = legacyCodec(::QuestDescriptor, QuestDescriptor::write)
|
val LEGACY_CODEC = legacyCodec(::QuestDescriptor, QuestDescriptor::write)
|
||||||
|
@ -140,7 +140,7 @@ abstract class VisitableWorldParameters {
|
|||||||
this.weatherPool = read.weatherPool
|
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(
|
val store = StoreData(
|
||||||
threatLevel,
|
threatLevel,
|
||||||
typeName,
|
typeName,
|
||||||
@ -164,7 +164,7 @@ abstract class VisitableWorldParameters {
|
|||||||
data["type"] = type.jsonName
|
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()
|
val data = JsonObject()
|
||||||
toJson(data, isLegacy)
|
toJson(data, isLegacy)
|
||||||
return data
|
return data
|
||||||
|
@ -211,7 +211,7 @@ class WorldLayout {
|
|||||||
return Starbound.gson.toJsonTree(SerializedForm(
|
return Starbound.gson.toJsonTree(SerializedForm(
|
||||||
worldSize, regionBlending, blockNoise, blendNoise,
|
worldSize, regionBlending, blockNoise, blendNoise,
|
||||||
playerStartSearchRegions, biomes.list, terrainSelectors.list,
|
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
|
)) as JsonObject
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,18 +1,13 @@
|
|||||||
package ru.dbotthepony.kstarbound.defs.world
|
package ru.dbotthepony.kstarbound.defs.world
|
||||||
|
|
||||||
import com.google.gson.JsonElement
|
|
||||||
import com.google.gson.JsonNull
|
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import ru.dbotthepony.kommons.gson.set
|
|
||||||
import ru.dbotthepony.kommons.util.Either
|
import ru.dbotthepony.kommons.util.Either
|
||||||
import ru.dbotthepony.kommons.vector.Vector2i
|
import ru.dbotthepony.kommons.vector.Vector2i
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
import ru.dbotthepony.kstarbound.fromJson
|
|
||||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||||
import ru.dbotthepony.kstarbound.world.Universe
|
import ru.dbotthepony.kstarbound.world.Universe
|
||||||
import ru.dbotthepony.kstarbound.world.UniversePos
|
import ru.dbotthepony.kstarbound.world.UniversePos
|
||||||
import ru.dbotthepony.kstarbound.world.WorldGeometry
|
import ru.dbotthepony.kstarbound.world.WorldGeometry
|
||||||
import kotlin.properties.Delegates
|
|
||||||
|
|
||||||
class WorldTemplate(val geometry: WorldGeometry) {
|
class WorldTemplate(val geometry: WorldGeometry) {
|
||||||
var seed: Long = 0L
|
var seed: Long = 0L
|
||||||
@ -58,7 +53,7 @@ class WorldTemplate(val geometry: WorldGeometry) {
|
|||||||
fun toJson(): JsonObject {
|
fun toJson(): JsonObject {
|
||||||
val data = Starbound.gson.toJsonTree(SerializedForm(
|
val data = Starbound.gson.toJsonTree(SerializedForm(
|
||||||
celestialParameters, worldParameters, skyParameters, seed,
|
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
|
worldLayout
|
||||||
)) as JsonObject
|
)) as JsonObject
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ abstract class NativeLegacy<NATIVE, LEGACY> {
|
|||||||
override fun write(out: JsonWriter, value: NativeLegacy<L, R>?) {
|
override fun write(out: JsonWriter, value: NativeLegacy<L, R>?) {
|
||||||
if (value == null)
|
if (value == null)
|
||||||
out.nullValue()
|
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)
|
right.write(out, value.legacy)
|
||||||
else
|
else
|
||||||
left.write(out, value.native)
|
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
|
package ru.dbotthepony.kstarbound.json
|
||||||
|
|
||||||
import com.google.gson.JsonElement
|
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.kommons.io.readBinaryString
|
||||||
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
|
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||||
import java.io.DataInputStream
|
import java.io.DataInputStream
|
||||||
|
|
||||||
|
@JsonFactory
|
||||||
data class VersionedJson(val identifier: String, val version: Int?, val content: JsonElement) {
|
data class VersionedJson(val identifier: String, val version: Int?, val content: JsonElement) {
|
||||||
constructor(data: DataInputStream) : this(
|
constructor(data: DataInputStream) : this(
|
||||||
data.readBinaryString(),
|
data.readBinaryString(),
|
||||||
data.read().let { if (it > 0) data.readInt() else null },
|
data.read().let { if (it > 0) data.readInt() else null },
|
||||||
data.readJsonElement())
|
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 {
|
companion object {
|
||||||
private val LOGGER = LogManager.getLogger()
|
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 builder = Builder(kclass)
|
||||||
val properties = kclass.declaredMembers.filterIsInstance<KProperty1<T, *>>()
|
val properties = kclass.declaredMembers.filterIsInstance<KProperty1<T, *>>()
|
||||||
|
|
||||||
@ -537,7 +537,7 @@ class FactoryAdapter<T : Any> private constructor(
|
|||||||
|
|
||||||
val bconfig = first[0] as JsonFactory
|
val bconfig = first[0] as JsonFactory
|
||||||
val kclass = raw.kotlin as KClass<T>
|
val kclass = raw.kotlin as KClass<T>
|
||||||
return createFor(kclass, bconfig, gson, stringInterner)
|
return createFor(kclass, gson, bconfig, stringInterner)
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
|
@ -3,12 +3,19 @@ package ru.dbotthepony.kstarbound.math
|
|||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.TypeAdapter
|
import com.google.gson.TypeAdapter
|
||||||
import com.google.gson.TypeAdapterFactory
|
import com.google.gson.TypeAdapterFactory
|
||||||
|
import com.google.gson.annotations.JsonAdapter
|
||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
import com.google.gson.stream.JsonReader
|
import com.google.gson.stream.JsonReader
|
||||||
import com.google.gson.stream.JsonWriter
|
import com.google.gson.stream.JsonWriter
|
||||||
import ru.dbotthepony.kommons.gson.consumeNull
|
import ru.dbotthepony.kommons.gson.consumeNull
|
||||||
import ru.dbotthepony.kommons.util.KOptional
|
import ru.dbotthepony.kommons.util.KOptional
|
||||||
import ru.dbotthepony.kommons.vector.Vector2d
|
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
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
private operator fun Vector2d.compareTo(other: Vector2d): Int {
|
private operator fun Vector2d.compareTo(other: Vector2d): Int {
|
||||||
@ -17,7 +24,10 @@ private operator fun Vector2d.compareTo(other: Vector2d): Int {
|
|||||||
return cmp
|
return cmp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonAdapter(Line2d.Adapter::class)
|
||||||
data class Line2d(val a: Vector2d, val b: Vector2d) {
|
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) {
|
data class Intersection(val intersects: Boolean, val point: KOptional<Vector2d>, val t: KOptional<Double>, val coincides: Boolean, val glances: Boolean) {
|
||||||
companion object {
|
companion object {
|
||||||
val EMPTY = Intersection(false, KOptional(), KOptional(), false, false)
|
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)
|
return Line2d(b, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun write(stream: DataOutputStream, isLegacy: Boolean) {
|
||||||
|
stream.writeStruct2d(a, isLegacy)
|
||||||
|
stream.writeStruct2d(b, isLegacy)
|
||||||
|
}
|
||||||
|
|
||||||
// original source of this intersection algorithm:
|
// original source of this intersection algorithm:
|
||||||
// https://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
|
// 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
|
// 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
|
return (other - a + difference() * proj).length
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object : TypeAdapterFactory {
|
class Adapter(gson: Gson) : TypeAdapter<Line2d>() {
|
||||||
const val NEAR_ZERO = Double.MIN_VALUE * 2.0
|
private val pair = gson.getAdapter<Pair<Vector2d, Vector2d>>()
|
||||||
const val NEAR_ONE = 1.0 - NEAR_ZERO
|
|
||||||
|
|
||||||
override fun <T : Any> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
|
override fun write(out: JsonWriter, value: Line2d) {
|
||||||
if (type.rawType == Line2d::class.java) {
|
pair.write(out, value.a to value.b)
|
||||||
val pair = gson.getAdapter(TypeToken.getParameterized(Vector2d::class.java, Vector2d::class.java)) as TypeAdapter<Pair<Vector2d, Vector2d>>
|
}
|
||||||
|
|
||||||
return object : TypeAdapter<Line2d>() {
|
override fun read(`in`: JsonReader): Line2d {
|
||||||
override fun write(out: JsonWriter, value: Line2d?) {
|
val (a, b) = pair.read(`in`)
|
||||||
if (value == null)
|
return Line2d(a, b)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
if (world == null) {
|
||||||
send(PlayerWarpResultPacket(false, request, false))
|
send(PlayerWarpResultPacket(false, request, false))
|
||||||
} else {
|
continue
|
||||||
try {
|
}
|
||||||
world.acceptClient(this, request).await()
|
|
||||||
} catch (err: Throwable) {
|
|
||||||
send(PlayerWarpResultPacket(false, request, false))
|
|
||||||
|
|
||||||
if (world == shipWorld) {
|
try {
|
||||||
disconnect("ShipWorld refused to accept its owner: $err")
|
world.acceptClient(this, request).await()
|
||||||
} else {
|
} catch (err: Throwable) {
|
||||||
enqueueWarp(returnWarp ?: WarpAlias.OwnShip)
|
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.api.ImmutableCell
|
||||||
import ru.dbotthepony.kstarbound.world.entities.AbstractEntity
|
import ru.dbotthepony.kstarbound.world.entities.AbstractEntity
|
||||||
import ru.dbotthepony.kstarbound.world.entities.player.PlayerEntity
|
import ru.dbotthepony.kstarbound.world.entities.player.PlayerEntity
|
||||||
import ru.dbotthepony.kstarbound.world.entities.tile.WorldObject
|
|
||||||
import java.io.DataOutputStream
|
import java.io.DataOutputStream
|
||||||
import java.util.HashMap
|
import java.util.HashMap
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue
|
import java.util.concurrent.ConcurrentLinkedQueue
|
||||||
@ -91,7 +90,7 @@ class ServerWorldTracker(val world: ServerWorld, val client: ServerConnection, p
|
|||||||
|
|
||||||
if (client.isLegacy) {
|
if (client.isLegacy) {
|
||||||
client.send(WorldStartPacket(
|
client.send(WorldStartPacket(
|
||||||
templateData = Starbound.writeLegacyJson { world.template.toJson() },
|
templateData = Starbound.legacyJson { world.template.toJson() },
|
||||||
skyData = skyData.toByteArray(),
|
skyData = skyData.toByteArray(),
|
||||||
weatherData = ByteArray(0),
|
weatherData = ByteArray(0),
|
||||||
playerStart = playerStart,
|
playerStart = playerStart,
|
||||||
@ -105,7 +104,7 @@ class ServerWorldTracker(val world: ServerWorld, val client: ServerConnection, p
|
|||||||
localInterpolationMode = false,
|
localInterpolationMode = false,
|
||||||
))
|
))
|
||||||
|
|
||||||
Starbound.writeLegacyJson {
|
Starbound.legacyJson {
|
||||||
client.sendAndFlush(CentralStructureUpdatePacket(Starbound.gson.toJsonTree(world.centralStructure)))
|
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.JsonSyntaxException
|
||||||
import com.google.gson.TypeAdapter
|
import com.google.gson.TypeAdapter
|
||||||
import com.google.gson.TypeAdapterFactory
|
import com.google.gson.TypeAdapterFactory
|
||||||
|
import com.google.gson.annotations.JsonAdapter
|
||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
import com.google.gson.stream.JsonReader
|
import com.google.gson.stream.JsonReader
|
||||||
import com.google.gson.stream.JsonToken
|
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
|
* No validity checking is done here, any coordinate to any body whether it
|
||||||
* exists in a specific universe or not can be expressed.
|
* 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) {
|
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())
|
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 CODEC = nativeCodec(::UniversePos, UniversePos::write)
|
||||||
val LEGACY_CODEC = legacyCodec(::UniversePos, UniversePos::write)
|
val LEGACY_CODEC = legacyCodec(::UniversePos, UniversePos::write)
|
||||||
|
|
||||||
private val splitter = Regex("[ _:]")
|
private val splitter = Regex("[ _:]")
|
||||||
val ZERO = UniversePos()
|
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