128 lines
4.1 KiB
Kotlin
128 lines
4.1 KiB
Kotlin
package ru.dbotthepony.kstarbound.defs
|
|
|
|
import com.google.gson.Gson
|
|
import com.google.gson.JsonElement
|
|
import com.google.gson.TypeAdapter
|
|
import com.google.gson.TypeAdapterFactory
|
|
import com.google.gson.internal.bind.JsonTreeReader
|
|
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.Object2ObjectOpenHashMap
|
|
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
|
|
import org.apache.logging.log4j.LogManager
|
|
import ru.dbotthepony.kommons.gson.consumeNull
|
|
import ru.dbotthepony.kstarbound.Starbound
|
|
import ru.dbotthepony.kstarbound.util.AssetPathStack
|
|
import ru.dbotthepony.kstarbound.util.sbIntern
|
|
import java.lang.reflect.ParameterizedType
|
|
import java.util.*
|
|
import java.util.concurrent.CompletableFuture
|
|
import java.util.concurrent.ConcurrentHashMap
|
|
import java.util.function.Consumer
|
|
import kotlin.collections.HashMap
|
|
|
|
class AssetReference<V> {
|
|
constructor(value: V?) {
|
|
this.value = CompletableFuture.completedFuture(value)
|
|
path = null
|
|
fullPath = null
|
|
this.json = CompletableFuture.completedFuture(null)
|
|
}
|
|
|
|
constructor(value: CompletableFuture<V?>) {
|
|
this.value = value
|
|
path = null
|
|
fullPath = null
|
|
this.json = CompletableFuture.completedFuture(null)
|
|
}
|
|
|
|
constructor(path: String?, fullPath: String?, value: V?, json: JsonElement?) {
|
|
this.path = path
|
|
this.fullPath = fullPath
|
|
this.value = CompletableFuture.completedFuture(value)
|
|
this.json = CompletableFuture.completedFuture(json)
|
|
}
|
|
|
|
constructor(path: String?, fullPath: String?, value: CompletableFuture<V?>, json: CompletableFuture<JsonElement?>) {
|
|
this.path = path
|
|
this.fullPath = fullPath
|
|
this.value = value
|
|
this.json = json
|
|
}
|
|
|
|
val path: String?
|
|
val fullPath: String?
|
|
val json: CompletableFuture<JsonElement?>
|
|
val value: CompletableFuture<V?>
|
|
|
|
companion object : TypeAdapterFactory {
|
|
private val LOGGER = LogManager.getLogger()
|
|
val EMPTY = AssetReference(null, null, null, null)
|
|
|
|
fun <V> empty() = EMPTY as AssetReference<V>
|
|
|
|
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<T>>() {
|
|
private val cache = ConcurrentHashMap<String, Pair<CompletableFuture<T?>, CompletableFuture<JsonElement?>>>()
|
|
private val adapter = gson.getAdapter(TypeToken.get(param.actualTypeArguments[0])) as TypeAdapter<T>
|
|
private val strings = gson.getAdapter(String::class.java)
|
|
private val missing = Collections.synchronizedSet(ObjectOpenHashSet<String>())
|
|
private val logger = LogManager.getLogger()
|
|
|
|
override fun write(out: JsonWriter, value: AssetReference<T>?) {
|
|
if (value == null)
|
|
out.nullValue()
|
|
else
|
|
out.value(value.fullPath)
|
|
}
|
|
|
|
override fun read(`in`: JsonReader): AssetReference<T>? {
|
|
if (`in`.consumeNull()) {
|
|
return null
|
|
} else if (`in`.peek() == JsonToken.STRING) {
|
|
val path = strings.read(`in`)!!
|
|
val fullPath = AssetPathStack.remap(path)
|
|
|
|
val get = cache.computeIfAbsent(fullPath) {
|
|
val json = Starbound.loadJsonAsset(fullPath)
|
|
|
|
json.thenAccept {
|
|
if (it == null && missing.add(fullPath)) {
|
|
logger.error("JSON asset does not exist: $fullPath")
|
|
}
|
|
}
|
|
|
|
val value = json.thenApplyAsync({ j ->
|
|
AssetPathStack(fullPath.substringBefore(':').substringBeforeLast('/')) {
|
|
adapter.fromJsonTree(j)
|
|
}
|
|
}, Starbound.EXECUTOR)
|
|
|
|
value.exceptionally {
|
|
LOGGER.error("Exception loading $fullPath", it)
|
|
null
|
|
}
|
|
|
|
value to json
|
|
}
|
|
|
|
return AssetReference(path.sbIntern(), fullPath.sbIntern(), get.first, get.second)
|
|
} else {
|
|
val json = Starbound.ELEMENTS_ADAPTER.read(`in`)
|
|
val value = adapter.read(JsonTreeReader(json)) ?: return null
|
|
return AssetReference(null, null, value, json)
|
|
}
|
|
}
|
|
} as TypeAdapter<T>
|
|
}
|
|
|
|
return null
|
|
}
|
|
}
|
|
}
|