Move stuff around to reduce stress on main starbound class

This commit is contained in:
DBotThePony 2023-09-29 22:05:26 +07:00
parent 86782e259e
commit ccf696a156
Signed by: DBot
GPG Key ID: DCC23B5715498507
14 changed files with 475 additions and 380 deletions

View File

@ -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<Unit>() {
private fun readResolve(): Any = EmptyTask
override fun getRawResult() {
}
override fun setRawResult(value: Unit?) {
}
override fun exec(): Boolean {
return true
}
}
private inline fun <reified T> load(path: String, accept: KMutableProperty0<T>, 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<ForkJoinTask<*>> {
val tasks = ArrayList<ForkJoinTask<*>>()
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"
})
}
}

View File

@ -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)

View File

@ -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 <reified T : Any> ObjectRegistry(name: String, noinline key: ((T) -> String)? = null, noinline intKey: ((T) -> Int)? = null): ObjectRegistry<T> {
@ -123,11 +124,15 @@ class RegistryObject<T : Any>(
}
class ObjectRegistry<T : Any>(val clazz: KClass<T>, val name: String, val key: ((T) -> String)? = null, val intKey: ((T) -> Int)? = null) {
private val objects = Object2ObjectOpenHashMap<String, RegistryObject<T>>()
private val intObjects = Int2ObjectOpenHashMap<RegistryObject<T>>()
val objects = Object2ObjectOpenHashMap<String, RegistryObject<T>>()
val intObjects = Int2ObjectOpenHashMap<RegistryObject<T>>()
val view: Map<String, RegistryObject<T>> = Collections.unmodifiableMap(objects)
val intView: Int2ObjectMap<RegistryObject<T>> = Int2ObjectMaps.unmodifiable(intObjects)
val values get() = objects.values
operator fun get(index: String) = objects[index]
operator fun get(index: Int): RegistryObject<T>? = intObjects[index]
operator fun contains(index: String) = index in objects
operator fun contains(index: Int) = index in intObjects
fun clear() {
objects.clear()

View File

@ -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<RegistryObject<RecipeDefinition>>()
private val group2recipesInternal = Object2ObjectOpenHashMap<String, LinkedList<RegistryObject<RecipeDefinition>>>()
private val group2recipesBacking = Object2ObjectOpenHashMap<String, List<RegistryObject<RecipeDefinition>>>()
@ -49,4 +58,30 @@ class RecipeRegistry {
}).add(recipe)
}
}
fun load(log: ILoadingLog, fileTree: Map<String, List<IStarboundFile>>, executor: ForkJoinPool): List<ForkJoinTask<*>> {
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<RecipeDefinition>(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"
}))
}
}

View File

@ -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<JsonFunction>("json functions")
val json2Functions = ObjectRegistry<Json2Function>("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 <T : Any> loadStage(
log: ILoadingLog,
registry: ObjectRegistry<T>,
files: List<IStarboundFile>,
) {
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<String, List<IStarboundFile>>, executor: ForkJoinPool): List<ForkJoinTask<*>> {
val tasks = ArrayList<ForkJoinTask<*>>()
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<String, Collection<IStarboundFile>>, executor: ForkJoinPool): List<ForkJoinTask<*>> {
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<ForkJoinTask<*>>()
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<IStarboundFile>) {
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<JsonFunction>(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<IStarboundFile>) {
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<Json2Function>(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<IStarboundFile>) {
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<TreasurePoolDefinition>(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
}
}
}
}

View File

@ -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<String> = Interner.newWeakInterner()
// val strings: Interner<String> = 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<String> = 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<JsonFunction>("json functions")
val jsonFunctions = _jsonFunctions.view
private val _json2Functions = ObjectRegistry<Json2Function>("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 <T : Any> loadStage(
log: ILoadingLog,
registry: ObjectRegistry<T>,
files: List<IStarboundFile>,
) {
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<ForkJoinTask<*>>()
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<String, Collection<IStarboundFile>>) {
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<IStarboundFile>) {
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<JsonFunction>(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<IStarboundFile>) {
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<Json2Function>(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<IStarboundFile>) {
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<TreasurePoolDefinition>(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<IStarboundFile>) {
for (listedFile in files) {
try {
callback("Loading $listedFile")
val json = gson.fromJson(listedFile.reader(), JsonElement::class.java)
val value = gson.fromJson<RecipeDefinition>(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
}
}
}
}

View File

@ -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)
}
}

View File

@ -31,7 +31,7 @@ class RegistryReferenceFactory : TypeAdapterFactory {
}
fun <T : Any> add(registry: ObjectRegistry<T>): RegistryReferenceFactory {
return add(registry.clazz.java, registry.view::get, registry.name)
return add(registry.clazz.java, registry::get, registry.name)
}
inline fun <reified T: Any> add(noinline resolver: (String) -> RegistryObject<T>?, name: String) = add(T::class.java, resolver, name)

View File

@ -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) {

View File

@ -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!")

View File

@ -35,15 +35,10 @@ interface IChunkCell : IStruct2i {
var envBiome: Int
var isIndestructible: Boolean
fun read(
materialAccess: (Int) -> RegistryObject<TileDefinition>?,
modifierAccess: (Int) -> RegistryObject<MaterialModifier>?,
liquidAccess: (Int) -> RegistryObject<LiquidDefinition>?,
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

View File

@ -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<LiquidDefinition>?,
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()

View File

@ -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<TileDefinition>?,
modifierAccess: (Int) -> RegistryObject<MaterialModifier>?,
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())
}

View File

@ -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 }