This commit is contained in:
DBotThePony 2023-01-22 20:29:48 +07:00
parent 2d7681628d
commit 2d8e3a7ff5
Signed by: DBot
GPG Key ID: DCC23B5715498507
10 changed files with 234 additions and 5 deletions

View File

@ -1,10 +1,13 @@
package ru.dbotthepony.kstarbound package ru.dbotthepony.kstarbound
import com.google.common.collect.ImmutableList
import com.google.gson.GsonBuilder
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import org.lwjgl.Version import org.lwjgl.Version
import org.lwjgl.glfw.GLFW.glfwSetWindowShouldClose import org.lwjgl.glfw.GLFW.glfwSetWindowShouldClose
import ru.dbotthepony.kstarbound.client.StarboundClient import ru.dbotthepony.kstarbound.client.StarboundClient
import ru.dbotthepony.kstarbound.io.* import ru.dbotthepony.kstarbound.io.*
import ru.dbotthepony.kstarbound.io.json.factory.ImmutableCollectionAdapterFactory
import ru.dbotthepony.kstarbound.world.ChunkPos import ru.dbotthepony.kstarbound.world.ChunkPos
import ru.dbotthepony.kstarbound.world.entities.ItemEntity import ru.dbotthepony.kstarbound.world.entities.ItemEntity
import ru.dbotthepony.kstarbound.world.entities.PlayerEntity import ru.dbotthepony.kstarbound.world.entities.PlayerEntity

View File

