diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/GlobalDefaults.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/GlobalDefaults.kt new file mode 100644 index 00000000..5b3a32ae --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/GlobalDefaults.kt @@ -0,0 +1,58 @@ +package ru.dbotthepony.kstarbound + +import org.apache.logging.log4j.LogManager +import ru.dbotthepony.kstarbound.defs.MovementParameters +import java.util.concurrent.ForkJoinPool +import java.util.concurrent.ForkJoinTask +import java.util.concurrent.Future +import kotlin.reflect.KMutableProperty0 + +object GlobalDefaults { + private val LOGGER = LogManager.getLogger() + + var defaultMovementParameters = MovementParameters() + private set + + private object EmptyTask : ForkJoinTask() { + private fun readResolve(): Any = EmptyTask + override fun getRawResult() { + } + + override fun setRawResult(value: Unit?) { + } + + override fun exec(): Boolean { + return true + } + } + + private inline fun load(path: String, accept: KMutableProperty0, executor: ForkJoinPool): ForkJoinTask<*> { + val file = Starbound.locate(path) + + if (!file.exists) { + LOGGER.fatal("$path does not exist, expect bad things to happen!") + return EmptyTask + } else if (!file.isFile) { + LOGGER.fatal("$path is not a file, expect bad things to happen!") + return EmptyTask + } else { + return executor.submit { + accept.set(Starbound.gson.fromJson(file.jsonReader(), T::class.java)) + } + } + } + + fun load(log: ILoadingLog, executor: ForkJoinPool): List> { + val tasks = ArrayList>() + + tasks.add(load("/default_actor_movement.config", ::defaultMovementParameters, executor)) + + return listOf(executor.submit { + val line = log.line("Loading global defaults...") + val time = System.nanoTime() + tasks.forEach { it.fork() } + tasks.forEach { it.join() } + line.text = "Loaded global defaults in ${((System.nanoTime() - time) / 1_000_000.0).toLong()}ms" + }) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt index 03d45657..69a36974 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt @@ -79,7 +79,7 @@ fun main() { if (cell == null) { IChunkCell.skip(reader) } else { - cell.read(Starbound.tilesByID::get, Starbound.tileModifiersByID::get, Starbound.liquidByID::get, reader) + cell.read(reader) } } } @@ -111,8 +111,8 @@ fun main() { //client.world!!.parallax = Starbound.parallaxAccess["garden"] - val item = Starbound.items.values.random() - val rand = java.util.Random() + val item = Registries.items.values.random() + val rand = Random() client.world!!.physics.gravity = Vector2d.ZERO @@ -131,7 +131,7 @@ fun main() { val def = Starbound.gson.fromJson(Starbound.locate("/animations/dust4/dust4.animation").reader(), AnimationDefinition::class.java) AssetPathStack.pop() - val avatar = Avatar(Starbound, UUID.randomUUID()) + val avatar = Avatar(UUID.randomUUID()) val quest = QuestInstance(avatar, descriptor = QuestDescriptor("floran_mission1")) quest.init() quest.start() @@ -145,7 +145,7 @@ fun main() { } } - println(Starbound.treasurePools["motherpoptopTreasure"]!!.value.evaluate(Random(), 2.0)) + println(Registries.treasurePools["motherpoptopTreasure"]!!.value.evaluate(Random(), 2.0)) } //ent.position += Vector2d(y = 14.0, x = -10.0) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/ObjectRegistry.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/ObjectRegistry.kt index 46a27754..4af771ac 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/ObjectRegistry.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/ObjectRegistry.kt @@ -17,6 +17,7 @@ import ru.dbotthepony.kstarbound.util.AssetPathStack import ru.dbotthepony.kstarbound.util.set import ru.dbotthepony.kstarbound.util.traverseJsonPath import java.util.* +import java.util.concurrent.atomic.AtomicInteger import kotlin.reflect.KClass inline fun ObjectRegistry(name: String, noinline key: ((T) -> String)? = null, noinline intKey: ((T) -> Int)? = null): ObjectRegistry { @@ -123,11 +124,15 @@ class RegistryObject( } class ObjectRegistry(val clazz: KClass, val name: String, val key: ((T) -> String)? = null, val intKey: ((T) -> Int)? = null) { - private val objects = Object2ObjectOpenHashMap>() - private val intObjects = Int2ObjectOpenHashMap>() + val objects = Object2ObjectOpenHashMap>() + val intObjects = Int2ObjectOpenHashMap>() - val view: Map> = Collections.unmodifiableMap(objects) - val intView: Int2ObjectMap> = Int2ObjectMaps.unmodifiable(intObjects) + val values get() = objects.values + + operator fun get(index: String) = objects[index] + operator fun get(index: Int): RegistryObject? = intObjects[index] + operator fun contains(index: String) = index in objects + operator fun contains(index: Int) = index in intObjects fun clear() { objects.clear() diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/RecipeRegistry.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/RecipeRegistry.kt index 8b0952bf..19cedeab 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/RecipeRegistry.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/RecipeRegistry.kt @@ -1,15 +1,24 @@ package ru.dbotthepony.kstarbound import com.google.gson.Gson +import com.google.gson.JsonElement import com.google.gson.JsonObject +import com.google.gson.internal.bind.JsonTreeReader import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap +import org.apache.logging.log4j.LogManager import ru.dbotthepony.kstarbound.api.IStarboundFile import ru.dbotthepony.kstarbound.defs.player.RecipeDefinition import java.util.Collections import java.util.LinkedList +import java.util.concurrent.ExecutorService +import java.util.concurrent.ForkJoinPool +import java.util.concurrent.ForkJoinTask +import java.util.concurrent.Future + +object RecipeRegistry { + private val LOGGER = LogManager.getLogger() -class RecipeRegistry { private val recipesInternal = ArrayList>() private val group2recipesInternal = Object2ObjectOpenHashMap>>() private val group2recipesBacking = Object2ObjectOpenHashMap>>() @@ -49,4 +58,30 @@ class RecipeRegistry { }).add(recipe) } } + + fun load(log: ILoadingLog, fileTree: Map>, executor: ForkJoinPool): List> { + val files = fileTree["recipe"] ?: return emptyList() + + return listOf(executor.submit(Runnable { + val line = log.line("Loading recipes...") + val time = System.nanoTime() + + for (listedFile in files) { + try { + line.text = ("Loading $listedFile") + val json = Starbound.gson.fromJson(listedFile.reader(), JsonElement::class.java) + val value = Starbound.gson.fromJson(JsonTreeReader(json), RecipeDefinition::class.java) + add(RegistryObject(value, json, listedFile)) + } catch (err: Throwable) { + LOGGER.error("Loading recipe definition file $listedFile", err) + } + + if (Starbound.terminateLoading) { + return@Runnable + } + } + + line.text = "Loaded recipes in ${((System.nanoTime() - time) / 1_000_000.0).toLong()}ms" + })) + } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Registries.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Registries.kt new file mode 100644 index 00000000..2490de1f --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Registries.kt @@ -0,0 +1,257 @@ +package ru.dbotthepony.kstarbound + +import com.google.gson.JsonObject +import com.google.gson.internal.bind.JsonTreeReader +import com.google.gson.stream.JsonReader +import org.apache.logging.log4j.LogManager +import ru.dbotthepony.kstarbound.api.IStarboundFile +import ru.dbotthepony.kstarbound.defs.Json2Function +import ru.dbotthepony.kstarbound.defs.JsonFunction +import ru.dbotthepony.kstarbound.defs.Species +import ru.dbotthepony.kstarbound.defs.StatusEffectDefinition +import ru.dbotthepony.kstarbound.defs.item.TreasurePoolDefinition +import ru.dbotthepony.kstarbound.defs.item.api.IItemDefinition +import ru.dbotthepony.kstarbound.defs.item.impl.BackArmorItemDefinition +import ru.dbotthepony.kstarbound.defs.item.impl.ChestArmorItemDefinition +import ru.dbotthepony.kstarbound.defs.item.impl.CurrencyItemDefinition +import ru.dbotthepony.kstarbound.defs.item.impl.FlashlightDefinition +import ru.dbotthepony.kstarbound.defs.item.impl.HarvestingToolPrototype +import ru.dbotthepony.kstarbound.defs.item.impl.HeadArmorItemDefinition +import ru.dbotthepony.kstarbound.defs.item.impl.ItemDefinition +import ru.dbotthepony.kstarbound.defs.item.impl.LegsArmorItemDefinition +import ru.dbotthepony.kstarbound.defs.item.impl.LiquidItemDefinition +import ru.dbotthepony.kstarbound.defs.item.impl.MaterialItemDefinition +import ru.dbotthepony.kstarbound.defs.monster.MonsterSkillDefinition +import ru.dbotthepony.kstarbound.defs.monster.MonsterTypeDefinition +import ru.dbotthepony.kstarbound.defs.npc.NpcTypeDefinition +import ru.dbotthepony.kstarbound.defs.npc.TenantDefinition +import ru.dbotthepony.kstarbound.defs.`object`.ObjectDefinition +import ru.dbotthepony.kstarbound.defs.particle.ParticleDefinition +import ru.dbotthepony.kstarbound.defs.player.TechDefinition +import ru.dbotthepony.kstarbound.defs.projectile.ProjectileDefinition +import ru.dbotthepony.kstarbound.defs.quest.QuestTemplate +import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition +import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier +import ru.dbotthepony.kstarbound.defs.tile.TileDefinition +import ru.dbotthepony.kstarbound.util.AssetPathStack +import java.util.concurrent.ExecutorService +import java.util.concurrent.ForkJoinPool +import java.util.concurrent.ForkJoinTask +import java.util.concurrent.Future + +object Registries { + private val LOGGER = LogManager.getLogger() + + val tiles = ObjectRegistry("tiles", TileDefinition::materialName, TileDefinition::materialId) + val tileModifiers = ObjectRegistry("tile modifiers", MaterialModifier::modName, MaterialModifier::modId) + val liquid = ObjectRegistry("liquid", LiquidDefinition::name, LiquidDefinition::liquidId) + val species = ObjectRegistry("species", Species::kind) + val statusEffects = ObjectRegistry("status effects", StatusEffectDefinition::name) + val particles = ObjectRegistry("particles", ParticleDefinition::kind) + val items = ObjectRegistry("items", IItemDefinition::itemName) + val questTemplates = ObjectRegistry("quest templates", QuestTemplate::id) + val techs = ObjectRegistry("techs", TechDefinition::name) + val jsonFunctions = ObjectRegistry("json functions") + val json2Functions = ObjectRegistry("json 2functions") + val npcTypes = ObjectRegistry("npc types", NpcTypeDefinition::type) + val projectiles = ObjectRegistry("projectiles", ProjectileDefinition::projectileName) + val tenants = ObjectRegistry("tenants", TenantDefinition::name) + val treasurePools = ObjectRegistry("treasure pools", TreasurePoolDefinition::name) + val monsterSkills = ObjectRegistry("monster skills", MonsterSkillDefinition::name) + val monsterTypes = ObjectRegistry("monster types", MonsterTypeDefinition::type) + val worldObjects = ObjectRegistry("objects", ObjectDefinition::objectName) + + private fun loadStage( + log: ILoadingLog, + loader: ((String) -> Unit) -> Unit, + name: String, + ) { + if (Starbound.terminateLoading) + return + + val time = System.currentTimeMillis() + val line = log.line("Loading $name...".also(LOGGER::info)) + + loader { + if (Starbound.terminateLoading) { + throw InterruptedException("Game is terminating") + } + + line.text = it + } + + line.text = ("Loaded $name in ${System.currentTimeMillis() - time}ms".also(LOGGER::info)) + } + + private fun loadStage( + log: ILoadingLog, + registry: ObjectRegistry, + files: List, + ) { + loadStage(log, loader = { + for (listedFile in files) { + try { + it("Loading $listedFile") + registry.add(listedFile) + } catch (err: Throwable) { + LOGGER.error("Loading ${registry.name} definition file $listedFile", err) + } + + if (Starbound.terminateLoading) { + break + } + } + }, registry.name) + } + + fun load(log: ILoadingLog, fileTree: Map>, executor: ForkJoinPool): List> { + val tasks = ArrayList>() + + tasks.addAll(loadItemDefinitions(log, fileTree, executor)) + + tasks.add(executor.submit { loadStage(log, { loadJsonFunctions(it, fileTree["functions"] ?: listOf()) }, "json functions") }) + tasks.add(executor.submit { loadStage(log, { loadJson2Functions(it, fileTree["2functions"] ?: listOf()) }, "json 2functions") }) + tasks.add(executor.submit { loadStage(log, { loadTreasurePools(it, fileTree["treasurepools"] ?: listOf()) }, "treasure pools") }) + + tasks.add(executor.submit { loadStage(log, tiles, fileTree["material"] ?: listOf()) }) + tasks.add(executor.submit { loadStage(log, tileModifiers, fileTree["matmod"] ?: listOf()) }) + tasks.add(executor.submit { loadStage(log, liquid, fileTree["liquid"] ?: listOf()) }) + tasks.add(executor.submit { loadStage(log, worldObjects, fileTree["object"] ?: listOf()) }) + tasks.add(executor.submit { loadStage(log, statusEffects, fileTree["statuseffect"] ?: listOf()) }) + tasks.add(executor.submit { loadStage(log, species, fileTree["species"] ?: listOf()) }) + tasks.add(executor.submit { loadStage(log, particles, fileTree["particle"] ?: listOf()) }) + tasks.add(executor.submit { loadStage(log, questTemplates, fileTree["questtemplate"] ?: listOf()) }) + tasks.add(executor.submit { loadStage(log, techs, fileTree["tech"] ?: listOf()) }) + tasks.add(executor.submit { loadStage(log, npcTypes, fileTree["npctype"] ?: listOf()) }) + // tasks.add(executor.submit { loadStage(log, projectiles, ext2files["projectile"] ?: listOf()) }) + // tasks.add(executor.submit { loadStage(log, tenants, ext2files["tenant"] ?: listOf()) }) + tasks.add(executor.submit { loadStage(log, monsterSkills, fileTree["monsterskill"] ?: listOf()) }) + // tasks.add(executor.submit { loadStage(log, _monsterTypes, ext2files["monstertype"] ?: listOf()) }) + + return tasks + } + + private fun loadItemDefinitions(log: ILoadingLog, files: Map>, executor: ForkJoinPool): List> { + val fileMap = mapOf( + "item" to ItemDefinition::class.java, + "currency" to CurrencyItemDefinition::class.java, + "liqitem" to LiquidItemDefinition::class.java, + "matitem" to MaterialItemDefinition::class.java, + "flashlight" to FlashlightDefinition::class.java, + "harvestingtool" to HarvestingToolPrototype::class.java, + "head" to HeadArmorItemDefinition::class.java, + "chest" to ChestArmorItemDefinition::class.java, + "legs" to LegsArmorItemDefinition::class.java, + "back" to BackArmorItemDefinition::class.java, + ) + + val tasks = ArrayList>() + + for ((ext, clazz) in fileMap) { + val fileList = files[ext] ?: continue + + tasks.add(executor.submit { + val line = log.line("Loading items '$ext'") + val time = System.nanoTime() + + for (listedFile in fileList) { + try { + line.text = "Loading $listedFile" + val json = Starbound.gson.fromJson(listedFile.reader(), JsonObject::class.java) + val def: IItemDefinition = AssetPathStack(listedFile.computeDirectory()) { Starbound.gson.fromJson(JsonTreeReader(json), clazz) } + items.add(def, json, listedFile) + } catch (err: Throwable) { + LOGGER.error("Loading item definition file $listedFile", err) + } + + if (Starbound.terminateLoading) { + return@submit + } + } + + line.text = "Loaded items '$ext' in ${((System.nanoTime() - time) / 1_000_000.0).toLong()}ms" + }) + } + + return tasks + } + + private fun loadJsonFunctions(callback: (String) -> Unit, files: Collection) { + for (listedFile in files) { + callback("Loading $listedFile") + try { + val json = Starbound.gson.getAdapter(JsonObject::class.java).read(JsonReader(listedFile.reader()).also { it.isLenient = true }) + + for ((k, v) in json.entrySet()) { + try { + callback("Loading $k from $listedFile") + val fn = Starbound.gson.fromJson(JsonTreeReader(v), JsonFunction::class.java) + Registries.jsonFunctions.add(fn, v, listedFile, k) + } catch (err: Exception) { + LOGGER.error("Loading json function definition $k from file $listedFile", err) + } + } + } catch (err: Exception) { + LOGGER.error("Loading json function definition $listedFile", err) + } + + if (Starbound.terminateLoading) { + return + } + } + } + + private fun loadJson2Functions(callback: (String) -> Unit, files: Collection) { + for (listedFile in files) { + callback("Loading $listedFile") + + try { + val json = Starbound.gson.getAdapter(JsonObject::class.java).read(JsonReader(listedFile.reader()).also { it.isLenient = true }) + + for ((k, v) in json.entrySet()) { + try { + callback("Loading $k from $listedFile") + val fn = Starbound.gson.fromJson(JsonTreeReader(v), Json2Function::class.java) + Registries.json2Functions.add(fn, v, listedFile, k) + } catch (err: Throwable) { + LOGGER.error("Loading json 2function definition $k from file $listedFile", err) + } + } + } catch (err: Exception) { + LOGGER.error("Loading json 2function definition $listedFile", err) + } + + if (Starbound.terminateLoading) { + return + } + } + } + + private fun loadTreasurePools(callback: (String) -> Unit, files: Collection) { + for (listedFile in files) { + callback("Loading $listedFile") + + try { + val json = Starbound.gson.getAdapter(JsonObject::class.java).read(JsonReader(listedFile.reader()).also { it.isLenient = true }) + + for ((k, v) in json.entrySet()) { + try { + callback("Loading $k from $listedFile") + val result = Starbound.gson.fromJson(JsonTreeReader(v), TreasurePoolDefinition::class.java) + result.name = k + Registries.treasurePools.add(result, v, listedFile) + } catch (err: Throwable) { + LOGGER.error("Loading treasure pool definition $k from file $listedFile", err) + } + } + } catch (err: Exception) { + LOGGER.error("Loading treasure pool definition $listedFile", err) + } + + if (Starbound.terminateLoading) { + return + } + } + } + +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt index cc04b1f1..7d795ee1 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt @@ -86,8 +86,11 @@ import ru.dbotthepony.kstarbound.util.set import ru.dbotthepony.kstarbound.util.traverseJsonPath import java.io.* import java.text.DateFormat +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors import java.util.concurrent.ForkJoinPool import java.util.concurrent.ForkJoinTask +import java.util.concurrent.Future import java.util.function.BiConsumer import java.util.function.BinaryOperator import java.util.function.Function @@ -101,82 +104,16 @@ object Starbound : ISBFileLocator { const val TICK_TIME_ADVANCE = 0.01666666666666664 const val TICK_TIME_ADVANCE_NANOS = 16_666_666L - // currently it saves only 4 megabytes of ram on pretty big modpack + // currently Caffeine one saves only 4 megabytes of RAM on pretty big modpack // Hrm. // val strings: Interner = Interner.newWeakInterner() // val strings: Interner = Interner { it } - - // custom-made on other hand is stupidly fast, - // and provide double the memory improvement over Caffeine one - // Only downside is that congestion is predefined on interner creation - // by no means this approach is the best, but it works great for this usecase - // This is the reason why JDK is not providing "the best" tools for one specific case, - // but the best possible tool for all possible usecases val strings: Interner = HashTableInterner(5) private val polyfill by lazy { loadInternalScript("polyfill") } private val logger = LogManager.getLogger() - private val _tiles = ObjectRegistry("tiles", TileDefinition::materialName, TileDefinition::materialId) - val tiles = _tiles.view - val tilesByID = _tiles.intView - - private val _tileModifiers = ObjectRegistry("tile modifiers", MaterialModifier::modName, MaterialModifier::modId) - val tileModifiers = _tileModifiers.view - val tileModifiersByID = _tileModifiers.intView - - private val _liquid = ObjectRegistry("liquid", LiquidDefinition::name, LiquidDefinition::liquidId) - val liquid = _liquid.view - val liquidByID = _liquid.intView - - private val _species = ObjectRegistry("species", Species::kind) - val species = _species.view - - private val _statusEffects = ObjectRegistry("status effects", StatusEffectDefinition::name) - val statusEffects = _statusEffects.view - - private val _particles = ObjectRegistry("particles", ParticleDefinition::kind) - val particles = _particles.view - - private val _items = ObjectRegistry("items", IItemDefinition::itemName) - val items = _items.view - - private val _questTemplates = ObjectRegistry("quest templates", QuestTemplate::id) - val questTemplates = _questTemplates.view - - private val _techs = ObjectRegistry("techs", TechDefinition::name) - val techs = _techs.view - - private val _jsonFunctions = ObjectRegistry("json functions") - val jsonFunctions = _jsonFunctions.view - - private val _json2Functions = ObjectRegistry("json 2functions") - val json2Functions = _json2Functions.view - - private val _npcTypes = ObjectRegistry("npc types", NpcTypeDefinition::type) - val npcTypes = _npcTypes.view - - private val _projectiles = ObjectRegistry("projectiles", ProjectileDefinition::projectileName) - val projectiles = _projectiles.view - - private val _tenants = ObjectRegistry("tenants", TenantDefinition::name) - val tenants = _tenants.view - - val recipeRegistry = RecipeRegistry() - - private val _treasurePools = ObjectRegistry("treasure pools", TreasurePoolDefinition::name) - val treasurePools = _treasurePools.view - - private val _monsterSkills = ObjectRegistry("monster skills", MonsterSkillDefinition::name) - val monsterSkills = _monsterSkills.view - - private val _monsterTypes = ObjectRegistry("monster types", MonsterTypeDefinition::type) - val monsterTypes = _monsterTypes.view - - private val _worldObjects = ObjectRegistry("objects", ObjectDefinition::objectName) - val worldObjects = _worldObjects.view - val gson: Gson = with(GsonBuilder()) { serializeNulls() setDateFormat(DateFormat.LONG) @@ -273,24 +210,24 @@ object Starbound : ISBFileLocator { registerTypeAdapterFactory(TreasurePoolDefinition.Companion) registerTypeAdapterFactory(with(RegistryReferenceFactory()) { - add(_tiles) - add(_tileModifiers) - add(_liquid) - add(_items) - add(_species) - add(_statusEffects) - add(_particles) - add(_questTemplates) - add(_techs) - add(_jsonFunctions) - add(_json2Functions) - add(_npcTypes) - add(_projectiles) - add(_tenants) - add(_treasurePools) - add(_monsterSkills) - add(_monsterTypes) - add(_worldObjects) + add(Registries.tiles) + add(Registries.tileModifiers) + add(Registries.liquid) + add(Registries.items) + add(Registries.species) + add(Registries.statusEffects) + add(Registries.particles) + add(Registries.questTemplates) + add(Registries.techs) + add(Registries.jsonFunctions) + add(Registries.json2Functions) + add(Registries.npcTypes) + add(Registries.projectiles) + add(Registries.tenants) + add(Registries.treasurePools) + add(Registries.monsterSkills) + add(Registries.monsterTypes) + add(Registries.worldObjects) }) registerTypeAdapter(LongRangeAdapter) @@ -302,26 +239,26 @@ object Starbound : ISBFileLocator { val f = NonExistingFile("/metamaterials.config") for (material in BuiltinMetaMaterials.MATERIALS) { - _tiles.add(material, JsonNull.INSTANCE, f) + Registries.tiles.add(material, JsonNull.INSTANCE, f) } } fun item(name: String): ItemStack { - return ItemStack(items[name] ?: return ItemStack.EMPTY) + return ItemStack(Registries.items[name] ?: return ItemStack.EMPTY) } fun item(name: String, count: Long): ItemStack { if (count <= 0L) return ItemStack.EMPTY - return ItemStack(items[name] ?: return ItemStack.EMPTY, count = count) + return ItemStack(Registries.items[name] ?: return ItemStack.EMPTY, count = count) } fun item(name: String, count: Long, parameters: JsonObject): ItemStack { if (count <= 0L) return ItemStack.EMPTY - return ItemStack(items[name] ?: return ItemStack.EMPTY, count = count, parameters = parameters) + return ItemStack(Registries.items[name] ?: return ItemStack.EMPTY, count = count, parameters = parameters) } fun item(descriptor: JsonObject): ItemStack { @@ -350,9 +287,6 @@ object Starbound : ISBFileLocator { @Volatile var terminateLoading = false - var defaultMovementParameters = MovementParameters() - private set - fun loadJsonAsset(path: String): JsonElement? { val filename: String val jsonPath: String? @@ -425,14 +359,14 @@ object Starbound : ISBFileLocator { state.setTableFunction("evalFunction", this) {args -> val name = args.getString() - val fn = jsonFunctions[name] ?: throw NoSuchElementException("No such function $name") + val fn = Registries.jsonFunctions[name] ?: throw NoSuchElementException("No such function $name") args.push(fn.value.evaluate(args.getDouble())) 1 } state.setTableFunction("evalFunction2", this) {args -> val name = args.getString() - val fn = json2Functions[name] ?: throw NoSuchElementException("No such 2function $name") + val fn = Registries.json2Functions[name] ?: throw NoSuchElementException("No such 2function $name") args.push(fn.value.evaluate(args.getDouble(), args.getDouble())) 1 } @@ -469,7 +403,7 @@ object Starbound : ISBFileLocator { state.setTableFunction("npcConfig", this) { args -> // Json root.npcConfig(String npcType) val name = args.getString() - args.push(npcTypes[name] ?: throw NoSuchElementException("No such NPC type $name")) + args.push(Registries.npcTypes[name] ?: throw NoSuchElementException("No such NPC type $name")) 1 } @@ -486,13 +420,13 @@ object Starbound : ISBFileLocator { state.setTableFunction("projectileConfig", this) { args -> // Json root.projectileConfig(String projectileName) val name = args.getString() - args.lua.push(projectiles[name]?.json ?: throw kotlin.NoSuchElementException("No such Projectile type $name")) + args.lua.push(Registries.projectiles[name]?.json ?: throw kotlin.NoSuchElementException("No such Projectile type $name")) 1 } state.setTableFunction("recipesForItem", this) { args -> args.lua.push(JsonArray().also { a -> - recipeRegistry.output2recipes[args.getString()]?.stream()?.map { it.toJson() }?.forEach { + RecipeRegistry.output2recipes[args.getString()]?.stream()?.map { it.toJson() }?.forEach { a.add(it) } }) @@ -502,20 +436,20 @@ object Starbound : ISBFileLocator { state.setTableFunction("itemType", this) { args -> val name = args.getString() - args.lua.push(items[name]?.value?.itemType ?: throw NoSuchElementException("No such item $name")) + args.lua.push(Registries.items[name]?.value?.itemType ?: throw NoSuchElementException("No such item $name")) 1 } state.setTableFunction("itemTags", this) { args -> val name = args.getString() - args.lua.pushStrings(items[name]?.value?.itemTags ?: throw NoSuchElementException("No such item $name")) + args.lua.pushStrings(Registries.items[name]?.value?.itemTags ?: throw NoSuchElementException("No such item $name")) 1 } state.setTableFunction("itemHasTag", this) { args -> val name = args.getString() val tag = args.getString() - args.push((items[name]?.value?.itemTags ?: throw NoSuchElementException("No such item $name")).contains(tag)) + args.push((Registries.items[name]?.value?.itemTags ?: throw NoSuchElementException("No such item $name")).contains(tag)) 1 } @@ -562,7 +496,7 @@ object Starbound : ISBFileLocator { state.setTableFunction("tenantConfig", this) { args -> // Json root.tenantConfig(String tenantName) val name = args.getString() - tenants[name]?.push(args) ?: throw NoSuchElementException("No such tenant $name") + Registries.tenants[name]?.push(args) ?: throw NoSuchElementException("No such tenant $name") 1 } @@ -577,7 +511,7 @@ object Starbound : ISBFileLocator { } } - args.push(tenants.values + args.push(Registries.tenants.values .stream() .filter { it.value.test(actualTags) } .sorted { a, b -> b.value.compareTo(a.value) } @@ -592,10 +526,10 @@ object Starbound : ISBFileLocator { if (args.isStringAt()) { val name = args.getString() - liquid = this.liquid[name]?.value ?: throw NoSuchElementException("No such liquid with name $name") + liquid = Registries.liquid[name]?.value ?: throw NoSuchElementException("No such liquid with name $name") } else { val id = args.getInt() - liquid = this.liquidByID[id]?.value ?: throw NoSuchElementException("No such liquid with ID $id") + liquid = Registries.liquid[id]?.value ?: throw NoSuchElementException("No such liquid with ID $id") } args.lua.pushStrings(liquid.statusEffects.stream().map { it.value?.value?.name }.filterNotNull().toList()) @@ -641,7 +575,7 @@ object Starbound : ISBFileLocator { state.setTableFunction("questConfig", this) { args -> val name = args.getString() - args.push(questTemplates[name] ?: throw NoSuchElementException("No such quest template $name")) + args.push(Registries.questTemplates[name] ?: throw NoSuchElementException("No such quest template $name")) 1 } @@ -658,7 +592,7 @@ object Starbound : ISBFileLocator { } state.setTableFunction("isTreasurePool", this) { args -> - args.push(args.getString() in treasurePools) + args.push(args.getString() in Registries.treasurePools) 1 } @@ -666,19 +600,19 @@ object Starbound : ISBFileLocator { val name = args.getString() val level = args.getDouble() val rand = if (args.hasSomethingAt()) java.util.Random(args.getLong()) else java.util.Random() - args.push(treasurePools[name]?.value?.evaluate(rand, level)?.stream()?.map { it.toJson() }?.filterNotNull()?.collect(JsonArrayCollector) ?: throw NoSuchElementException("No such treasure pool $name")) + args.push(Registries.treasurePools[name]?.value?.evaluate(rand, level)?.stream()?.map { it.toJson() }?.filterNotNull()?.collect(JsonArrayCollector) ?: throw NoSuchElementException("No such treasure pool $name")) 1 } state.setTableFunction("materialMiningSound", this) { args -> val name = args.getString() val mod = if (args.hasSomethingAt()) args.getString() else null - val mat = tiles[name] ?: throw NoSuchElementException("No such material $name") + val mat = Registries.tiles[name] ?: throw NoSuchElementException("No such material $name") if (mod == null) { args.push(mat.value.miningSounds.firstOrNull()) } else { - val getMod = tileModifiers[mod] ?: throw NoSuchElementException("No such material modifier $mod") + val getMod = Registries.tileModifiers[mod] ?: throw NoSuchElementException("No such material modifier $mod") args.push(getMod.value.miningSounds.firstOrNull() ?: mat.value.miningSounds.firstOrNull()) } @@ -688,12 +622,12 @@ object Starbound : ISBFileLocator { state.setTableFunction("materialFootstepSound", this) { args -> val name = args.getString() val mod = if (args.hasSomethingAt()) args.getString() else null - val mat = tiles[name] ?: throw NoSuchElementException("No such material $name") + val mat = Registries.tiles[name] ?: throw NoSuchElementException("No such material $name") if (mod == null) { args.push(mat.value.footstepSound) } else { - val getMod = tileModifiers[mod] ?: throw NoSuchElementException("No such material modifier $mod") + val getMod = Registries.tileModifiers[mod] ?: throw NoSuchElementException("No such material modifier $mod") args.push(getMod.value.footstepSound ?: mat.value.footstepSound) } @@ -703,12 +637,12 @@ object Starbound : ISBFileLocator { state.setTableFunction("materialHealth", this) { args -> val name = args.getString() val mod = if (args.hasSomethingAt()) args.getString() else null - val mat = tiles[name] ?: throw NoSuchElementException("No such material $name") + val mat = Registries.tiles[name] ?: throw NoSuchElementException("No such material $name") if (mod == null) { args.push(mat.value.health) } else { - val getMod = tileModifiers[mod] ?: throw NoSuchElementException("No such material modifier $mod") + val getMod = Registries.tileModifiers[mod] ?: throw NoSuchElementException("No such material modifier $mod") args.push(getMod.value.health + mat.value.health) } @@ -717,23 +651,23 @@ object Starbound : ISBFileLocator { state.setTableFunction("materialConfig", this) { args -> val name = args.getString() - args.pushFull(tiles[name]) + args.pushFull(Registries.tiles[name]) 1 } state.setTableFunction("modConfig", this) { args -> val name = args.getString() - args.pushFull(tileModifiers[name]) + args.pushFull(Registries.tileModifiers[name]) 1 } state.setTableFunction("liquidConfig", this) { args -> if (args.isNumberAt()) { val id = args.getLong().toInt() - args.pushFull(liquidByID[id]) + args.pushFull(Registries.liquid[id]) } else { val name = args.getString() - args.pushFull(liquid[name]) + args.pushFull(Registries.liquid[name]) } 1 @@ -741,13 +675,13 @@ object Starbound : ISBFileLocator { state.setTableFunction("liquidName", this) { args -> val id = args.getLong().toInt() - args.push(liquidByID[id]?.value?.name ?: throw NoSuchElementException("No such liquid with ID $id")) + args.push(Registries.liquid[id]?.value?.name ?: throw NoSuchElementException("No such liquid with ID $id")) 1 } state.setTableFunction("liquidId", this) { args -> val name = args.getString() - args.push(liquid[name]?.value?.name ?: throw NoSuchElementException("No such liquid $name")) + args.push(Registries.liquid[name]?.value?.name ?: throw NoSuchElementException("No such liquid $name")) 1 } @@ -755,20 +689,20 @@ object Starbound : ISBFileLocator { val name = args.getString() val param = args.getString() // parity: если скила не существует, то оригинальный движок просто возвращает nil - args.push(monsterSkills[name]?.value?.config?.get(param)) + args.push(Registries.monsterSkills[name]?.value?.config?.get(param)) 1 } state.setTableFunction("monsterParameters", this) { args -> val name = args.getString() - val monster = monsterTypes[name] ?: throw NoSuchElementException("No such monster type $name") + val monster = Registries.monsterTypes[name] ?: throw NoSuchElementException("No such monster type $name") args.push(monster.traverseJsonPath("baseParameters")) 1 } state.setTableFunction("monsterMovementSettings", this) { args -> val name = args.getString() - val monster = monsterTypes[name] ?: throw NoSuchElementException("No such monster type $name") + val monster = Registries.monsterTypes[name] ?: throw NoSuchElementException("No such monster type $name") args.push(gson.toJsonTree(monster.value.baseParameters.movementSettings)) 1 } @@ -778,20 +712,20 @@ object Starbound : ISBFileLocator { } state.setTableFunction("hasTech", this) { args -> - args.push(args.getString() in techs) + args.push(args.getString() in Registries.techs) 1 } state.setTableFunction("techType", this) { args -> val name = args.getString() - val tech = techs[name] ?: throw NoSuchElementException("No such tech $name") + val tech = Registries.techs[name] ?: throw NoSuchElementException("No such tech $name") args.push(tech.value.type) 1 } state.setTableFunction("techConfig", this) { args -> val name = args.getString() - val tech = techs[name] ?: throw NoSuchElementException("No such tech $name") + val tech = Registries.techs[name] ?: throw NoSuchElementException("No such tech $name") tech.push(args) 1 } @@ -908,50 +842,7 @@ object Starbound : ISBFileLocator { var playerDefinition: PlayerDefinition by WriteOnce() private set - private fun loadStage( - log: ILoadingLog, - loader: ((String) -> Unit) -> Unit, - name: String, - ) { - if (terminateLoading) - return - - val time = System.currentTimeMillis() - val line = log.line("Loading $name...".also(logger::info)) - - loader { - if (terminateLoading) { - throw InterruptedException("Game is terminating") - } - - line.text = it - } - - line.text = ("Loaded $name in ${System.currentTimeMillis() - time}ms".also(logger::info)) - } - - private fun loadStage( - log: ILoadingLog, - registry: ObjectRegistry, - files: List, - ) { - loadStage(log, loader = { - for (listedFile in files) { - try { - it("Loading $listedFile") - registry.add(listedFile) - } catch (err: Throwable) { - logger.error("Loading ${registry.name} definition file $listedFile", err) - } - - if (terminateLoading) { - break - } - } - }, registry.name) - } - - private fun doInitialize(log: ILoadingLog) { + private fun doInitialize(log: ILoadingLog, parallel: Boolean = true) { var time = System.currentTimeMillis() if (archivePaths.isNotEmpty()) { @@ -1006,30 +897,12 @@ object Starbound : ISBFileLocator { log.line("Finished building file index in ${System.currentTimeMillis() - time}ms".also(logger::info)) - val pool = ForkJoinPool.commonPool() val tasks = ArrayList>() + val pool = if (parallel) ForkJoinPool.commonPool() else ForkJoinPool(1) - tasks.add(pool.submit { loadStage(log, { loadItemDefinitions(it, ext2files) }, "item definitions") }) - - tasks.add(pool.submit { loadStage(log, { loadJsonFunctions(it, ext2files["functions"] ?: listOf()) }, "json functions") }) - tasks.add(pool.submit { loadStage(log, { loadJson2Functions(it, ext2files["2functions"] ?: listOf()) }, "json 2functions") }) - tasks.add(pool.submit { loadStage(log, { loadRecipes(it, ext2files["recipe"] ?: listOf()) }, "recipes") }) - tasks.add(pool.submit { loadStage(log, { loadTreasurePools(it, ext2files["treasurepools"] ?: listOf()) }, "treasure pools") }) - - tasks.add(pool.submit { loadStage(log, _tiles, ext2files["material"] ?: listOf()) }) - tasks.add(pool.submit { loadStage(log, _tileModifiers, ext2files["matmod"] ?: listOf()) }) - tasks.add(pool.submit { loadStage(log, _liquid, ext2files["liquid"] ?: listOf()) }) - tasks.add(pool.submit { loadStage(log, _worldObjects, ext2files["object"] ?: listOf()) }) - tasks.add(pool.submit { loadStage(log, _statusEffects, ext2files["statuseffect"] ?: listOf()) }) - tasks.add(pool.submit { loadStage(log, _species, ext2files["species"] ?: listOf()) }) - tasks.add(pool.submit { loadStage(log, _particles, ext2files["particle"] ?: listOf()) }) - tasks.add(pool.submit { loadStage(log, _questTemplates, ext2files["questtemplate"] ?: listOf()) }) - tasks.add(pool.submit { loadStage(log, _techs, ext2files["tech"] ?: listOf()) }) - tasks.add(pool.submit { loadStage(log, _npcTypes, ext2files["npctype"] ?: listOf()) }) - // tasks.add(pool.submit { loadStage(log, _projectiles, ext2files["projectile"] ?: listOf()) }) - // tasks.add(pool.submit { loadStage(log, _tenants, ext2files["tenant"] ?: listOf()) }) - tasks.add(pool.submit { loadStage(log, _monsterSkills, ext2files["monsterskill"] ?: listOf()) }) - // tasks.add(pool.submit { loadStage(log, _monsterTypes, ext2files["monstertype"] ?: listOf()) }) + tasks.addAll(Registries.load(log, ext2files, pool)) + tasks.addAll(RecipeRegistry.load(log, ext2files, pool)) + tasks.addAll(GlobalDefaults.load(log, pool)) AssetPathStack.block("/") { //playerDefinition = gson.fromJson(locate("/player.config").reader(), PlayerDefinition::class.java) @@ -1037,7 +910,8 @@ object Starbound : ISBFileLocator { tasks.forEach { it.join() } - defaultMovementParameters = gson.fromJson(jsonReader("/default_actor_movement.config"))!! + if (!parallel) + pool.shutdown() initializing = false initialized = true @@ -1077,134 +951,5 @@ object Starbound : ISBFileLocator { initCallbacks.clear() } } - - private fun loadItemDefinitions(callback: (String) -> Unit, files: Map>) { - val fileMap = mapOf( - "item" to ItemDefinition::class.java, - "currency" to CurrencyItemDefinition::class.java, - "liqitem" to LiquidItemDefinition::class.java, - "matitem" to MaterialItemDefinition::class.java, - "flashlight" to FlashlightDefinition::class.java, - "harvestingtool" to HarvestingToolPrototype::class.java, - "head" to HeadArmorItemDefinition::class.java, - "chest" to ChestArmorItemDefinition::class.java, - "legs" to LegsArmorItemDefinition::class.java, - "back" to BackArmorItemDefinition::class.java, - ) - - for ((ext, clazz) in fileMap) { - val fileList = files[ext] ?: continue - - for (listedFile in fileList) { - try { - callback("Loading $listedFile") - val json = gson.fromJson(listedFile.reader(), JsonObject::class.java) - val def: IItemDefinition = AssetPathStack(listedFile.computeDirectory()) { gson.fromJson(JsonTreeReader(json), clazz) } - _items.add(def, json, listedFile) - } catch (err: Throwable) { - logger.error("Loading item definition file $listedFile", err) - } - - if (terminateLoading) { - return - } - } - } - } - - private fun loadJsonFunctions(callback: (String) -> Unit, files: Collection) { - for (listedFile in files) { - callback("Loading $listedFile") - try { - val json = gson.getAdapter(JsonObject::class.java).read(JsonReader(listedFile.reader()).also { it.isLenient = true }) - - for ((k, v) in json.entrySet()) { - try { - callback("Loading $k from $listedFile") - val fn = gson.fromJson(JsonTreeReader(v), JsonFunction::class.java) - _jsonFunctions.add(fn, v, listedFile, k) - } catch (err: Exception) { - logger.error("Loading json function definition $k from file $listedFile", err) - } - } - } catch (err: Exception) { - logger.error("Loading json function definition $listedFile", err) - } - - if (terminateLoading) { - return - } - } - } - - private fun loadJson2Functions(callback: (String) -> Unit, files: Collection) { - for (listedFile in files) { - callback("Loading $listedFile") - - try { - val json = gson.getAdapter(JsonObject::class.java).read(JsonReader(listedFile.reader()).also { it.isLenient = true }) - - for ((k, v) in json.entrySet()) { - try { - callback("Loading $k from $listedFile") - val fn = gson.fromJson(JsonTreeReader(v), Json2Function::class.java) - _json2Functions.add(fn, v, listedFile, k) - } catch (err: Throwable) { - logger.error("Loading json 2function definition $k from file $listedFile", err) - } - } - } catch (err: Exception) { - logger.error("Loading json 2function definition $listedFile", err) - } - - if (terminateLoading) { - return - } - } - } - - private fun loadTreasurePools(callback: (String) -> Unit, files: Collection) { - for (listedFile in files) { - callback("Loading $listedFile") - - try { - val json = gson.getAdapter(JsonObject::class.java).read(JsonReader(listedFile.reader()).also { it.isLenient = true }) - - for ((k, v) in json.entrySet()) { - try { - callback("Loading $k from $listedFile") - val result = gson.fromJson(JsonTreeReader(v), TreasurePoolDefinition::class.java) - result.name = k - _treasurePools.add(result, v, listedFile) - } catch (err: Throwable) { - logger.error("Loading treasure pool definition $k from file $listedFile", err) - } - } - } catch (err: Exception) { - logger.error("Loading treasure pool definition $listedFile", err) - } - - if (terminateLoading) { - return - } - } - } - - private fun loadRecipes(callback: (String) -> Unit, files: Collection) { - for (listedFile in files) { - try { - callback("Loading $listedFile") - val json = gson.fromJson(listedFile.reader(), JsonElement::class.java) - val value = gson.fromJson(JsonTreeReader(json), RecipeDefinition::class.java) - recipeRegistry.add(RegistryObject(value, json, listedFile)) - } catch (err: Throwable) { - logger.error("Loading recipe definition file $listedFile", err) - } - - if (terminateLoading) { - return - } - } - } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/TileRenderer.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/TileRenderer.kt index fedd6304..ad08347a 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/TileRenderer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/TileRenderer.kt @@ -6,6 +6,7 @@ import com.github.benmanes.caffeine.cache.Scheduler import org.apache.logging.log4j.LogManager import org.lwjgl.opengl.GL45.* import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf +import ru.dbotthepony.kstarbound.Registries import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.client.StarboundClient import ru.dbotthepony.kstarbound.client.gl.* @@ -49,14 +50,14 @@ class TileRenderers(val client: StarboundClient) { fun getMaterialRenderer(defName: String): TileRenderer { return matCache.get(defName) { - val def = Starbound.tiles[defName] // TODO: Пустой рендерер + val def = Registries.tiles[defName] // TODO: Пустой рендерер TileRenderer(this, def!!.value) } } fun getModifierRenderer(defName: String): TileRenderer { return modCache.get(defName) { - val def = Starbound.tileModifiers[defName] // TODO: Пустой рендерер + val def = Registries.tileModifiers[defName] // TODO: Пустой рендерер TileRenderer(this, def!!.value) } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/RegistryReference.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/RegistryReference.kt index f00c5bf6..5736a269 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/RegistryReference.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/RegistryReference.kt @@ -31,7 +31,7 @@ class RegistryReferenceFactory : TypeAdapterFactory { } fun add(registry: ObjectRegistry): RegistryReferenceFactory { - return add(registry.clazz.java, registry.view::get, registry.name) + return add(registry.clazz.java, registry::get, registry.name) } inline fun add(noinline resolver: (String) -> RegistryObject?, name: String) = add(T::class.java, resolver, name) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/player/Avatar.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/player/Avatar.kt index dcc5c34a..b53398c2 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/player/Avatar.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/player/Avatar.kt @@ -6,6 +6,7 @@ import com.google.gson.JsonPrimitive import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet +import ru.dbotthepony.kstarbound.Registries import ru.dbotthepony.kstarbound.RegistryObject import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.defs.player.TechDefinition @@ -21,7 +22,7 @@ import kotlin.collections.ArrayList * * [Avatar] реализует Lua интерфейс `player`. */ -class Avatar(val starbound: Starbound, val uniqueId: UUID) { +class Avatar(val uniqueId: UUID) { enum class EssentialSlot { BEAM_AXE, WIRE_TOOL, @@ -61,7 +62,7 @@ class Avatar(val starbound: Starbound, val uniqueId: UUID) { * Teaches the player any recipes which can be used to craft the specified item. */ fun giveBlueprint(name: String): Boolean { - val item = starbound.item(name).conciseToNull() ?: return false + val item = Starbound.item(name).conciseToNull() ?: return false if (knownBlueprints.add(item)) { newBlueprints.add(item) @@ -75,7 +76,7 @@ class Avatar(val starbound: Starbound, val uniqueId: UUID) { * Returns `true` if the player knows one or more recipes to create the specified item and `false` otherwise. */ fun blueprintKnown(name: String): Boolean { - return (starbound.item(name).conciseToNull() ?: return false) in knownBlueprints + return (Starbound.item(name).conciseToNull() ?: return false) in knownBlueprints } /** @@ -83,9 +84,9 @@ class Avatar(val starbound: Starbound, val uniqueId: UUID) { */ private fun blueprintKnown(name: JsonElement): Boolean { if (name is JsonPrimitive) { - return (starbound.item(name.asString).conciseToNull() ?: return false) in knownBlueprints + return (Starbound.item(name.asString).conciseToNull() ?: return false) in knownBlueprints } else if (name is JsonObject) { - return (starbound.item(name).conciseToNull() ?: return false) in knownBlueprints + return (Starbound.item(name).conciseToNull() ?: return false) in knownBlueprints } else { return false } @@ -98,9 +99,9 @@ class Avatar(val starbound: Starbound, val uniqueId: UUID) { val item: ItemStack if (name is JsonPrimitive) { - item = starbound.item(name.asString).conciseToNull() ?: return false + item = Starbound.item(name.asString).conciseToNull() ?: return false } else if (name is JsonObject) { - item = starbound.item(name).conciseToNull() ?: return false + item = Starbound.item(name).conciseToNull() ?: return false } else { return false } @@ -117,14 +118,14 @@ class Avatar(val starbound: Starbound, val uniqueId: UUID) { * Adds the specified tech to the player's list of available (unlockable) techs. */ fun makeTechAvailable(name: String): Boolean { - return availableTechs.add(starbound.techs[name] ?: return false) + return availableTechs.add(Registries.techs[name] ?: return false) } /** * Removes the specified tech from player's list of available (unlockable) techs. */ fun makeTechUnavailable(name: String): Boolean { - val tech = starbound.techs[name] ?: return false + val tech = Registries.techs[name] ?: return false if (availableTechs.remove(tech)) { enabledTechs.remove(tech) @@ -139,7 +140,7 @@ class Avatar(val starbound: Starbound, val uniqueId: UUID) { * Unlocks the specified tech, allowing it to be equipped through the tech GUI. */ fun enableTech(name: String): Boolean { - val tech = starbound.techs[name] ?: return false + val tech = Registries.techs[name] ?: return false availableTechs.add(tech) return enabledTechs.add(tech) } @@ -148,7 +149,7 @@ class Avatar(val starbound: Starbound, val uniqueId: UUID) { * Equips the specified tech. */ fun equipTech(name: String): Boolean { - val tech = starbound.techs[name] ?: return false + val tech = Registries.techs[name] ?: return false availableTechs.add(tech) enabledTechs.add(tech) return equippedTechs.put(tech.value.type, tech) != tech @@ -158,7 +159,7 @@ class Avatar(val starbound: Starbound, val uniqueId: UUID) { * Unequips the specified tech. */ fun unequipTech(name: String): Boolean { - val tech = starbound.techs[name] ?: return false + val tech = Registries.techs[name] ?: return false return equippedTechs.remove(tech.value.type) == tech } @@ -323,20 +324,20 @@ class Avatar(val starbound: Starbound, val uniqueId: UUID) { lua.setTableClosure("addCurrency", this) { addCurrency(it.getString(), it.getLong()) } lua.setTableFunction("consumeCurrency", this) { it.lua.push(consumeCurrency(it.getString(), it.getLong())); 1 } lua.setTableClosure("cleanupItems", this) { cleanupItems() } - lua.setTableClosure("giveItem", this) { giveItem(starbound.item(it.getValue())) } + lua.setTableClosure("giveItem", this) { giveItem(Starbound.item(it.getValue())) } lua.setTableFunction("hasItem", this) { - it.lua.push(hasItem(starbound.item(it.getValue()), it.getBoolOrNull() ?: false)) + it.lua.push(hasItem(Starbound.item(it.getValue()), it.getBoolOrNull() ?: false)) 1 } lua.setTableFunction("hasCountOfItem", this) { - it.lua.push(hasCountOfItem(starbound.item(it.getValue()), it.getBoolOrNull() ?: false)) + it.lua.push(hasCountOfItem(Starbound.item(it.getValue()), it.getBoolOrNull() ?: false)) 1 } lua.setTableFunction("consumeItem", this) { - it.lua.push(consumeItem(starbound.item(it.getValue()), it.getBoolOrNull() ?: false, it.getBoolOrNull() ?: false).toJson()) + it.lua.push(consumeItem(Starbound.item(it.getValue()), it.getBoolOrNull() ?: false, it.getBoolOrNull() ?: false).toJson()) 1 } @@ -415,7 +416,7 @@ class Avatar(val starbound: Starbound, val uniqueId: UUID) { lua.setTableClosure("giveEssentialItem", this) { val name = it.getString() - val item = starbound.item(it.getValue()) + val item = Starbound.item(it.getValue()) val slot = essentialSlotsMap[name] ?: throw IllegalArgumentException("Invalid slot '$name'") giveEssentialItem(slot, item) } @@ -435,7 +436,7 @@ class Avatar(val starbound: Starbound, val uniqueId: UUID) { lua.setTableClosure("setEquippedItem", this) { val name = it.getString() - val item = starbound.item(it.getValue()) + val item = Starbound.item(it.getValue()) val slot = equipmentSlotsMap[name] ?: throw IllegalArgumentException("Invalid slot '$name'") setEquippedItem(slot, item) } @@ -446,7 +447,7 @@ class Avatar(val starbound: Starbound, val uniqueId: UUID) { } lua.setTableClosure("setSwapSlotItem", this) { - cursorItem = starbound.item(it.getValue()) + cursorItem = Starbound.item(it.getValue()) } lua.setTableFunction("startQuest", this) { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/player/QuestInstance.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/player/QuestInstance.kt index a2b028d2..2e943e7a 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/player/QuestInstance.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/player/QuestInstance.kt @@ -5,6 +5,8 @@ import com.google.gson.JsonElement import com.google.gson.JsonObject import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import org.apache.logging.log4j.LogManager +import ru.dbotthepony.kstarbound.Registries +import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.defs.quest.QuestTemplate import ru.dbotthepony.kstarbound.lua.LuaState import ru.dbotthepony.kstarbound.lua.MessageHandler @@ -20,7 +22,7 @@ class QuestInstance( val serverID: UUID? = null, val worldID: String? = null ) { - val template: QuestTemplate = avatar.starbound.questTemplates[descriptor.templateId]?.value ?: throw IllegalArgumentException("No such quest template ${descriptor.templateId}") + val template: QuestTemplate = Registries.questTemplates[descriptor.templateId]?.value ?: throw IllegalArgumentException("No such quest template ${descriptor.templateId}") val id get() = descriptor.questId val lua = LuaState() @@ -70,7 +72,7 @@ class QuestInstance( } private fun addReward(value: JsonElement) { - val item = avatar.starbound.item(value) + val item = Starbound.item(value) if (!item.isEmpty) { rewards.add(item) @@ -101,7 +103,7 @@ class QuestInstance( lua.setTableClosure("complete", this) { complete() } lua.setTableClosure("fail", this) { fail() } lua.setTableClosure("setCanTurnIn", this) { canTurnIn = it.getBool() } - lua.setTableFunction("questDescriptor", this) { it.lua.push(avatar.starbound.gson.toJsonTree(descriptor)); 1 } + lua.setTableFunction("questDescriptor", this) { it.lua.push(Starbound.gson.toJsonTree(descriptor)); 1 } lua.setTableFunction("questId", this) { it.lua.push(id); 1 } lua.setTableFunction("templateId", this) { it.lua.push(template.id); 1 } lua.setTableFunction("seed", this) { it.lua.push(seed); 1 } @@ -120,7 +122,7 @@ class QuestInstance( lua.pop() - avatar.starbound.pushLuaAPI(lua) + Starbound.pushLuaAPI(lua) avatar.pushLuaAPI(lua) lua.exposeConfig(template.scriptConfig) } @@ -162,7 +164,7 @@ class QuestInstance( if (!isInitialized) { isInitialized = true - val script = avatar.starbound.locate(template.script.fullPath) + val script = Starbound.locate(template.script.fullPath) if (!script.isFile) { LOGGER.error("Quest ${template.id} specifies ${template.script.fullPath} as its script, but it is not a file or does not exist!") diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/IChunkCell.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/IChunkCell.kt index c66651aa..b7ca691f 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/IChunkCell.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/IChunkCell.kt @@ -35,15 +35,10 @@ interface IChunkCell : IStruct2i { var envBiome: Int var isIndestructible: Boolean - fun read( - materialAccess: (Int) -> RegistryObject?, - modifierAccess: (Int) -> RegistryObject?, - liquidAccess: (Int) -> RegistryObject?, - stream: DataInputStream - ) { - foreground.read(materialAccess, modifierAccess, stream) - background.read(materialAccess, modifierAccess, stream) - liquid.read(liquidAccess, stream) + fun read(stream: DataInputStream) { + foreground.read(stream) + background.read(stream) + liquid.read(stream) stream.skipNBytes(1) // collisionMap diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ILiquidState.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ILiquidState.kt index 592c280f..6eb51ca5 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ILiquidState.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ILiquidState.kt @@ -1,5 +1,6 @@ package ru.dbotthepony.kstarbound.world.api +import ru.dbotthepony.kstarbound.Registries import ru.dbotthepony.kstarbound.RegistryObject import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition import java.io.DataInputStream @@ -10,11 +11,8 @@ interface ILiquidState { var pressure: Float var isInfinite: Boolean - fun read( - liquidAccess: (Int) -> RegistryObject?, - stream: DataInputStream - ) { - def = liquidAccess(stream.readUnsignedByte())?.value + fun read(stream: DataInputStream) { + def = Registries.liquid[stream.readUnsignedByte()]?.value level = stream.readFloat() pressure = stream.readFloat() isInfinite = stream.readBoolean() diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ITileState.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ITileState.kt index e488026e..e0612724 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ITileState.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ITileState.kt @@ -1,5 +1,6 @@ package ru.dbotthepony.kstarbound.world.api +import ru.dbotthepony.kstarbound.Registries import ru.dbotthepony.kstarbound.RegistryObject import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier @@ -33,15 +34,11 @@ interface ITileState { } } - fun read( - materialAccess: (Int) -> RegistryObject?, - modifierAccess: (Int) -> RegistryObject?, - stream: DataInputStream - ) { - material = materialAccess(stream.readUnsignedShort())?.value ?: BuiltinMetaMaterials.EMPTY + fun read(stream: DataInputStream) { + material = Registries.tiles[stream.readUnsignedShort()]?.value ?: BuiltinMetaMaterials.EMPTY setHueShift(stream.read()) color = TileColor.of(stream.read()) - modifier = modifierAccess(stream.readUnsignedShort())?.value + modifier = Registries.tileModifiers[stream.readUnsignedShort()]?.value setModHueShift(stream.read()) } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/WorldObject.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/WorldObject.kt index 96526954..be646146 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/WorldObject.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/WorldObject.kt @@ -5,6 +5,7 @@ import com.google.gson.JsonObject import com.google.gson.TypeAdapter import com.google.gson.reflect.TypeToken import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap +import ru.dbotthepony.kstarbound.Registries import ru.dbotthepony.kstarbound.RegistryObject import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.client.world.ClientWorld @@ -29,7 +30,7 @@ open class WorldObject( ) : JsonDriven(prototype.file.computeDirectory()) { constructor(world: World<*, *>, data: JsonObject) : this( world, - Starbound.worldObjects[data["name"]?.asString ?: throw IllegalArgumentException("Missing object name")] ?: throw IllegalArgumentException("No such object defined for '${data["name"]}'"), + Registries.worldObjects[data["name"]?.asString ?: throw IllegalArgumentException("Missing object name")] ?: throw IllegalArgumentException("No such object defined for '${data["name"]}'"), data.get("tilePosition", vectors) { throw IllegalArgumentException("No tilePosition was present in saved data") } ) { direction = data.get("direction", directions) { Side.LEFT }