KStarbound/src/main/kotlin/ru/dbotthepony/kstarbound/defs/AssetReference.kt
DBotThePony 15abdba2c5
Starbound теперь более не синглтон, а настоящий класс
удалил кучу устаревших классов ибо они совсем не имеют смысла
2023-02-06 17:17:42 +07:00

90 lines
2.8 KiB
Kotlin

package ru.dbotthepony.kstarbound.defs
import com.google.gson.Gson
import com.google.gson.TypeAdapter
import com.google.gson.TypeAdapterFactory
import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
import ru.dbotthepony.kstarbound.api.ISBFileLocator
import ru.dbotthepony.kstarbound.util.AssetPathStack
import java.io.Reader
import java.lang.reflect.ParameterizedType
import java.util.*
import java.util.concurrent.ConcurrentHashMap
/**
* Данный [AssetReferenceFactory] реализует возможности чтения данных по "ссылке" на файл.
*
* Созданный [TypeAdapter] имеет встроенный кеш.
*/
class AssetReferenceFactory(val remapper: AssetPathStack, val locator: ISBFileLocator) : TypeAdapterFactory {
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
if (type.rawType == AssetReference::class.java) {
val param = type.type as? ParameterizedType ?: return null
return object : TypeAdapter<AssetReference<Any>>() {
private val cache = ConcurrentHashMap<String, Any>()
private val adapter = gson.getAdapter(TypeToken.get(param.actualTypeArguments[0])) as TypeAdapter<Any>
private val strings = gson.getAdapter(String::class.java)
private val missing = Collections.synchronizedSet(ObjectOpenHashSet<String>())
override fun write(out: JsonWriter, value: AssetReference<Any>?) {
if (value == null)
out.nullValue()
else
out.value(value.fullPath)
}
override fun read(`in`: JsonReader): AssetReference<Any>? {
if (`in`.peek() == JsonToken.NULL) {
return null
} else if (`in`.peek() == JsonToken.STRING) {
val path = strings.read(`in`)!!
val fullPath = remapper.remap(path)
val get = cache[fullPath]
if (get != null)
return AssetReference(path, fullPath, get)
if (fullPath in missing)
return null
val file = locator.locate(fullPath)
if (!file.exists) {
missing.add(fullPath)
return AssetReference(path, fullPath, null)
}
val reader = file.reader()
val value = remapper(fullPath) {
adapter.read(JsonReader(reader).also {
it.isLenient = true
})
}
if (value == null) {
missing.add(fullPath)
return AssetReference(path, fullPath, null)
}
cache[fullPath] = value
return AssetReference(path, fullPath, value)
} else {
val value = adapter.read(`in`) ?: return null
return AssetReference(null, null, value)
}
}
} as TypeAdapter<T>
}
return null
}
}
data class AssetReference<V>(val path: String?, val fullPath: String?, val value: V?)