@ -46,6 +46,7 @@ import ru.dbotthepony.kstarbound.io.json.Vector2dTypeAdapter
import ru.dbotthepony.kstarbound.io.json.Vector2fTypeAdapter import ru.dbotthepony.kstarbound.io.json.Vector2fTypeAdapter
import ru.dbotthepony.kstarbound.io.json.Vector2iTypeAdapter import ru.dbotthepony.kstarbound.io.json.Vector2iTypeAdapter
import ru.dbotthepony.kstarbound.io.json.Vector4iTypeAdapter import ru.dbotthepony.kstarbound.io.json.Vector4iTypeAdapter
import ru.dbotthepony.kstarbound.io.json.factory.ImmutableCollectionAdapterFactory
import ru.dbotthepony.kstarbound.math.* import ru.dbotthepony.kstarbound.math.*
import ru.dbotthepony.kvector.vector.Color import ru.dbotthepony.kvector.vector.Color
import java.io.* import java.io.*
@ -145,7 +146,9 @@ object Starbound {
.setDateFormat(DateFormat.LONG) .setDateFormat(DateFormat.LONG)
.setFieldNamingPolicy(FieldNamingPolicy.IDENTITY) .setFieldNamingPolicy(FieldNamingPolicy.IDENTITY)
.setPrettyPrinting() .setPrettyPrinting()
.registerTypeAdapter(Color::class.java, ColorTypeAdapter.nullSafe()) .registerTypeAdapter(ColorTypeAdapter.nullSafe())
.registerTypeAdapterFactory(ImmutableCollectionAdapterFactory)
// чтоб строки всегда intern'ились // чтоб строки всегда intern'ились
.registerTypeAdapter(NULLABLE_STRING_ADAPTER) .registerTypeAdapter(NULLABLE_STRING_ADAPTER)

View File

@ -2,11 +2,14 @@ package ru.dbotthepony.kstarbound.io.json.builder
import com.google.common.collect.ImmutableMap import com.google.common.collect.ImmutableMap
import com.google.common.collect.ImmutableSet import com.google.common.collect.ImmutableSet
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.TypeAdapterFactory
import com.google.gson.internal.bind.JsonTreeReader import com.google.gson.internal.bind.JsonTreeReader
import com.google.gson.internal.bind.TypeAdapters import com.google.gson.internal.bind.TypeAdapters
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
import com.google.gson.stream.JsonWriter import com.google.gson.stream.JsonWriter
@ -241,13 +244,17 @@ class BuilderAdapter<T : Any> private constructor(
var mustBePresent: Boolean? = null var mustBePresent: Boolean? = null
} }
class Builder<T : Any>(val factory: () -> T, vararg fields: KMutableProperty1<T, *>) { class Builder<T : Any>(val factory: () -> T, vararg fields: KMutableProperty1<T, *>) : TypeAdapterFactory {
private val properties = ArrayList<WrappedProperty<T, *>>() private val properties = ArrayList<WrappedProperty<T, *>>()
private val flatProperties = ArrayList<WrappedProperty<T, *>>() private val flatProperties = ArrayList<WrappedProperty<T, *>>()
private val ignoreKeys = ObjectArraySet<String>() private val ignoreKeys = ObjectArraySet<String>()
var extraPropertiesAreFatal = false var extraPropertiesAreFatal = false
var logMisses: Boolean? = null var logMisses: Boolean? = null
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
return null
}
/** /**
* Являются ли "лишние" ключи в JSON структуре ошибкой. * Являются ли "лишние" ключи в JSON структуре ошибкой.
* *

View File

@ -2,13 +2,16 @@ package ru.dbotthepony.kstarbound.io.json.builder
import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap import com.google.common.collect.ImmutableMap
import com.google.gson.Gson
import com.google.gson.JsonArray import com.google.gson.JsonArray
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.google.gson.JsonParseException import com.google.gson.JsonParseException
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.internal.bind.JsonTreeReader import com.google.gson.internal.bind.JsonTreeReader
import com.google.gson.internal.bind.TypeAdapters import com.google.gson.internal.bind.TypeAdapters
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
import com.google.gson.stream.JsonWriter import com.google.gson.stream.JsonWriter
@ -382,7 +385,7 @@ class FactoryAdapter<T : Any> private constructor(
/** /**
* Позволяет построить класс [FactoryAdapter] на основе заданных параметров * Позволяет построить класс [FactoryAdapter] на основе заданных параметров
*/ */
class Builder<T : Any>(val clazz: KClass<T>) { class Builder<T : Any>(val clazz: KClass<T>) : TypeAdapterFactory {
constructor(clazz: KClass<T>, vararg fields: KProperty1<T, *>) : this(clazz) { constructor(clazz: KClass<T>, vararg fields: KProperty1<T, *>) : this(clazz) {
for (field in fields) { for (field in fields) {
auto(field) auto(field)
@ -391,6 +394,14 @@ class FactoryAdapter<T : Any> private constructor(
private val types = ArrayList<PackedProperty<T, *>>() private val types = ArrayList<PackedProperty<T, *>>()
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
if (type.rawType == clazz.java) {
return build() as TypeAdapter<T>
}
return null
}
/** /**
* Принимает ли класс *последним* аргументом JSON структуру * Принимает ли класс *последним* аргументом JSON структуру
* *

View File

@ -0,0 +1,37 @@
package ru.dbotthepony.kstarbound.io.json.factory
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
import com.google.gson.JsonSyntaxException
import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
class ImmutableArrayMapTypeAdapter<K, V>(val keyAdapter: TypeAdapter<K>, val elementAdapter: TypeAdapter<V>) : TypeAdapter<ImmutableMap<K, V>>() {
override fun write(out: JsonWriter, value: ImmutableMap<K, V>) {
out.beginArray()
for ((k, v) in value) {
keyAdapter.write(out, k)
elementAdapter.write(out, v)
}
out.endArray()
}
override fun read(reader: JsonReader): ImmutableMap<K, V> {
reader.beginArray()
val builder = ImmutableMap.Builder<K, V>()
while (reader.peek() != JsonToken.END_OBJECT) {
builder.put(
keyAdapter.read(reader) ?: throw JsonSyntaxException("Nulls as keys are not allowed, near ${reader.path}"),
elementAdapter.read(reader) ?: throw JsonSyntaxException("Nulls as values are not allowed, near ${reader.path}"))
}
reader.endArray()
return builder.build()
}
}

View File

@ -0,0 +1,42 @@
package ru.dbotthepony.kstarbound.io.json.factory
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
import com.google.common.collect.ImmutableSet
import com.google.gson.Gson
import com.google.gson.TypeAdapter
import com.google.gson.TypeAdapterFactory
import com.google.gson.internal.`$Gson$Types`
import com.google.gson.reflect.TypeToken
@Suppress("unchecked_cast")
object ImmutableCollectionAdapterFactory : TypeAdapterFactory {
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
when (type.rawType) {
ImmutableList::class.java -> {
val elementType = `$Gson$Types`.getCollectionElementType(type.type, type.rawType)
return ImmutableListTypeAdapter(gson.getAdapter(TypeToken.get(elementType))) as TypeAdapter<T>
}
ImmutableSet::class.java -> {
val elementType = `$Gson$Types`.getCollectionElementType(type.type, type.rawType)
return ImmutableSetTypeAdapter(gson.getAdapter(TypeToken.get(elementType))) as TypeAdapter<T>
}
ImmutableMap::class.java -> {
val (elementType0, elementType1) = `$Gson$Types`.getMapKeyAndValueTypes(type.type, type.rawType)
if (`$Gson$Types`.getRawType(elementType0) == String::class.java) {
return ImmutableMapTypeAdapter(gson.getAdapter(TypeToken.get(elementType1))) as TypeAdapter<T>
}
return ImmutableArrayMapTypeAdapter(
gson.getAdapter(TypeToken.get(elementType0)),
gson.getAdapter(TypeToken.get(elementType1))
) as TypeAdapter<T>
}
else -> return null
}
}
}

View File

@ -0,0 +1,45 @@
package ru.dbotthepony.kstarbound.io.json.factory
import com.google.common.collect.ImmutableList
import com.google.gson.JsonSyntaxException
import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
class ImmutableListTypeAdapter<E>(val elementAdapter: TypeAdapter<E>) : TypeAdapter<ImmutableList<E>>() {
override fun write(out: JsonWriter, value: ImmutableList<E>) {
if (value.size == 1) {
elementAdapter.write(out, value[0])
return
}
out.beginArray()
for (v in value) {
elementAdapter.write(out, v)
}
out.endArray()
}
override fun read(reader: JsonReader): ImmutableList<E> {
if (reader.peek() != JsonToken.BEGIN_ARRAY) {
// не массив, возможно упрощение структуры "a": [value] -> "a": value
return ImmutableList.of(elementAdapter.read(reader) ?: throw JsonSyntaxException("List does not accept nulls"))
}
reader.beginArray()
val builder = ImmutableList.Builder<E>()
while (reader.peek() != JsonToken.END_ARRAY) {
val readObject = elementAdapter.read(reader) ?: throw JsonSyntaxException("List does not accept nulls")
builder.add(readObject)
}
reader.endArray()
return builder.build()
}
}

View File

@ -0,0 +1,35 @@
package ru.dbotthepony.kstarbound.io.json.factory
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
import com.google.gson.JsonSyntaxException
import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
class ImmutableMapTypeAdapter<V>(val elementAdapter: TypeAdapter<V>) : TypeAdapter<ImmutableMap<String, V>>() {
override fun write(out: JsonWriter, value: ImmutableMap<String, V>) {
out.beginObject()
for ((k, v) in value) {
out.name(k)
elementAdapter.write(out, v)
}
out.endObject()
}
override fun read(reader: JsonReader): ImmutableMap<String, V> {
reader.beginObject()
val builder = ImmutableMap.Builder<String, V>()
while (reader.peek() != JsonToken.END_OBJECT) {
builder.put(reader.nextName(), elementAdapter.read(reader) ?: throw JsonSyntaxException("Nulls are not allowed, near ${reader.path}"))
}
reader.endObject()
return builder.build()
}
}

View File

@ -0,0 +1,46 @@
package ru.dbotthepony.kstarbound.io.json.factory
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableSet
import com.google.gson.JsonSyntaxException
import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
class ImmutableSetTypeAdapter<E>(val elementAdapter: TypeAdapter<E>) : TypeAdapter<ImmutableSet<E>>() {
override fun write(out: JsonWriter, value: ImmutableSet<E>) {
if (value.size == 1) {
elementAdapter.write(out, value.first())
return
}
out.beginArray()
for (v in value) {
elementAdapter.write(out, v)
}
out.endArray()
}
override fun read(reader: JsonReader): ImmutableSet<E> {
if (reader.peek() != JsonToken.BEGIN_ARRAY) {
// не массив, возможно упрощение структуры "a": [value] -> "a": value
return ImmutableSet.of(elementAdapter.read(reader) ?: throw JsonSyntaxException("List does not accept nulls"))
}
reader.beginArray()
val builder = ImmutableSet.Builder<E>()
while (reader.peek() != JsonToken.END_ARRAY) {
val readObject = elementAdapter.read(reader) ?: throw JsonSyntaxException("List does not accept nulls")
builder.add(readObject)
}
reader.endArray()
return builder.build()
}
}

View File

@ -5,8 +5,8 @@ import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter import com.google.gson.stream.JsonWriter
import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.Starbound
class LazyTypeProvider<T : Any?>(private val bound: Class<T>) : TypeAdapter<T>() { class LazyTypeProvider<T : Any?>(private val bound: Class<T>, private val resolver: (Class<T>) -> TypeAdapter<T> = Starbound::getTypeAdapter) : TypeAdapter<T>() {
private val resolved by lazy { Starbound.getTypeAdapter(bound) } private val resolved by lazy { resolver(bound) }
override fun write(out: JsonWriter, value: T) { override fun write(out: JsonWriter, value: T) {
resolved.write(out, value) resolved.write(out, value)