diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Globals.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Globals.kt index 69b2819d..36d86cc6 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Globals.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Globals.kt @@ -227,10 +227,9 @@ object Globals { tasks.add(Starbound.GLOBAL_SCOPE.launch { load("/names/profanityfilter.config", ::profanityFilterInternal, lazy { Starbound.gson.listAdapter() }) }.asCompletableFuture()) - CompletableFuture.allOf(*tasks.toTypedArray()).thenApply { + // if some load tasks fail then don't fail entire future + CompletableFuture.allOf(*tasks.map { it.exceptionally { null } }.toTypedArray()).thenApply { onLoadedFuture.complete(Unit) - }.exceptionally { - onLoadedFuture.completeExceptionally(it) } return tasks diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Registries.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Registries.kt index 70835ba8..3eb6b478 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Registries.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Registries.kt @@ -45,6 +45,7 @@ import ru.dbotthepony.kstarbound.defs.world.BiomeDefinition import ru.dbotthepony.kstarbound.item.ItemRegistry import ru.dbotthepony.kstarbound.json.JsonPatch import ru.dbotthepony.kstarbound.json.builder.JsonFactory +import ru.dbotthepony.kstarbound.json.fromJsonTreeFast import ru.dbotthepony.kstarbound.world.terrain.TerrainSelectorType import ru.dbotthepony.kstarbound.util.AssetPathStack import ru.dbotthepony.kstarbound.world.physics.CollisionType @@ -124,7 +125,7 @@ object Registries { val elem = JsonPatch.applyAsync(Starbound.ELEMENTS_ADAPTER.read(listedFile.asyncJsonReader().await()), patches[listedFile.computeFullPath()]) AssetPathStack(listedFile.computeFullPath()) { - val read = adapter.fromJsonTree(elem) + val read = adapter.fromJsonTreeFast(elem) val keys = keyProvider(read) after(read, listedFile) @@ -199,7 +200,7 @@ object Registries { for ((k, v) in json.entrySet()) { try { - val value = adapter.fromJsonTree(v) + val value = adapter.fromJsonTreeFast(v) transform(value, k) registry.add { @@ -264,7 +265,7 @@ object Registries { private fun loadMetaMaterials(): Future<*> { return Starbound.GLOBAL_SCOPE.async { val read = Starbound.loadJsonAsset("/metamaterials.config").await() ?: return@async - val read2 = Starbound.gson.getAdapter(object : TypeToken>() {}).fromJsonTree(read) + val read2 = Starbound.gson.getAdapter(object : TypeToken>() {}).fromJsonTreeFast(read) for (def in read2) { tiles.add { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt index d1a3b537..482f29ac 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt @@ -772,7 +772,7 @@ object Starbound : BlockableEventLoop("Universe Thread"), Scheduler, ISBFileLoca } fun generateName(asset: String, random: RandomGenerator): String { - val load = loadJsonAsset(asset) as? JsonArray ?: return "missingasset" + val load = loadJsonAsset(asset).get() as? JsonArray ?: return "missingasset" var tries = 500 var result = "" diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/VersionRegistry.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/VersionRegistry.kt index 954b6fa0..1d841926 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/VersionRegistry.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/VersionRegistry.kt @@ -5,6 +5,7 @@ import com.google.gson.JsonObject import com.google.gson.TypeAdapter import com.google.gson.stream.JsonReader import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap +import ru.dbotthepony.kstarbound.json.FastJsonTreeReader import ru.dbotthepony.kstarbound.json.VersionedJson import java.util.concurrent.Future @@ -19,24 +20,32 @@ object VersionRegistry { return VersionedJson(name, currentVersion(name), contents) } - fun load(name: String, reader: JsonReader, adapter: TypeAdapter): T { - val read = this.adapter.read(reader) ?: throw NullPointerException("Expected versioned json $name, but found null") - - if (read.version != currentVersion(name)) { - throw IllegalStateException("NYI: Migrating $name from ${read.version} to ${currentVersion(name)}") + private fun migrate(read: VersionedJson, identifier: String = read.id): JsonElement { + if (read.version != currentVersion(identifier)) { + throw IllegalStateException("NYI: Migrating $identifier from ${read.version} to ${currentVersion(identifier)}") } - return adapter.fromJsonTree(read.content) + return read.content + } + + fun load(name: String, reader: JsonReader, adapter: TypeAdapter): T { + val read = this.adapter.read(reader) ?: throw NullPointerException("Expected versioned json $name, but found null") + return adapter.fromJsonTree(migrate(read, name)) + } + + fun load(name: String, reader: JsonElement, adapter: TypeAdapter): T { + val read = this.adapter.read(FastJsonTreeReader(reader)) ?: throw NullPointerException("Expected versioned json $name, but found null") + return adapter.fromJsonTree(migrate(read, name)) } fun load(name: String, reader: JsonReader): JsonElement { val read = this.adapter.read(reader) ?: throw NullPointerException("Expected versioned json $name, but found null") + return migrate(read, name) + } - if (read.version != currentVersion(name)) { - throw IllegalStateException("NYI: Migrating $name from ${read.version} to ${currentVersion(name)}") - } - - return read.content + fun load(name: String, reader: JsonElement): JsonElement { + val read = this.adapter.read(FastJsonTreeReader(reader)) ?: throw NullPointerException("Expected versioned json $name, but found null") + return migrate(read, name) } private val adapter by lazy { Starbound.gson.getAdapter(VersionedJson::class.java) } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/ItemDescriptor.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/ItemDescriptor.kt index 1b7ef06d..8c79cefc 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/ItemDescriptor.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/ItemDescriptor.kt @@ -24,6 +24,7 @@ import ru.dbotthepony.kommons.io.StreamCodec import ru.dbotthepony.kommons.io.readVarLong import ru.dbotthepony.kommons.io.writeBinaryString import ru.dbotthepony.kommons.io.writeVarLong +import ru.dbotthepony.kommons.util.Either import ru.dbotthepony.kommons.util.KOptional import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.VersionRegistry @@ -203,30 +204,33 @@ data class ItemDescriptor( } } + fun build(level: Double? = null, seed: Long? = null, random: RandomGenerator? = null): ItemStack { + try { + val (config, parameters) = buildConfig(level, seed, random) + val jConfig = config.map({ it }, { toJsonFromLua(it).asJsonObject }) + val jParameters = parameters.map({ it }, { toJsonFromLua(it).asJsonObject }) + return ref.type.factory(ref, jConfig, jParameters, count) + } catch (err: Throwable) { + LOGGER.error("Error while building item '$name' using script ${ref.json["builder"]}", err) + return ref.type.factory(ref, ref.json, parameters.deepCopy(), count) + } + } + /** * This is fragile, to some extent, because it is SOLE RESPONSIBILITY of builder script to store * [level] and/or [seed] inside item parameters once it is built (otherwise we will get different or * reset stats on next item load from serialized state) */ - fun build(level: Double? = null, seed: Long? = null, random: RandomGenerator? = null): ItemStack { - val builder = ref.json["builder"]?.asString ?: return ref.type.factory(ref, ref.json, parameters.deepCopy(), count) + fun buildConfig(level: Double? = null, seed: Long? = null, random: RandomGenerator? = null): Pair, Either> { + val builder = ref.json["builder"]?.asString ?: return Either.left(ref.json) to Either.left(parameters.deepCopy()) - try { - val lua = LuaEnvironment() - lua.attach(Starbound.loadScript(builder)) - lua.random = random ?: lua.random - lua.init(false) + val lua = LuaEnvironment() + lua.attach(Starbound.loadScript(builder)) + lua.random = random ?: lua.random + lua.init(false) - val (config, parameters) = lua.invokeGlobal("build", ref.directory + "/", lua.from(ref.json), lua.from(parameters), level, seed) - - val jConfig = toJsonFromLua(config).asJsonObject - val jParameters = toJsonFromLua(parameters).asJsonObject - - return ref.type.factory(ref, jConfig, jParameters, count) - } catch (err: Throwable) { - LOGGER.error("Error while building item '$name' using script $builder", err) - return ref.type.factory(ref, ref.json, parameters.deepCopy(), count) - } + val (config, parameters) = lua.invokeGlobal("build", ref.directory + "/", lua.from(ref.json), lua.from(parameters), level, seed) + return Either.right(config as Table) to Either.right(parameters as Table) } fun write(stream: DataOutputStream) { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/item/ItemRegistry.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/item/ItemRegistry.kt index e9fe879a..11ef5548 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/item/ItemRegistry.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/item/ItemRegistry.kt @@ -70,6 +70,10 @@ object ItemRegistry { return entry } + operator fun contains(name: String): Boolean { + return name in entries + } + @JsonFactory data class ReadData( val itemName: String, diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/item/RecipeRegistry.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/item/RecipeRegistry.kt index ff2799e5..ecc2aa2a 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/item/RecipeRegistry.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/item/RecipeRegistry.kt @@ -3,6 +3,7 @@ package ru.dbotthepony.kstarbound.item import com.google.gson.JsonElement import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap +import it.unimi.dsi.fastutil.objects.ObjectArrayList import org.apache.logging.log4j.LogManager import ru.dbotthepony.kstarbound.IStarboundFile import ru.dbotthepony.kstarbound.Starbound @@ -18,7 +19,7 @@ object RecipeRegistry { data class Entry(val value: RecipeDefinition, val json: JsonElement, val file: IStarboundFile) - private val recipesInternal = ArrayList() + private val recipesInternal = ObjectArrayList() private val group2recipesInternal = Object2ObjectOpenHashMap>() private val group2recipesBacking = Object2ObjectOpenHashMap>() private val output2recipesInternal = Object2ObjectOpenHashMap>() diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/lua/bindings/RootBindings.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/lua/bindings/RootBindings.kt index d75fd2bb..dd278e8d 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/lua/bindings/RootBindings.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/lua/bindings/RootBindings.kt @@ -1,6 +1,7 @@ package ru.dbotthepony.kstarbound.lua.bindings import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap +import org.apache.logging.log4j.LogManager import org.classdump.luna.ByteString import org.classdump.luna.LuaRuntimeException import org.classdump.luna.Table @@ -12,9 +13,11 @@ import ru.dbotthepony.kstarbound.item.RecipeRegistry import ru.dbotthepony.kstarbound.Registries import ru.dbotthepony.kstarbound.Registry import ru.dbotthepony.kstarbound.Starbound +import ru.dbotthepony.kstarbound.VersionRegistry import ru.dbotthepony.kstarbound.defs.image.Image import ru.dbotthepony.kstarbound.defs.image.SpriteReference import ru.dbotthepony.kstarbound.defs.item.ItemDescriptor +import ru.dbotthepony.kstarbound.item.ItemRegistry import ru.dbotthepony.kstarbound.lua.LUA_HINT_ARRAY import ru.dbotthepony.kstarbound.lua.LuaEnvironment import ru.dbotthepony.kstarbound.lua.StateMachine @@ -32,7 +35,9 @@ import ru.dbotthepony.kstarbound.lua.luaStub import ru.dbotthepony.kstarbound.lua.nextOptionalFloat import ru.dbotthepony.kstarbound.lua.nextOptionalInteger import ru.dbotthepony.kstarbound.lua.set +import ru.dbotthepony.kstarbound.lua.tableMapOf import ru.dbotthepony.kstarbound.lua.tableOf +import ru.dbotthepony.kstarbound.lua.toJsonFromLua import ru.dbotthepony.kstarbound.lua.toLuaInteger import ru.dbotthepony.kstarbound.util.random.random import kotlin.collections.component1 @@ -43,6 +48,8 @@ import kotlin.collections.set import kotlin.collections.withIndex import kotlin.math.max +private val LOGGER = LogManager.getLogger() + private fun lookup(registry: Registry, key: Any?): Registry.Entry? { if (key is ByteString) { return registry[key.decode()] @@ -63,20 +70,20 @@ private fun lookupStrict(registry: Registry, key: Any?): Registry.E } } -private fun evalFunction(context: ExecutionContext, name: ByteString, value: Double) { +private val evalFunction = luaFunction { name: ByteString, value: Double -> val fn = Registries.jsonFunctions[name.decode()] ?: throw LuaRuntimeException("No such function $name") - context.returnBuffer.setTo(fn.value.evaluate(value)) + returnBuffer.setTo(fn.value.evaluate(value)) } -private fun evalFunction2(context: ExecutionContext, name: ByteString, value: Double, value2: Double) { +private val evalFunction2 = luaFunction { name: ByteString, value: Double, value2: Double -> val fn = Registries.json2Functions[name.decode()] ?: throw LuaRuntimeException("No such function $name") - context.returnBuffer.setTo(fn.value.evaluate(value, value2)) + returnBuffer.setTo(fn.value.evaluate(value, value2)) } -private fun imageSize(context: ExecutionContext, name: ByteString) { +private val imageSize = luaFunction { name: ByteString -> val ref = SpriteReference.create(name.decode()) val sprite = ref.sprite ?: throw LuaRuntimeException("No such image or sprite $ref") - context.returnBuffer.setTo(context.tableOf(sprite.width, sprite.height)) + returnBuffer.setTo(tableOf(sprite.width, sprite.height)) } private fun imageSpaces(context: ExecutionContext, arguments: ArgumentIterator): StateMachine { @@ -133,15 +140,15 @@ private fun registryDefExists(registry: Registry<*>): LuaFunction val list = RecipeRegistry.output2recipes[name.decode()] if (list == null) { - context.returnBuffer.setTo(context.newTable()) + returnBuffer.setTo(newTable()) } else { - context.returnBuffer.setTo(context.newTable(list.size, 0).also { + returnBuffer.setTo(newTable(list.size, 0).also { for ((i, v) in list.withIndex()) { - it.rawset(i + 1L, context.from(v.json)) + it.rawset(i + 1L, from(v.json)) } }) } @@ -170,30 +177,16 @@ private fun getMatchingTenants(context: ExecutionContext, tags: Table) { }) } -private fun liquidStatusEffects(context: ExecutionContext, id: Any) { +private val liquidStatusEffects = luaFunction { id: Any -> val liquid = lookup(Registries.liquid, id) if (liquid == null) { - context.returnBuffer.setTo(context.newTable()) + returnBuffer.setTo(tableOf()) } else { - context.returnBuffer.setTo(context.newTable(liquid.value.statusEffects.size, 0).also { - for ((k, v) in liquid.value.statusEffects.withIndex()) { - it[k + 1] = v.key.map({ it }, { it }) - } - }) + returnBuffer.setTo(tableOf(*liquid.value.statusEffects.map { it.key.map({ it }, { it }) }.toTypedArray())) } } -private fun createTreasure(context: ExecutionContext, arguments: ArgumentIterator) { - val pool = arguments.nextString().decode() - val level = arguments.nextFloat() - val seed = arguments.nextOptionalInteger() - - val get = Registries.treasurePools[pool] ?: throw LuaRuntimeException("No such treasure pool $pool") - - context.returnBuffer.setTo(context.tableOf(*get.value.evaluate(seed ?: System.nanoTime(), level).filter { it.isNotEmpty }.map { context.from(it.toJson()) }.toTypedArray())) -} - private fun materialMiningSound(context: ExecutionContext, arguments: ArgumentIterator) { val tile = lookup(Registries.tiles, arguments.nextAny()) val mod = lookup(Registries.tiles, arguments.nextOptionalAny(null)) @@ -229,24 +222,24 @@ private fun materialFootstepSound(context: ExecutionContext, arguments: Argument context.returnBuffer.setTo(Globals.client.defaultFootstepSound.map({ it }, { it.random() })) } -private fun materialHealth(context: ExecutionContext, arguments: ArgumentIterator) { - context.returnBuffer.setTo(lookupStrict(Registries.tiles, arguments.nextAny()).value.actualDamageTable.totalHealth) +private val materialHealth = luaFunction { id: Any -> + returnBuffer.setTo(lookupStrict(Registries.tiles, id).value.actualDamageTable.totalHealth) } -private fun liquidName(context: ExecutionContext, arguments: ArgumentIterator) { - context.returnBuffer.setTo(lookupStrict(Registries.liquid, arguments.nextAny()).key) +private val liquidName = luaFunction { id: Any -> + returnBuffer.setTo(lookupStrict(Registries.liquid, id).key) } -private fun liquidId(context: ExecutionContext, arguments: ArgumentIterator) { - context.returnBuffer.setTo(lookupStrict(Registries.liquid, arguments.nextAny()).id) +private val liquidId = luaFunction { id: Any -> + returnBuffer.setTo(lookupStrict(Registries.liquid, id).id) } -private fun techType(context: ExecutionContext, arguments: ArgumentIterator) { - context.returnBuffer.setTo(lookupStrict(Registries.techs, arguments.nextAny()).value.type) +private val techType = luaFunction { id: Any -> + returnBuffer.setTo(lookupStrict(Registries.techs, id).value.type) } -private fun techConfig(context: ExecutionContext, arguments: ArgumentIterator) { - context.returnBuffer.setTo(lookupStrict(Registries.techs, arguments.nextAny()).json) +private val techConfig = luaFunction { id: Any -> + returnBuffer.setTo(lookupStrict(Registries.techs, id).json) } private val jobject = luaFunction { returnBuffer.setTo(createJsonObject()) } @@ -343,20 +336,100 @@ private val jresize = luaFunction { self: Table, target: Long -> indexSetNoYield(self, target, indexNoYield(self, target)) } +private val assetJson = luaFunction { path: ByteString -> + returnBuffer.setTo(from(Starbound.loadJsonAsset(path.decode()).get())) +} + +private val makeCurrentVersionedJson = luaFunction { identifier: ByteString, content: Any? -> + returnBuffer.setTo(from(VersionRegistry.make(identifier.decode(), toJsonFromLua(content)).toJson())) +} + +private val loadVersionedJson = luaFunction { data: Any?, identifier: ByteString -> + returnBuffer.setTo(from(VersionRegistry.load(identifier.decode(), toJsonFromLua(data)))) +} + +private val createBiome = luaFunction { name: ByteString, seed: Number, verticalMidPoint: Number, threatLevel: Number -> + try { + val biome = Registries.biomes + .getOrThrow(name.decode()) + .value + .create(random(seed.toLong()), verticalMidPoint.toInt(), threatLevel.toDouble()) + + returnBuffer.setTo(from(Starbound.gson.toJsonTree(biome))) + } catch (err: Throwable) { + LOGGER.error("Exception while creating biome for Lua script of name $name with seed $seed, with verticalMidPoint $verticalMidPoint, with threatLevel $threatLevel", err) + } +} + +private val treeStemDirectory = luaFunction { name: ByteString -> + returnBuffer.setTo(Registries.treeStemVariants[name.decode()]?.file?.computeDirectory(true) ?: "/") +} + +private val treeFoliageDirectory = luaFunction { name: ByteString -> + returnBuffer.setTo(Registries.treeFoliageVariants[name.decode()]?.file?.computeDirectory(true) ?: "/") +} + +private val itemConfig = luaFunction { descriptor: Any, level: Number?, seed: Number? -> + val desc = ItemDescriptor(descriptor) + + if (desc.name !in ItemRegistry) { + returnBuffer.setTo() + } else { + val (config, params) = desc.buildConfig(level?.toDouble(), seed?.toLong()) + + returnBuffer.setTo(tableMapOf( + "directory" to ItemRegistry[desc.name].directory, + "config" to config.map({ from(it) }, { it }), + "parameters" to params.map({ from(it) }, { it }) + )) + } +} + +// why +private val createItem = luaFunction { descriptor: Any, level: Number?, seed: Number? -> + val desc = ItemDescriptor(descriptor) + + if (desc.name !in ItemRegistry) { + throw LuaRuntimeException("No such item ${desc.name}") + } else { + val (_, params) = desc.buildConfig(level?.toDouble(), seed?.toLong()) + val tab = desc.toTable(this) + + if (tab != null) + tab["parameters"] = params.map({ from(it) }, { it }) + + returnBuffer.setTo(tab) + } +} + +private val itemType = luaFunction { identifier: ByteString -> + returnBuffer.setTo(ItemRegistry[identifier.decode()].type.jsonName) +} + +private val itemTags = luaFunction { identifier: ByteString -> + returnBuffer.setTo(tableOf(*ItemRegistry[identifier.decode()].itemTags.toTypedArray())) +} + +private val itemHasTag = luaFunction { identifier: ByteString, tag: ByteString -> + returnBuffer.setTo(tag.decode() in ItemRegistry[identifier.decode()].itemTags) +} + fun provideRootBindings(lua: LuaEnvironment) { val table = lua.newTable() lua.globals["root"] = table + lua.globals["jobject"] = jobject + lua.globals["jarray"] = jarray + lua.globals["jremove"] = jremove + lua.globals["jsize"] = jsize + lua.globals["jresize"] = jresize - table["assetJson"] = luaFunction { path: ByteString -> - returnBuffer.setTo(from(Starbound.loadJsonAsset(path.decode()).get())) - } + table["assetJson"] = assetJson + table["makeCurrentVersionedJson"] = makeCurrentVersionedJson + table["loadVersionedJson"] = loadVersionedJson - table["makeCurrentVersionedJson"] = luaStub("makeCurrentVersionedJson") - table["loadVersionedJson"] = luaStub("loadVersionedJson") - - table["evalFunction"] = luaFunction(::evalFunction) - table["evalFunction2"] = luaFunction(::evalFunction2) - table["imageSize"] = luaFunction(::imageSize) + table["evalFunction"] = evalFunction + table["evalFunction2"] = evalFunction2 + table["imageSize"] = imageSize table["imageSpaces"] = luaFunctionNS("imageSpaces", ::imageSpaces) table["nonEmptyRegion"] = luaFunction(::nonEmptyRegion) table["npcConfig"] = registryDef(Registries.npcTypes) @@ -365,18 +438,17 @@ fun provideRootBindings(lua: LuaEnvironment) { table["projectileGravityMultiplier"] = luaStub("projectileGravityMultiplier") table["projectileConfig"] = registryDef(Registries.projectiles) - table["recipesForItem"] = luaFunction(::recipesForItem) - table["itemType"] = luaStub("itemType") - table["itemTags"] = luaStub("itemTags") - table["itemHasTag"] = luaStub("itemHasTag") + table["recipesForItem"] = recipesForItem + table["itemType"] = itemType + table["itemTags"] = itemTags + table["itemHasTag"] = itemHasTag + table["itemConfig"] = itemConfig + table["createItem"] = createItem - table["itemConfig"] = luaStub("itemConfig") - - table["createItem"] = luaStub("createItem") table["tenantConfig"] = registryDef(Registries.tenants) table["getMatchingTenants"] = luaFunction(::getMatchingTenants) - table["liquidStatusEffects"] = luaFunction(::liquidStatusEffects) + table["liquidStatusEffects"] = liquidStatusEffects table["generateName"] = luaFunction { asset: ByteString, seed: Number? -> returnBuffer.setTo(Starbound.generateName(asset.decode(), if (seed == null) lua.random else random(seed.toLong()))) @@ -389,43 +461,39 @@ fun provideRootBindings(lua: LuaEnvironment) { table["npcPortrait"] = luaStub("npcPortrait") table["isTreasurePool"] = registryDefExists(Registries.treasurePools) - table["createTreasure"] = luaFunctionN("createTreasure", ::createTreasure) + + table["createTreasure"] = luaFunction { pool: ByteString, level: Number, seed: Number? -> + val get = Registries.treasurePools[pool.decode()] ?: throw LuaRuntimeException("No such treasure pool $pool") + val random = if (seed == null) lua.random else random(seed.toLong()) + returnBuffer.setTo(tableOf(*get.value.evaluate(random, level.toDouble()).filter { it.isNotEmpty }.map { from(it.toJson()) }.toTypedArray())) + } + table["materialMiningSound"] = luaFunctionN("materialMiningSound", ::materialMiningSound) table["materialFootstepSound"] = luaFunctionN("materialFootstepSound", ::materialFootstepSound) - table["materialHealth"] = luaFunctionN("materialHealth", ::materialHealth) + table["materialHealth"] = materialHealth table["materialConfig"] = registryDef2(Registries.tiles) table["modConfig"] = registryDef2(Registries.tileModifiers) - table["liquidName"] = luaFunctionN("liquidName", ::liquidName) - table["liquidId"] = luaFunctionN("liquidId", ::liquidId) + table["liquidName"] = liquidName + table["liquidId"] = liquidId + + table["createBiome"] = createBiome - table["createBiome"] = luaStub("createBiome") table["monsterSkillParameter"] = luaStub("monsterSkillParameter") table["monsterParameters"] = luaStub("monsterParameters") table["monsterMovementSettings"] = luaStub("monsterMovementSettings") table["hasTech"] = registryDefExists(Registries.techs) - table["techType"] = luaFunctionN("techType", ::techType) - table["techConfig"] = luaFunctionN("techConfig", ::techConfig) + table["techType"] = techType + table["techConfig"] = techConfig - table["treeStemDirectory"] = luaFunction { name: ByteString -> - returnBuffer.setTo(Registries.treeStemVariants[name.decode()]?.file?.computeDirectory(true) ?: "/") - } - - table["treeFoliageDirectory"] = luaFunction { name: ByteString -> - returnBuffer.setTo(Registries.treeFoliageVariants[name.decode()]?.file?.computeDirectory(true)) - } + table["treeStemDirectory"] = treeStemDirectory + table["treeFoliageDirectory"] = treeFoliageDirectory table["collection"] = luaStub("collection") table["collectables"] = luaStub("collectables") table["elementalResistance"] = luaStub("elementalResistance") table["dungeonMetadata"] = luaStub("dungeonMetadata") table["behavior"] = luaStub("behavior") - - lua.globals["jobject"] = jobject - lua.globals["jarray"] = jarray - lua.globals["jremove"] = jremove - lua.globals["jsize"] = jsize - lua.globals["jresize"] = jresize } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/network/syncher/NetworkedDynamicGroup.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/network/syncher/NetworkedDynamicGroup.kt index 2a84923e..ebba2ce3 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/network/syncher/NetworkedDynamicGroup.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/network/syncher/NetworkedDynamicGroup.kt @@ -4,6 +4,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap import it.unimi.dsi.fastutil.ints.IntAVLTreeSet import it.unimi.dsi.fastutil.io.FastByteArrayInputStream import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream +import it.unimi.dsi.fastutil.objects.ObjectArrayList import ru.dbotthepony.kommons.io.readByteArray import ru.dbotthepony.kommons.io.readVarInt import ru.dbotthepony.kommons.io.writeByteArray @@ -68,7 +69,7 @@ class NetworkedDynamicGroup(private val factory: () -> T, private val a } private var listenedVersion = 0L - private val nonListenableElements = ArrayList() + private val nonListenableElements = ObjectArrayList() private val listenersOnElements = Int2ObjectAVLTreeMap() private fun setupElement(element: NetworkedElement, id: Int) { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/network/syncher/NetworkedGroup.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/network/syncher/NetworkedGroup.kt index c211b1d0..6435b5ac 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/network/syncher/NetworkedGroup.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/network/syncher/NetworkedGroup.kt @@ -1,5 +1,6 @@ package ru.dbotthepony.kstarbound.network.syncher +import it.unimi.dsi.fastutil.objects.ObjectArrayList import ru.dbotthepony.kommons.io.readVarInt import ru.dbotthepony.kommons.io.writeVarInt import ru.dbotthepony.kommons.util.Listenable @@ -16,8 +17,8 @@ class NetworkedGroup() : NetworkedElement() { // element -> propagateInterpolation private data class Element(val element: NetworkedElement, val propagateInterpolation: Boolean, val listener: Listenable.L?) - private val elements = ArrayList() - private val nonListenableElements = ArrayList() + private val elements = ObjectArrayList() + private val nonListenableElements = ObjectArrayList() private var listenedVersion = 0L var isInterpolating = false @@ -26,7 +27,7 @@ class NetworkedGroup() : NetworkedElement() { private set override fun toString(): String { - if (elements.isEmpty()) + if (elements.isEmpty) return "NetworkedGroup[]" return "NetworkedGroup[\n${elements.joinToString("\n") { "\t" + it.element.toString() }}]" diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/server/ServerConnection.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/server/ServerConnection.kt index 508c674e..c14fc197 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/server/ServerConnection.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/server/ServerConnection.kt @@ -522,6 +522,7 @@ class ServerConnection(val server: StarboundServer, type: ConnectionType) : Conn } override fun disconnect(reason: String) { + LOGGER.info("${alias()} disconnect initiated with reason $reason") announceDisconnect(reason) if (channel.isOpen) { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/util/BlockableEventLoop.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/util/BlockableEventLoop.kt index c17d32d9..87c03aff 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/util/BlockableEventLoop.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/util/BlockableEventLoop.kt @@ -29,6 +29,9 @@ import kotlin.math.absoluteValue open class BlockableEventLoop(name: String) : Thread(name), ScheduledExecutorService { private class ScheduledTask(callable: Callable, var executeAt: Long, val repeat: Boolean, val timeDelay: Long, val isFixedDelay: Boolean) : FutureTask(callable), ScheduledFuture { override fun compareTo(other: Delayed): Int { + if (other is ScheduledTask<*>) + return executeAt.compareTo(other.executeAt) + return getDelay(TimeUnit.NANOSECONDS).compareTo(other.getDelay(TimeUnit.NANOSECONDS)) } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/EntityIndex.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/EntityIndex.kt index a002bc1e..41866461 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/EntityIndex.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/EntityIndex.kt @@ -67,7 +67,7 @@ class EntityIndex(val geometry: WorldGeometry) { inner class Entry(val value: AbstractEntity) : Comparable { private val sectors = Object2IntAVLTreeMap() val id = counter.getAndIncrement() - private val fixtures = ArrayList(1) + private val fixtures = ObjectArrayList(1) // default fixture since in most cases it should be enough val fixture = Fixture() diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/tile/ContainerObject.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/tile/ContainerObject.kt index 0de42ecd..8a81035b 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/tile/ContainerObject.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/tile/ContainerObject.kt @@ -190,9 +190,20 @@ class ContainerObject(config: Registry.Entry) : WorldObject(co override fun handleMessage(connection: Int, message: String, arguments: JsonArray): JsonElement? { return when (message.lowercase()) { - "startcrafting" -> startCrafting() - "stopcrafting" -> stopCrafting() - "burncontainercontents" -> burnContainerContents() + "startcrafting" -> { + startCrafting() + JsonNull.INSTANCE + } + + "stopcrafting" -> { + stopCrafting() + JsonNull.INSTANCE + } + + "burncontainercontents" -> { + burnContainerContents() + JsonNull.INSTANCE + } // returns not inserted items "additems" -> items.add(ItemDescriptor(arguments[0]).build()).toJson() @@ -219,16 +230,13 @@ class ContainerObject(config: Registry.Entry) : WorldObject(co } } - private fun startCrafting(): JsonElement { - return JsonNull.INSTANCE + private fun startCrafting() { } - private fun stopCrafting(): JsonElement { - return JsonNull.INSTANCE + private fun stopCrafting() { } - private fun burnContainerContents(): JsonElement { - return JsonNull.INSTANCE + private fun burnContainerContents() { } // Networking of this container to legacy clients is incredibly stupid, diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/tile/TileEntity.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/tile/TileEntity.kt index 0b9ac562..ca1d087c 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/tile/TileEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/tile/TileEntity.kt @@ -306,11 +306,6 @@ abstract class TileEntity : AbstractEntity() { } else { clear = false } - } else { - // bugger, someone rooted onto our tile! - // will try again... - // TODO: however, shouldn't we just ignore this and move on? - clear = false } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/terrain/TerrainSelectorType.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/terrain/TerrainSelectorType.kt index bcc018f1..cb939b6c 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/terrain/TerrainSelectorType.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/terrain/TerrainSelectorType.kt @@ -15,6 +15,7 @@ import ru.dbotthepony.kommons.gson.value import ru.dbotthepony.kstarbound.Registries import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.defs.world.TerrainSelectorParameters +import ru.dbotthepony.kstarbound.json.fromJsonTreeFast private inline fun > data(noinline factory: (D, TerrainSelectorParameters) -> T): Data { return Data(TypeToken.get(D::class.java), factory) @@ -83,13 +84,13 @@ enum class TerrainSelectorType(val jsonName: String, private val data: Data<*, * for (value in entries) { if (value.lowercase == type) { - return Factory(value.data.adapter.fromJsonTree(config), value.data.factory as ((Any, TerrainSelectorParameters) -> AbstractTerrainSelector)) + return Factory(value.data.adapter.fromJsonTreeFast(config), value.data.factory as ((Any, TerrainSelectorParameters) -> AbstractTerrainSelector)) } } } else { for (value in entries) { if (value.lowercase == type) { - return Factory(value.data.adapter.fromJsonTree(json), value.data.factory as ((Any, TerrainSelectorParameters) -> AbstractTerrainSelector)) + return Factory(value.data.adapter.fromJsonTreeFast(json), value.data.factory as ((Any, TerrainSelectorParameters) -> AbstractTerrainSelector)) } } }