Больше свойств у прототипа предмета

This commit is contained in:
DBotThePony 2022-12-29 16:32:24 +07:00
parent 5f3c33d9cb
commit 5e29072fcf
Signed by: DBot
GPG Key ID: DCC23B5715498507
2 changed files with 141 additions and 33 deletions
src/main/kotlin/ru/dbotthepony/kstarbound

View File

@ -3,6 +3,8 @@ package ru.dbotthepony.kstarbound.defs.item
import com.google.gson.GsonBuilder import com.google.gson.GsonBuilder
import ru.dbotthepony.kstarbound.io.KConcreteTypeAdapter import ru.dbotthepony.kstarbound.io.KConcreteTypeAdapter
import ru.dbotthepony.kstarbound.registerTypeAdapter import ru.dbotthepony.kstarbound.registerTypeAdapter
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
import ru.dbotthepony.kvector.vector.nint.Vector2i
data class ItemDefinition( data class ItemDefinition(
val itemName: String, val itemName: String,
@ -12,8 +14,71 @@ data class ItemDefinition(
val inventoryIcon: String? = null, val inventoryIcon: String? = null,
val description: String = "...", val description: String = "...",
val shortdescription: String = "...", val shortdescription: String = "...",
val itemTags: List<String> = listOf() val itemTags: List<String> = listOf(),
val learnBlueprintsOnPickup: List<String> = listOf(),
val maxStack: Long = 9999L,
val eventCategory: String? = null,
val consumeOnPickup: Boolean = false,
val pickupQuestTemplates: List<String> = listOf(),
/**
* Используется в костях-ископаемых
*/
val race: String? = null,
val displayImage: String? = null,
val displayoffset: Vector2d? = null,
/**
* Используется в костях-ископаемых
*/
val fossilSetName: String? = null,
/**
* Используется в костях-ископаемых
*/
val setIndex: Int? = null,
/**
* Используется в костях-ископаемых
*/
val setCount: Int? = null,
/**
* Используется в костях-ископаемых
*/
val setCollectables: Map<String, String>? = null,
/**
* Используется в костях-ископаемых
*/
val completeFossilIcon: String? = null,
/**
* Используется в костях-ископаемых
*/
val completeFossilObject: String? = null,
/**
* Используется в костях-ископаемых
*/
val completeSetDescriptions: FossilSetDescription? = null,
/**
* Заставляет SAIL болтать
*/
val radioMessagesOnPickup: List<String> = listOf(),
/**
* Топливо корабля
*/
val fuelAmount: Long? = null,
) { ) {
data class FossilSetDescription(
val price: Long = 0L,
val shortdescription: String = "...",
val description: String = "..."
)
companion object { companion object {
val ADAPTER = KConcreteTypeAdapter.Builder(ItemDefinition::class) val ADAPTER = KConcreteTypeAdapter.Builder(ItemDefinition::class)
.plain(ItemDefinition::itemName) .plain(ItemDefinition::itemName)
@ -23,11 +88,40 @@ data class ItemDefinition(
.plain(ItemDefinition::inventoryIcon) .plain(ItemDefinition::inventoryIcon)
.plain(ItemDefinition::description) .plain(ItemDefinition::description)
.plain(ItemDefinition::shortdescription) .plain(ItemDefinition::shortdescription)
.list(ItemDefinition::itemTags) .list(ItemDefinition::itemTags)
.list(ItemDefinition::learnBlueprintsOnPickup)
.plain(ItemDefinition::maxStack)
.plain(ItemDefinition::eventCategory)
.plain(ItemDefinition::consumeOnPickup)
.list(ItemDefinition::pickupQuestTemplates)
.plain(ItemDefinition::race)
.plain(ItemDefinition::displayImage)
.plain(ItemDefinition::displayoffset)
.plain(ItemDefinition::fossilSetName)
.plain(ItemDefinition::setIndex)
.plain(ItemDefinition::setCount)
.map(ItemDefinition::setCollectables, String::class)
.plain(ItemDefinition::completeFossilIcon)
.plain(ItemDefinition::completeFossilObject)
.plain(ItemDefinition::completeSetDescriptions)
.list(ItemDefinition::radioMessagesOnPickup)
.plain(ItemDefinition::fuelAmount)
.build()
val FOSSIL_ADAPTER = KConcreteTypeAdapter.Builder(FossilSetDescription::class)
.plain(FossilSetDescription::price)
.plain(FossilSetDescription::shortdescription)
.plain(FossilSetDescription::description)
.build() .build()
fun registerGson(gsonBuilder: GsonBuilder) { fun registerGson(gsonBuilder: GsonBuilder) {
gsonBuilder.registerTypeAdapter(ADAPTER) gsonBuilder.registerTypeAdapter(ADAPTER)
gsonBuilder.registerTypeAdapter(FOSSIL_ADAPTER)
} }
} }
} }

View File

@ -2,8 +2,8 @@ package ru.dbotthepony.kstarbound.io
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.common.collect.Interner
import com.google.common.collect.Interners import com.google.common.collect.Interners
import com.google.gson.GsonBuilder
import com.google.gson.JsonSyntaxException import com.google.gson.JsonSyntaxException
import com.google.gson.TypeAdapter import com.google.gson.TypeAdapter
import com.google.gson.internal.bind.TypeAdapters import com.google.gson.internal.bind.TypeAdapters
@ -18,19 +18,19 @@ import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.getValue import ru.dbotthepony.kstarbound.getValue
import ru.dbotthepony.kstarbound.setValue import ru.dbotthepony.kstarbound.setValue
import java.lang.reflect.Constructor import java.lang.reflect.Constructor
import java.util.BitSet
import kotlin.jvm.internal.DefaultConstructorMarker import kotlin.jvm.internal.DefaultConstructorMarker
import kotlin.properties.Delegates
import kotlin.reflect.* import kotlin.reflect.*
import kotlin.reflect.full.isSuperclassOf import kotlin.reflect.full.isSuperclassOf
import kotlin.reflect.full.isSupertypeOf import kotlin.reflect.full.isSupertypeOf
import kotlin.reflect.full.memberProperties import kotlin.reflect.full.memberProperties
@Suppress("unchecked_cast") @Suppress("unchecked_cast")
private fun <T> resolveBound(bound: Class<T>): TypeAdapter<T> { private fun <T> resolveBound(bound: Class<T>, stringAdapter: TypeAdapter<String>): TypeAdapter<T> {
return when (bound) { return when (bound) {
Float::class.java -> TypeAdapters.FLOAT as TypeAdapter<T> Float::class.java -> TypeAdapters.FLOAT as TypeAdapter<T>
Double::class.java -> TypeAdapters.DOUBLE as TypeAdapter<T> Double::class.java -> TypeAdapters.DOUBLE as TypeAdapter<T>
String::class.java -> TypeAdapters.STRING as TypeAdapter<T> String::class.java -> stringAdapter as TypeAdapter<T>
Int::class.java -> TypeAdapters.INTEGER as TypeAdapter<T> Int::class.java -> TypeAdapters.INTEGER as TypeAdapter<T>
Long::class.java -> TypeAdapters.LONG as TypeAdapter<T> Long::class.java -> TypeAdapters.LONG as TypeAdapter<T>
Boolean::class.java -> TypeAdapters.BOOLEAN as TypeAdapter<T> Boolean::class.java -> TypeAdapters.BOOLEAN as TypeAdapter<T>
@ -38,10 +38,8 @@ private fun <T> resolveBound(bound: Class<T>): TypeAdapter<T> {
} }
} }
class ListAdapter<T>(private val bound: Class<T>) : TypeAdapter<List<T>>() { private class ListAdapter<T>(private val bound: Class<T>, private val stringAdapter: TypeAdapter<String>) : TypeAdapter<List<T>>() {
private val resolvedBound by lazy { private val resolvedBound by lazy { resolveBound(bound, stringAdapter) }
resolveBound(bound)
}
override fun write(out: JsonWriter, value: List<T>) { override fun write(out: JsonWriter, value: List<T>) {
out.beginArray() out.beginArray()
@ -72,14 +70,9 @@ class ListAdapter<T>(private val bound: Class<T>) : TypeAdapter<List<T>>() {
} }
} }
class MapAdapter<K, V>(private val boundKey: Class<K>, private val boundValue: Class<V>) : TypeAdapter<Map<K, V>>() { private class MapAdapter<K, V>(private val boundKey: Class<K>, private val boundValue: Class<V>, private val stringAdapter: TypeAdapter<String>) : TypeAdapter<Map<K, V>>() {
private val resolvedKey by lazy { private val resolvedKey by lazy { resolveBound(boundKey, stringAdapter) }
resolveBound(boundKey) private val resolvedValue by lazy { resolveBound(boundValue, stringAdapter) }
}
private val resolvedValue by lazy {
resolveBound(boundValue)
}
override fun write(out: JsonWriter, value: Map<K, V>) { override fun write(out: JsonWriter, value: Map<K, V>) {
out.beginArray() out.beginArray()
@ -117,10 +110,8 @@ class MapAdapter<K, V>(private val boundKey: Class<K>, private val boundValue: C
} }
} }
class StringMapAdapter<V>(private val bound: Class<V>) : TypeAdapter<Map<String, V>>() { private class StringMapAdapter<V>(private val bound: Class<V>, private val stringAdapter: TypeAdapter<String>, private val interner: () -> Interner<String>) : TypeAdapter<Map<String, V>>() {
private val resolvedBound by lazy { private val resolvedBound by lazy { resolveBound(bound, stringAdapter) }
resolveBound(bound)
}
override fun write(out: JsonWriter, value: Map<String, V>) { override fun write(out: JsonWriter, value: Map<String, V>) {
val resolvedBound = resolvedBound val resolvedBound = resolvedBound
@ -143,7 +134,7 @@ class StringMapAdapter<V>(private val bound: Class<V>) : TypeAdapter<Map<String,
val resolvedBound = resolvedBound val resolvedBound = resolvedBound
while (reader.peek() != JsonToken.END_OBJECT) { while (reader.peek() != JsonToken.END_OBJECT) {
builder.put(reader.nextName(), resolvedBound.read(reader)) builder.put(interner.invoke().intern(reader.nextName()), resolvedBound.read(reader))
} }
reader.endObject() reader.endObject()
@ -153,7 +144,7 @@ class StringMapAdapter<V>(private val bound: Class<V>) : TypeAdapter<Map<String,
} }
private class LazyTypeProvider<T : Any?>(private val bound: Class<T>) : TypeAdapter<T>() { private class LazyTypeProvider<T : Any?>(private val bound: Class<T>) : TypeAdapter<T>() {
private val resolved by lazy { resolveBound(bound) } private val resolved by lazy { resolveBound(bound, TypeAdapters.STRING) }
override fun write(out: JsonWriter, value: T) { override fun write(out: JsonWriter, value: T) {
resolved.write(out, value) resolved.write(out, value)
@ -441,6 +432,17 @@ class KConcreteTypeAdapter<T : Any>(
*/ */
class Builder<T : Any>(val clazz: KClass<T>) { class Builder<T : Any>(val clazz: KClass<T>) {
private val types = ArrayList<Pair<KProperty1<T, *>, TypeAdapter<*>>>() private val types = ArrayList<Pair<KProperty1<T, *>, TypeAdapter<*>>>()
private var interner by Delegates.notNull<Interner<String>>()
private val internedStringAdapter: TypeAdapter<String> = object : TypeAdapter<String>() {
override fun write(out: JsonWriter, value: String) {
return TypeAdapters.STRING.write(out, value)
}
override fun read(`in`: JsonReader): String {
return interner.intern(TypeAdapters.STRING.read(`in`))
}
}
/** /**
* Добавляет поле с определённым адаптером * Добавляет поле с определённым адаптером
@ -467,7 +469,7 @@ class KConcreteTypeAdapter<T : Any>(
} else if (classifier.isSuperclassOf(Long::class)) { } else if (classifier.isSuperclassOf(Long::class)) {
types.add(field to TypeAdapters.LONG) types.add(field to TypeAdapters.LONG)
} else if (classifier.isSuperclassOf(String::class)) { } else if (classifier.isSuperclassOf(String::class)) {
types.add(field to TypeAdapters.STRING) types.add(field to internedStringAdapter)
} else if (classifier.isSuperclassOf(Boolean::class)) { } else if (classifier.isSuperclassOf(Boolean::class)) {
types.add(field to TypeAdapters.BOOLEAN) types.add(field to TypeAdapters.BOOLEAN)
} else { } else {
@ -498,7 +500,7 @@ class KConcreteTypeAdapter<T : Any>(
* Список неизменяем (создаётся объект [ImmutableList]) * Список неизменяем (создаётся объект [ImmutableList])
*/ */
fun <V : Any> list(field: KProperty1<T, List<V>?>, type: Class<V>): Builder<T> { fun <V : Any> list(field: KProperty1<T, List<V>?>, type: Class<V>): Builder<T> {
types.add(field to ListAdapter(type)) types.add(field to ListAdapter(type, internedStringAdapter))
return this return this
} }
@ -517,7 +519,7 @@ class KConcreteTypeAdapter<T : Any>(
* Таблица неизменяема (создаётся объект [ImmutableMap]) * Таблица неизменяема (создаётся объект [ImmutableMap])
*/ */
fun <K, V> map(field: KProperty1<T, Map<K, V>>, keyType: Class<K>, valueType: Class<V>): Builder<T> { fun <K, V> map(field: KProperty1<T, Map<K, V>>, keyType: Class<K>, valueType: Class<V>): Builder<T> {
types.add(field to MapAdapter(keyType, valueType)) types.add(field to MapAdapter(keyType, valueType, internedStringAdapter))
return this return this
} }
@ -526,8 +528,8 @@ class KConcreteTypeAdapter<T : Any>(
* *
* Таблица неизменяема (создаётся объект [ImmutableMap]) * Таблица неизменяема (создаётся объект [ImmutableMap])
*/ */
fun <K : Any, V : Any> map(field: KProperty1<T, Map<K, V>>, keyType: KClass<K>, valueType: KClass<V>): Builder<T> { fun <K : Any, V : Any> map(field: KProperty1<T, Map<K, V>?>, keyType: KClass<K>, valueType: KClass<V>): Builder<T> {
types.add(field to MapAdapter(keyType.java, valueType.java)) types.add(field to MapAdapter(keyType.java, valueType.java, internedStringAdapter))
return this return this
} }
@ -536,8 +538,8 @@ class KConcreteTypeAdapter<T : Any>(
* *
* Таблица неизменяема (создаётся объект [ImmutableMap]) * Таблица неизменяема (создаётся объект [ImmutableMap])
*/ */
fun <V> map(field: KProperty1<T, Map<String, V>>, valueType: Class<V>): Builder<T> { fun <V> map(field: KProperty1<T, Map<String, V>?>, valueType: Class<V>): Builder<T> {
types.add(field to StringMapAdapter(valueType)) types.add(field to StringMapAdapter(valueType, internedStringAdapter, ::interner))
return this return this
} }
@ -546,17 +548,29 @@ class KConcreteTypeAdapter<T : Any>(
* *
* Таблица неизменяема (создаётся объект [ImmutableMap]) * Таблица неизменяема (создаётся объект [ImmutableMap])
*/ */
fun <V : Any> map(field: KProperty1<T, Map<String, V>>, valueType: KClass<V>): Builder<T> { fun <V : Any> map(field: KProperty1<T, Map<String, V>?>, valueType: KClass<V>): Builder<T> {
types.add(field to StringMapAdapter(valueType.java)) types.add(field to StringMapAdapter(valueType.java, internedStringAdapter, ::interner))
return this return this
} }
fun build(asList: Boolean = false): KConcreteTypeAdapter<T> { fun build(asList: Boolean = false): KConcreteTypeAdapter<T> {
return KConcreteTypeAdapter(clazz, ImmutableList.copyOf(types), asList = asList) return KConcreteTypeAdapter(clazz, ImmutableList.copyOf(types), asList = asList).also {
interner = it.internedStrings
}
} }
} }
companion object { companion object {
private val LOGGER = LogManager.getLogger() private val LOGGER = LogManager.getLogger()
/*fun <T : Any> simple(clazz: KClass<T>, asList: Boolean = false): KConcreteTypeAdapter<T> {
val builder = Builder(clazz)
for (argument in clazz.primaryConstructor!!.parameters) {
builder.plain(argument.)
}
return builder.build(asList = asList)
}*/
} }
} }