From 176ca9db659683a118ec63579152bc3561a78c54 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Thu, 29 Dec 2022 15:34:35 +0700 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=BE=D0=B4=D0=B3=D1=80=D1=83=D0=B7?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=BF=D1=80=D0=BE=D1=82=D0=BE=D1=82=D0=B8=D0=BF?= =?UTF-8?q?=D0=BE=D0=B2=20=D0=BF=D1=80=D0=B5=D0=B4=D0=BC=D0=B5=D1=82=D0=BE?= =?UTF-8?q?=D0=B2,=20=D0=BD=D0=B5=D0=BC=D0=BD=D0=BE=D0=B3=D0=BE=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BC=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D1=80=D0=B8=D0=B5?= =?UTF-8?q?=D0=B2=20=D0=B2=20=D0=BA=D0=BE=D0=B4=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/ru/dbotthepony/kstarbound/Ext.kt | 9 ++++ .../ru/dbotthepony/kstarbound/Starbound.kt | 43 +++++++++++++++---- .../kstarbound/defs/item/ItemDefinition.kt | 33 ++++++++++++++ .../kstarbound/defs/item/ItemRarity.kt | 32 ++++++++++++++ .../kstarbound/io/CustomEnumTypeAdapter.kt | 2 +- .../kstarbound/io/KConcreteTypeAdapter.kt | 14 +++++- 6 files changed, 122 insertions(+), 11 deletions(-) create mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/ItemDefinition.kt create mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/ItemRarity.kt diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Ext.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Ext.kt index f160e5e2..02a2ab32 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Ext.kt @@ -4,9 +4,18 @@ import com.google.gson.GsonBuilder import com.google.gson.TypeAdapter import java.util.Arrays import java.util.stream.Stream +import kotlin.reflect.KProperty inline fun GsonBuilder.registerTypeAdapter(adapter: TypeAdapter): GsonBuilder { return registerTypeAdapter(T::class.java, adapter) } fun Array.stream(): Stream = Arrays.stream(this) + +operator fun ThreadLocal.getValue(thisRef: Any, property: KProperty<*>): T? { + return get() +} + +operator fun ThreadLocal.setValue(thisRef: Any, property: KProperty<*>, value: T?) { + set(value) +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt index d669bed1..b1e092ff 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt @@ -8,6 +8,8 @@ import ru.dbotthepony.kstarbound.api.NonExistingFile import ru.dbotthepony.kstarbound.api.PhysicalFile import ru.dbotthepony.kstarbound.api.explore import ru.dbotthepony.kstarbound.defs.* +import ru.dbotthepony.kstarbound.defs.item.ItemDefinition +import ru.dbotthepony.kstarbound.defs.item.ItemRarity import ru.dbotthepony.kstarbound.defs.liquid.LiquidDefinition import ru.dbotthepony.kstarbound.defs.projectile.* import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier @@ -42,14 +44,10 @@ const val PIXELS_IN_STARBOUND_UNITf = 8.0f object Starbound { private val LOGGER = LogManager.getLogger() - private val _readingFolder = ThreadLocal() - /** * Служит переменной для указания из какой папки происходит чтение asset'а в данном потоке */ - var readingFolder: String? - get() = _readingFolder.get() - private set(value) { _readingFolder.set(value) } + var readingFolder by ThreadLocal() private val tiles = HashMap() private val tilesByMaterialID = Int2ObjectAVLTreeMap() @@ -64,6 +62,8 @@ object Starbound { private val parallax = HashMap() private val functions = HashMap() + private val items = HashMap() + val liquidAccess: Map = Collections.unmodifiableMap(liquid) val liquidByIDAccess: Map = Collections.unmodifiableMap(liquidByID) val tileModifiersAccess: Map = Collections.unmodifiableMap(tileModifiers) @@ -73,6 +73,7 @@ object Starbound { val projectilesAccess: Map = Collections.unmodifiableMap(projectiles) val parallaxAccess: Map = Collections.unmodifiableMap(parallax) val functionsAccess: Map = Collections.unmodifiableMap(functions) + val itemAccess: Map = Collections.unmodifiableMap(items) val gson: Gson = GsonBuilder() .enableComplexMapKeySerialization() @@ -100,6 +101,8 @@ object Starbound { .also(RenderTemplate::registerGson) .also(TileDefinition::registerGson) .also(LiquidDefinition::registerGson) + .also(ItemDefinition::registerGson) + .also(ItemRarity::registerGson) .registerTypeAdapter(DamageType::class.java, CustomEnumTypeAdapter(DamageType.values()).nullSafe()) @@ -239,6 +242,7 @@ object Starbound { loadStage(callback, this::loadParallax, "parallax definitions") loadStage(callback, this::loadMaterialModifiers, "material modifier definitions") loadStage(callback, this::loadLiquidDefinitions, "liquid definitions") + loadStage(callback, this::loadItemDefinitions, "item definitions") initializing = false initialized = true @@ -339,7 +343,7 @@ object Starbound { private fun loadParallax(callback: (String) -> Unit) { for (fs in fileSystems) { - for (listedFile in fs.explore().filter { it.isFile }.filter { it.name.endsWith(".parallax") }) { + for (listedFile in fs.explore().filter { it.isFile }.filter { it.name.endsWith(".parallax") && it.isFile }) { try { callback("Loading $listedFile") @@ -360,7 +364,7 @@ object Starbound { readingFolder = "/tiles/materials" for (fs in fileSystems) { - for (listedFile in fs.explore().filter { it.isFile }.filter { it.name.endsWith(".matmod") }) { + for (listedFile in fs.explore().filter { it.isFile }.filter { it.name.endsWith(".matmod") && it.isFile }) { try { callback("Loading $listedFile") @@ -387,7 +391,7 @@ object Starbound { private fun loadLiquidDefinitions(callback: (String) -> Unit) { for (fs in fileSystems) { - for (listedFile in fs.explore().filter { it.isFile }.filter { it.name.endsWith(".liquid") }) { + for (listedFile in fs.explore().filter { it.isFile }.filter { it.name.endsWith(".liquid") && it.isFile }) { try { callback("Loading $listedFile") @@ -409,4 +413,27 @@ object Starbound { readingFolder = null } + + private fun loadItemDefinitions(callback: (String) -> Unit) { + for (fs in fileSystems) { + for (listedFile in fs.explore().filter { it.isFile }.filter { it.name.endsWith(".item") && it.isFile }) { + try { + callback("Loading $listedFile") + + readingFolder = listedFile.computeDirectory() + val def = gson.fromJson(listedFile.reader(), ItemDefinition::class.java) + + check(items.put(def.itemName, def) == null) { "Already has item with name ${def.itemName} loaded!" } + } catch (err: Throwable) { + LOGGER.error("Loading item definition file $listedFile", err) + } + + if (terminateLoading) { + return + } + } + } + + readingFolder = null + } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/ItemDefinition.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/ItemDefinition.kt new file mode 100644 index 00000000..e127f287 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/ItemDefinition.kt @@ -0,0 +1,33 @@ +package ru.dbotthepony.kstarbound.defs.item + +import com.google.gson.GsonBuilder +import ru.dbotthepony.kstarbound.io.KConcreteTypeAdapter +import ru.dbotthepony.kstarbound.registerTypeAdapter + +data class ItemDefinition( + val itemName: String, + val price: Long = 0L, + val rarity: ItemRarity = ItemRarity.COMMON, + val category: String? = null, + val inventoryIcon: String? = null, + val description: String = "...", + val shortdescription: String = "...", + val itemTags: List = listOf() +) { + companion object { + val ADAPTER = KConcreteTypeAdapter.Builder(ItemDefinition::class) + .plain(ItemDefinition::itemName) + .plain(ItemDefinition::price) + .plain(ItemDefinition::rarity) + .plain(ItemDefinition::category) + .plain(ItemDefinition::inventoryIcon) + .plain(ItemDefinition::description) + .plain(ItemDefinition::shortdescription) + .list(ItemDefinition::itemTags) + .build() + + fun registerGson(gsonBuilder: GsonBuilder) { + gsonBuilder.registerTypeAdapter(ADAPTER) + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/ItemRarity.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/ItemRarity.kt new file mode 100644 index 00000000..16e1cda7 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/ItemRarity.kt @@ -0,0 +1,32 @@ +package ru.dbotthepony.kstarbound.defs.item + +import com.google.gson.GsonBuilder +import com.google.gson.TypeAdapter +import com.google.gson.stream.JsonWriter +import ru.dbotthepony.kstarbound.io.CustomEnumTypeAdapter +import ru.dbotthepony.kstarbound.io.IStringSerializable +import ru.dbotthepony.kstarbound.registerTypeAdapter + +enum class ItemRarity(val canonical: String) : IStringSerializable { + COMMON("Common"), + UNCOMMON("Uncommon"), + RARE("Rare"), + LEGENDARY("Legendary"), + ESSENTIAL("Essential"); + + override fun match(name: String): Boolean { + return name == this.canonical || name.lowercase() == this.name.lowercase() + } + + override fun write(out: JsonWriter) { + out.value(canonical) + } + + companion object { + val ADAPTER: TypeAdapter = CustomEnumTypeAdapter(values()).nullSafe() + + fun registerGson(gsonBuilder: GsonBuilder) { + gsonBuilder.registerTypeAdapter(ADAPTER) + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/io/CustomEnumTypeAdapter.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/io/CustomEnumTypeAdapter.kt index db68b710..d228d9f7 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/io/CustomEnumTypeAdapter.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/io/CustomEnumTypeAdapter.kt @@ -26,6 +26,6 @@ class CustomEnumTypeAdapter>(private val clazz: Array) : TypeAdap } } - throw IllegalArgumentException("${clazz[0]::class.java.name} does not have value for $str") + throw IllegalArgumentException("${clazz[0]::class.qualifiedName} does not have value for $str") } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/io/KConcreteTypeAdapter.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/io/KConcreteTypeAdapter.kt index 815df8d0..62715c38 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/io/KConcreteTypeAdapter.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/io/KConcreteTypeAdapter.kt @@ -2,6 +2,7 @@ package ru.dbotthepony.kstarbound.io import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableMap +import com.google.common.collect.Interners import com.google.gson.GsonBuilder import com.google.gson.JsonSyntaxException import com.google.gson.TypeAdapter @@ -174,7 +175,7 @@ class KConcreteTypeAdapter( private val returnTypeCache = Object2ObjectArrayMap, KType>() private val mapped = Object2IntArrayMap() - + private val internedStrings = Interners.newWeakInterner() private val loggedMisses = ObjectArraySet() init { @@ -267,9 +268,11 @@ class KConcreteTypeAdapter( reader.beginObject() } + // таблица присутствия значений (если значение true то на i было значение внутри json) val presentValues = BooleanArray(types.size) val readValues = arrayOfNulls(types.size) + // Если нам необходимо читать объект как набор данных массива, то давай if (asList) { val iterator = types.iterator() var fieldId = 0 @@ -299,6 +302,7 @@ class KConcreteTypeAdapter( fieldId++ } + // иначе - читаем как json object } else { while (reader.peek() != JsonToken.END_OBJECT) { val name = reader.nextName() @@ -323,9 +327,10 @@ class KConcreteTypeAdapter( } } + // intern'им строки для более быстрой работы сравнения последних for (i in readValues.indices) { if (readValues[i] is String) { - readValues[i] = (readValues[i] as String).intern() + readValues[i] = internedStrings.intern(readValues[i] as String) } } @@ -335,6 +340,8 @@ class KConcreteTypeAdapter( reader.endObject() } + // если у нас есть все значения для конструктора (все значения присутствуют в исходном json объекте) + // или у нас нет Java'вского конструктора, то делаем вызов "обычного" конструктора if (syntheticFactory == null || presentValues.all { it }) { for ((i, pair) in types.withIndex()) { val (field) = pair @@ -407,6 +414,9 @@ class KConcreteTypeAdapter( } } + /** + * Позволяет построить класс [KConcreteTypeAdapter] на основе заданных параметров + */ class Builder(val clazz: KClass) { private val types = ArrayList, TypeAdapter<*>>>()