KStarbound/src/main/kotlin/ru/dbotthepony/kstarbound/Globals.kt

239 lines
8.5 KiB
Kotlin

package ru.dbotthepony.kstarbound
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
import com.google.common.collect.ImmutableSet
import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
import kotlinx.coroutines.future.asCompletableFuture
import kotlinx.coroutines.future.await
import kotlinx.coroutines.launch
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kstarbound.defs.ActorMovementParameters
import ru.dbotthepony.kstarbound.defs.ClientConfig
import ru.dbotthepony.kstarbound.defs.CurrencyDefinition
import ru.dbotthepony.kstarbound.defs.MovementParameters
import ru.dbotthepony.kstarbound.defs.UniverseServerConfig
import ru.dbotthepony.kstarbound.defs.WorldServerConfig
import ru.dbotthepony.kstarbound.defs.actor.player.PlayerConfig
import ru.dbotthepony.kstarbound.defs.item.ItemDropConfig
import ru.dbotthepony.kstarbound.defs.item.ItemGlobalConfig
import ru.dbotthepony.kstarbound.defs.tile.TileDamageParameters
import ru.dbotthepony.kstarbound.defs.world.TerrestrialWorldsConfig
import ru.dbotthepony.kstarbound.defs.world.AsteroidWorldsConfig
import ru.dbotthepony.kstarbound.defs.world.CelestialBaseInformation
import ru.dbotthepony.kstarbound.defs.world.CelestialConfig
import ru.dbotthepony.kstarbound.defs.world.CelestialNames
import ru.dbotthepony.kstarbound.defs.world.DungeonWorldsConfig
import ru.dbotthepony.kstarbound.defs.world.InstanceWorldConfig
import ru.dbotthepony.kstarbound.defs.world.SkyGlobalConfig
import ru.dbotthepony.kstarbound.defs.world.SystemWorldConfig
import ru.dbotthepony.kstarbound.defs.world.SystemWorldObjectConfig
import ru.dbotthepony.kstarbound.defs.world.WorldTemplateConfig
import ru.dbotthepony.kstarbound.json.listAdapter
import ru.dbotthepony.kstarbound.json.mapAdapter
import ru.dbotthepony.kstarbound.util.AssetPathStack
import java.util.concurrent.CompletableFuture
import java.util.concurrent.Future
import kotlin.properties.Delegates
import kotlin.reflect.KMutableProperty0
object Globals {
private val LOGGER = LogManager.getLogger()
var player by Delegates.notNull<PlayerConfig>()
private set
var actorMovementParameters by Delegates.notNull<ActorMovementParameters>()
private set
var movementParameters by Delegates.notNull<MovementParameters>()
private set
var client by Delegates.notNull<ClientConfig>()
private set
var worldTemplate by Delegates.notNull<WorldTemplateConfig>()
private set
var terrestrialWorlds by Delegates.notNull<TerrestrialWorldsConfig>()
private set
var asteroidWorlds by Delegates.notNull<AsteroidWorldsConfig>()
private set
var dungeonWorlds by Delegates.notNull<ImmutableMap<String, DungeonWorldsConfig>>()
private set
var grassDamage by Delegates.notNull<TileDamageParameters>()
private set
var treeDamage by Delegates.notNull<TileDamageParameters>()
private set
var bushDamage by Delegates.notNull<TileDamageParameters>()
private set
var tileDamage by Delegates.notNull<TileDamageParameters>()
private set
var sky by Delegates.notNull<SkyGlobalConfig>()
private set
var universeServer by Delegates.notNull<UniverseServerConfig>()
private set
var worldServer by Delegates.notNull<WorldServerConfig>()
private set
var itemDrop by Delegates.notNull<ItemDropConfig>()
private set
var currencies by Delegates.notNull<ImmutableMap<String, CurrencyDefinition>>()
private set
var systemObjects by Delegates.notNull<ImmutableMap<String, SystemWorldObjectConfig>>()
private set
var systemWorld by Delegates.notNull<SystemWorldConfig>()
private set
var celestialBaseInformation by Delegates.notNull<CelestialBaseInformation>()
private set
var celestialConfig by Delegates.notNull<CelestialConfig>()
private set
var celestialNames by Delegates.notNull<CelestialNames>()
private set
var instanceWorlds by Delegates.notNull<ImmutableMap<String, InstanceWorldConfig>>()
private set
var itemParameters by Delegates.notNull<ItemGlobalConfig>()
private set
private var profanityFilterInternal by Delegates.notNull<ImmutableList<String>>()
val profanityFilter: ImmutableSet<String> by lazy {
// reverse "encryption"
val words = ImmutableSet.Builder<String>()
for (word in profanityFilterInternal) {
val chars = CharArray(word.length)
for (i in word.indices) {
var c = word[i]
if ((c >= 'a' + 13 && c <= 'm' + 13) || (c >= 'A' + 13 && c <= 'M' + 13))
c -= 13
else if ((c >= 'n' - 13 && c <= 'z' - 13) || (c >= 'N' - 13 && c <= 'Z' - 13))
c += 13
chars[i] = c
}
words.add(String(chars))
}
words.build()
}
val onLoadedFuture = CompletableFuture<Unit>()
private suspend fun <T> load(path: String, accept: KMutableProperty0<T>, adapter: Lazy<TypeAdapter<T>>) {
val file = Starbound.loadJsonAsset(path).await()
if (file == null) {
LOGGER.fatal("$path does not exist or is not a file, expect bad things to happen!")
} else {
try {
AssetPathStack(path) {
accept.set(adapter.value.fromJsonTree(file))
}
} catch (err: Throwable) {
LOGGER.fatal("Error while reading $path, expect bad things to happen!", err)
throw err
}
}
}
private inline fun <reified T> load(path: String, accept: KMutableProperty0<T>): CompletableFuture<*> {
return Starbound.GLOBAL_SCOPE.launch { load(path, accept, lazy(LazyThreadSafetyMode.NONE) { Starbound.gson.getAdapter(T::class.java) }) }.asCompletableFuture()
}
private inline fun <reified T : Any> mapAdapter(filename: String): Lazy<TypeAdapter<ImmutableMap<String, T>>> {
val parent by lazy { Starbound.gson.getAdapter(T::class.java) }
return lazy(LazyThreadSafetyMode.NONE) {
object : TypeAdapter<ImmutableMap<String, T>>() {
override fun write(out: JsonWriter, value: ImmutableMap<String, T>) {
TODO("Not yet implemented")
}
override fun read(`in`: JsonReader): ImmutableMap<String, T> {
`in`.beginObject()
val builder = ImmutableMap.Builder<String, T>()
while (`in`.hasNext()) {
val name = `in`.nextName()
val element = Starbound.ELEMENTS_ADAPTER.read(`in`)
try {
builder.put(name, parent.fromJsonTree(element))
} catch (err: Throwable) {
LOGGER.error("Exception reading element at $name from $filename; it will not be loaded", err)
}
}
`in`.endObject()
return builder.build()
}
}
}
}
fun load(): List<Future<*>> {
val tasks = ArrayList<CompletableFuture<*>>()
tasks.add(load("/default_actor_movement.config", ::actorMovementParameters))
tasks.add(load("/default_movement.config", ::movementParameters))
tasks.add(load("/client.config", ::client))
tasks.add(load("/terrestrial_worlds.config", ::terrestrialWorlds))
tasks.add(load("/asteroids_worlds.config", ::asteroidWorlds))
tasks.add(load("/world_template.config", ::worldTemplate))
tasks.add(load("/sky.config", ::sky))
tasks.add(load("/universe_server.config", ::universeServer))
tasks.add(load("/worldserver.config", ::worldServer))
tasks.add(load("/player.config", ::player))
tasks.add(load("/systemworld.config", ::systemWorld))
tasks.add(load("/itemdrop.config", ::itemDrop))
tasks.add(load("/celestial.config", ::celestialBaseInformation))
tasks.add(load("/celestial.config", ::celestialConfig))
tasks.add(load("/celestial/names.config", ::celestialNames))
tasks.add(load("/items/defaultParameters.config", ::itemParameters))
tasks.add(load("/plants/grassDamage.config", ::grassDamage))
tasks.add(load("/plants/treeDamage.config", ::treeDamage))
tasks.add(load("/plants/bushDamage.config", ::bushDamage))
tasks.add(load("/tiles/defaultDamage.config", ::tileDamage))
tasks.add(Starbound.GLOBAL_SCOPE.launch { load("/dungeon_worlds.config", ::dungeonWorlds, mapAdapter("/dungeon_worlds.config")) }.asCompletableFuture())
tasks.add(Starbound.GLOBAL_SCOPE.launch { load("/currencies.config", ::currencies, mapAdapter("/currencies.config")) }.asCompletableFuture())
tasks.add(Starbound.GLOBAL_SCOPE.launch { load("/system_objects.config", ::systemObjects, mapAdapter("/system_objects.config")) }.asCompletableFuture())
tasks.add(Starbound.GLOBAL_SCOPE.launch { load("/instance_worlds.config", ::instanceWorlds, mapAdapter("/instance_worlds.config")) }.asCompletableFuture())
tasks.add(Starbound.GLOBAL_SCOPE.launch { load("/names/profanityfilter.config", ::profanityFilterInternal, lazy { Starbound.gson.listAdapter() }) }.asCompletableFuture())
CompletableFuture.allOf(*tasks.toTypedArray()).thenApply {
onLoadedFuture.complete(Unit)
}.exceptionally {
onLoadedFuture.completeExceptionally(it)
}
return tasks
}
}