More Lua bindings, minor performance improvements

This commit is contained in:
DBotThePony 2024-05-01 13:09:16 +07:00
parent 884a432676
commit 151571e8d0
Signed by: DBot
GPG Key ID: DCC23B5715498507
16 changed files with 227 additions and 131 deletions

View File

@ -227,10 +227,9 @@ object Globals {
tasks.add(Starbound.GLOBAL_SCOPE.launch { load("/names/profanityfilter.config", ::profanityFilterInternal, lazy { Starbound.gson.listAdapter() }) }.asCompletableFuture()) 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) onLoadedFuture.complete(Unit)
}.exceptionally {
onLoadedFuture.completeExceptionally(it)
} }
return tasks return tasks

View File

@ -45,6 +45,7 @@ import ru.dbotthepony.kstarbound.defs.world.BiomeDefinition
import ru.dbotthepony.kstarbound.item.ItemRegistry import ru.dbotthepony.kstarbound.item.ItemRegistry
import ru.dbotthepony.kstarbound.json.JsonPatch import ru.dbotthepony.kstarbound.json.JsonPatch
import ru.dbotthepony.kstarbound.json.builder.JsonFactory import ru.dbotthepony.kstarbound.json.builder.JsonFactory
import ru.dbotthepony.kstarbound.json.fromJsonTreeFast
import ru.dbotthepony.kstarbound.world.terrain.TerrainSelectorType import ru.dbotthepony.kstarbound.world.terrain.TerrainSelectorType
import ru.dbotthepony.kstarbound.util.AssetPathStack import ru.dbotthepony.kstarbound.util.AssetPathStack
import ru.dbotthepony.kstarbound.world.physics.CollisionType 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()]) val elem = JsonPatch.applyAsync(Starbound.ELEMENTS_ADAPTER.read(listedFile.asyncJsonReader().await()), patches[listedFile.computeFullPath()])
AssetPathStack(listedFile.computeFullPath()) { AssetPathStack(listedFile.computeFullPath()) {
val read = adapter.fromJsonTree(elem) val read = adapter.fromJsonTreeFast(elem)
val keys = keyProvider(read) val keys = keyProvider(read)
after(read, listedFile) after(read, listedFile)
@ -199,7 +200,7 @@ object Registries {
for ((k, v) in json.entrySet()) { for ((k, v) in json.entrySet()) {
try { try {
val value = adapter.fromJsonTree(v) val value = adapter.fromJsonTreeFast(v)
transform(value, k) transform(value, k)
registry.add { registry.add {
@ -264,7 +265,7 @@ object Registries {
private fun loadMetaMaterials(): Future<*> { private fun loadMetaMaterials(): Future<*> {
return Starbound.GLOBAL_SCOPE.async { return Starbound.GLOBAL_SCOPE.async {
val read = Starbound.loadJsonAsset("/metamaterials.config").await() ?: return@async val read = Starbound.loadJsonAsset("/metamaterials.config").await() ?: return@async
val read2 = Starbound.gson.getAdapter(object : TypeToken<ImmutableList<MetaMaterialDef>>() {}).fromJsonTree(read) val read2 = Starbound.gson.getAdapter(object : TypeToken<ImmutableList<MetaMaterialDef>>() {}).fromJsonTreeFast(read)
for (def in read2) { for (def in read2) {
tiles.add { tiles.add {

View File

@ -772,7 +772,7 @@ object Starbound : BlockableEventLoop("Universe Thread"), Scheduler, ISBFileLoca
} }
fun generateName(asset: String, random: RandomGenerator): String { 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 tries = 500
var result = "" var result = ""

View File

@ -5,6 +5,7 @@ import com.google.gson.JsonObject
import com.google.gson.TypeAdapter import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader import com.google.gson.stream.JsonReader
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap
import ru.dbotthepony.kstarbound.json.FastJsonTreeReader
import ru.dbotthepony.kstarbound.json.VersionedJson import ru.dbotthepony.kstarbound.json.VersionedJson
import java.util.concurrent.Future import java.util.concurrent.Future
@ -19,24 +20,32 @@ object VersionRegistry {
return VersionedJson(name, currentVersion(name), contents) return VersionedJson(name, currentVersion(name), contents)
} }
fun <T> load(name: String, reader: JsonReader, adapter: TypeAdapter<T>): T { private fun migrate(read: VersionedJson, identifier: String = read.id): JsonElement {
val read = this.adapter.read(reader) ?: throw NullPointerException("Expected versioned json $name, but found null") if (read.version != currentVersion(identifier)) {
throw IllegalStateException("NYI: Migrating $identifier from ${read.version} to ${currentVersion(identifier)}")
if (read.version != currentVersion(name)) {
throw IllegalStateException("NYI: Migrating $name from ${read.version} to ${currentVersion(name)}")
} }
return adapter.fromJsonTree(read.content) return read.content
}
fun <T> load(name: String, reader: JsonReader, adapter: TypeAdapter<T>): T {
val read = this.adapter.read(reader) ?: throw NullPointerException("Expected versioned json $name, but found null")
return adapter.fromJsonTree(migrate(read, name))
}
fun <T> load(name: String, reader: JsonElement, adapter: TypeAdapter<T>): 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 { fun load(name: String, reader: JsonReader): JsonElement {
val read = this.adapter.read(reader) ?: throw NullPointerException("Expected versioned json $name, but found null") val read = this.adapter.read(reader) ?: throw NullPointerException("Expected versioned json $name, but found null")
return migrate(read, name)
}
if (read.version != currentVersion(name)) { fun load(name: String, reader: JsonElement): JsonElement {
throw IllegalStateException("NYI: Migrating $name from ${read.version} to ${currentVersion(name)}") val read = this.adapter.read(FastJsonTreeReader(reader)) ?: throw NullPointerException("Expected versioned json $name, but found null")
} return migrate(read, name)
return read.content
} }
private val adapter by lazy { Starbound.gson.getAdapter(VersionedJson::class.java) } private val adapter by lazy { Starbound.gson.getAdapter(VersionedJson::class.java) }

View File

@ -24,6 +24,7 @@ import ru.dbotthepony.kommons.io.StreamCodec
import ru.dbotthepony.kommons.io.readVarLong import ru.dbotthepony.kommons.io.readVarLong
import ru.dbotthepony.kommons.io.writeBinaryString import ru.dbotthepony.kommons.io.writeBinaryString
import ru.dbotthepony.kommons.io.writeVarLong import ru.dbotthepony.kommons.io.writeVarLong
import ru.dbotthepony.kommons.util.Either
import ru.dbotthepony.kommons.util.KOptional import ru.dbotthepony.kommons.util.KOptional
import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.VersionRegistry 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 * 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 * [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) * reset stats on next item load from serialized state)
*/ */
fun build(level: Double? = null, seed: Long? = null, random: RandomGenerator? = null): ItemStack { fun buildConfig(level: Double? = null, seed: Long? = null, random: RandomGenerator? = null): Pair<Either<JsonObject, Table>, Either<JsonObject, Table>> {
val builder = ref.json["builder"]?.asString ?: return ref.type.factory(ref, ref.json, parameters.deepCopy(), count) val builder = ref.json["builder"]?.asString ?: return Either.left<JsonObject, Table>(ref.json) to Either.left(parameters.deepCopy())
try { val lua = LuaEnvironment()
val lua = LuaEnvironment() lua.attach(Starbound.loadScript(builder))
lua.attach(Starbound.loadScript(builder)) lua.random = random ?: lua.random
lua.random = random ?: lua.random lua.init(false)
lua.init(false)
val (config, parameters) = lua.invokeGlobal("build", ref.directory + "/", lua.from(ref.json), lua.from(parameters), level, seed) val (config, parameters) = lua.invokeGlobal("build", ref.directory + "/", lua.from(ref.json), lua.from(parameters), level, seed)
return Either.right<JsonObject, Table>(config as Table) to Either.right(parameters as Table)
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)
}
} }
fun write(stream: DataOutputStream) { fun write(stream: DataOutputStream) {

View File

@ -70,6 +70,10 @@ object ItemRegistry {
return entry return entry
} }
operator fun contains(name: String): Boolean {
return name in entries
}
@JsonFactory @JsonFactory
data class ReadData( data class ReadData(
val itemName: String, val itemName: String,

View File

@ -3,6 +3,7 @@ package ru.dbotthepony.kstarbound.item
import com.google.gson.JsonElement import com.google.gson.JsonElement
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
import it.unimi.dsi.fastutil.objects.ObjectArrayList
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kstarbound.IStarboundFile import ru.dbotthepony.kstarbound.IStarboundFile
import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.Starbound
@ -18,7 +19,7 @@ object RecipeRegistry {
data class Entry(val value: RecipeDefinition, val json: JsonElement, val file: IStarboundFile) data class Entry(val value: RecipeDefinition, val json: JsonElement, val file: IStarboundFile)
private val recipesInternal = ArrayList<Entry>() private val recipesInternal = ObjectArrayList<Entry>()
private val group2recipesInternal = Object2ObjectOpenHashMap<String, ArrayList<Entry>>() private val group2recipesInternal = Object2ObjectOpenHashMap<String, ArrayList<Entry>>()
private val group2recipesBacking = Object2ObjectOpenHashMap<String, List<Entry>>() private val group2recipesBacking = Object2ObjectOpenHashMap<String, List<Entry>>()
private val output2recipesInternal = Object2ObjectOpenHashMap<String, ArrayList<Entry>>() private val output2recipesInternal = Object2ObjectOpenHashMap<String, ArrayList<Entry>>()

View File

@ -1,6 +1,7 @@
package ru.dbotthepony.kstarbound.lua.bindings package ru.dbotthepony.kstarbound.lua.bindings
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap
import org.apache.logging.log4j.LogManager
import org.classdump.luna.ByteString import org.classdump.luna.ByteString
import org.classdump.luna.LuaRuntimeException import org.classdump.luna.LuaRuntimeException
import org.classdump.luna.Table import org.classdump.luna.Table
@ -12,9 +13,11 @@ import ru.dbotthepony.kstarbound.item.RecipeRegistry
import ru.dbotthepony.kstarbound.Registries import ru.dbotthepony.kstarbound.Registries
import ru.dbotthepony.kstarbound.Registry import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.VersionRegistry
import ru.dbotthepony.kstarbound.defs.image.Image import ru.dbotthepony.kstarbound.defs.image.Image
import ru.dbotthepony.kstarbound.defs.image.SpriteReference import ru.dbotthepony.kstarbound.defs.image.SpriteReference
import ru.dbotthepony.kstarbound.defs.item.ItemDescriptor 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.LUA_HINT_ARRAY
import ru.dbotthepony.kstarbound.lua.LuaEnvironment import ru.dbotthepony.kstarbound.lua.LuaEnvironment
import ru.dbotthepony.kstarbound.lua.StateMachine 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.nextOptionalFloat
import ru.dbotthepony.kstarbound.lua.nextOptionalInteger import ru.dbotthepony.kstarbound.lua.nextOptionalInteger
import ru.dbotthepony.kstarbound.lua.set import ru.dbotthepony.kstarbound.lua.set
import ru.dbotthepony.kstarbound.lua.tableMapOf
import ru.dbotthepony.kstarbound.lua.tableOf import ru.dbotthepony.kstarbound.lua.tableOf
import ru.dbotthepony.kstarbound.lua.toJsonFromLua
import ru.dbotthepony.kstarbound.lua.toLuaInteger import ru.dbotthepony.kstarbound.lua.toLuaInteger
import ru.dbotthepony.kstarbound.util.random.random import ru.dbotthepony.kstarbound.util.random.random
import kotlin.collections.component1 import kotlin.collections.component1
@ -43,6 +48,8 @@ import kotlin.collections.set
import kotlin.collections.withIndex import kotlin.collections.withIndex
import kotlin.math.max import kotlin.math.max
private val LOGGER = LogManager.getLogger()
private fun <T : Any> lookup(registry: Registry<T>, key: Any?): Registry.Entry<T>? { private fun <T : Any> lookup(registry: Registry<T>, key: Any?): Registry.Entry<T>? {
if (key is ByteString) { if (key is ByteString) {
return registry[key.decode()] return registry[key.decode()]
@ -63,20 +70,20 @@ private fun <T : Any> lookupStrict(registry: Registry<T>, 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") 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") 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 ref = SpriteReference.create(name.decode())
val sprite = ref.sprite ?: throw LuaRuntimeException("No such image or sprite $ref") 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 { private fun imageSpaces(context: ExecutionContext, arguments: ArgumentIterator): StateMachine {
@ -133,15 +140,15 @@ private fun registryDefExists(registry: Registry<*>): LuaFunction<ByteString, *,
} }
} }
private fun recipesForItem(context: ExecutionContext, name: ByteString) { private val recipesForItem = luaFunction { name: ByteString ->
val list = RecipeRegistry.output2recipes[name.decode()] val list = RecipeRegistry.output2recipes[name.decode()]
if (list == null) { if (list == null) {
context.returnBuffer.setTo(context.newTable()) returnBuffer.setTo(newTable())
} else { } else {
context.returnBuffer.setTo(context.newTable(list.size, 0).also { returnBuffer.setTo(newTable(list.size, 0).also {
for ((i, v) in list.withIndex()) { 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) val liquid = lookup(Registries.liquid, id)
if (liquid == null) { if (liquid == null) {
context.returnBuffer.setTo(context.newTable()) returnBuffer.setTo(tableOf())
} else { } else {
context.returnBuffer.setTo(context.newTable(liquid.value.statusEffects.size, 0).also { returnBuffer.setTo(tableOf(*liquid.value.statusEffects.map { it.key.map({ it }, { it }) }.toTypedArray()))
for ((k, v) in liquid.value.statusEffects.withIndex()) {
it[k + 1] = v.key.map({ it }, { it })
}
})
} }
} }
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) { private fun materialMiningSound(context: ExecutionContext, arguments: ArgumentIterator) {
val tile = lookup(Registries.tiles, arguments.nextAny()) val tile = lookup(Registries.tiles, arguments.nextAny())
val mod = lookup(Registries.tiles, arguments.nextOptionalAny(null)) 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() })) context.returnBuffer.setTo(Globals.client.defaultFootstepSound.map({ it }, { it.random() }))
} }
private fun materialHealth(context: ExecutionContext, arguments: ArgumentIterator) { private val materialHealth = luaFunction { id: Any ->
context.returnBuffer.setTo(lookupStrict(Registries.tiles, arguments.nextAny()).value.actualDamageTable.totalHealth) returnBuffer.setTo(lookupStrict(Registries.tiles, id).value.actualDamageTable.totalHealth)
} }
private fun liquidName(context: ExecutionContext, arguments: ArgumentIterator) { private val liquidName = luaFunction { id: Any ->
context.returnBuffer.setTo(lookupStrict(Registries.liquid, arguments.nextAny()).key) returnBuffer.setTo(lookupStrict(Registries.liquid, id).key)
} }
private fun liquidId(context: ExecutionContext, arguments: ArgumentIterator) { private val liquidId = luaFunction { id: Any ->
context.returnBuffer.setTo(lookupStrict(Registries.liquid, arguments.nextAny()).id) returnBuffer.setTo(lookupStrict(Registries.liquid, id).id)
} }
private fun techType(context: ExecutionContext, arguments: ArgumentIterator) { private val techType = luaFunction { id: Any ->
context.returnBuffer.setTo(lookupStrict(Registries.techs, arguments.nextAny()).value.type) returnBuffer.setTo(lookupStrict(Registries.techs, id).value.type)
} }
private fun techConfig(context: ExecutionContext, arguments: ArgumentIterator) { private val techConfig = luaFunction { id: Any ->
context.returnBuffer.setTo(lookupStrict(Registries.techs, arguments.nextAny()).json) returnBuffer.setTo(lookupStrict(Registries.techs, id).json)
} }
private val jobject = luaFunction { returnBuffer.setTo(createJsonObject()) } 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)) 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) { fun provideRootBindings(lua: LuaEnvironment) {
val table = lua.newTable() val table = lua.newTable()
lua.globals["root"] = table 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 -> table["assetJson"] = assetJson
returnBuffer.setTo(from(Starbound.loadJsonAsset(path.decode()).get())) table["makeCurrentVersionedJson"] = makeCurrentVersionedJson
} table["loadVersionedJson"] = loadVersionedJson
table["makeCurrentVersionedJson"] = luaStub("makeCurrentVersionedJson") table["evalFunction"] = evalFunction
table["loadVersionedJson"] = luaStub("loadVersionedJson") table["evalFunction2"] = evalFunction2
table["imageSize"] = imageSize
table["evalFunction"] = luaFunction(::evalFunction)
table["evalFunction2"] = luaFunction(::evalFunction2)
table["imageSize"] = luaFunction(::imageSize)
table["imageSpaces"] = luaFunctionNS("imageSpaces", ::imageSpaces) table["imageSpaces"] = luaFunctionNS("imageSpaces", ::imageSpaces)
table["nonEmptyRegion"] = luaFunction(::nonEmptyRegion) table["nonEmptyRegion"] = luaFunction(::nonEmptyRegion)
table["npcConfig"] = registryDef(Registries.npcTypes) table["npcConfig"] = registryDef(Registries.npcTypes)
@ -365,18 +438,17 @@ fun provideRootBindings(lua: LuaEnvironment) {
table["projectileGravityMultiplier"] = luaStub("projectileGravityMultiplier") table["projectileGravityMultiplier"] = luaStub("projectileGravityMultiplier")
table["projectileConfig"] = registryDef(Registries.projectiles) table["projectileConfig"] = registryDef(Registries.projectiles)
table["recipesForItem"] = luaFunction(::recipesForItem) table["recipesForItem"] = recipesForItem
table["itemType"] = luaStub("itemType") table["itemType"] = itemType
table["itemTags"] = luaStub("itemTags") table["itemTags"] = itemTags
table["itemHasTag"] = luaStub("itemHasTag") table["itemHasTag"] = itemHasTag
table["itemConfig"] = itemConfig
table["createItem"] = createItem
table["itemConfig"] = luaStub("itemConfig")
table["createItem"] = luaStub("createItem")
table["tenantConfig"] = registryDef(Registries.tenants) table["tenantConfig"] = registryDef(Registries.tenants)
table["getMatchingTenants"] = luaFunction(::getMatchingTenants) table["getMatchingTenants"] = luaFunction(::getMatchingTenants)
table["liquidStatusEffects"] = luaFunction(::liquidStatusEffects) table["liquidStatusEffects"] = liquidStatusEffects
table["generateName"] = luaFunction { asset: ByteString, seed: Number? -> table["generateName"] = luaFunction { asset: ByteString, seed: Number? ->
returnBuffer.setTo(Starbound.generateName(asset.decode(), if (seed == null) lua.random else random(seed.toLong()))) 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["npcPortrait"] = luaStub("npcPortrait")
table["isTreasurePool"] = registryDefExists(Registries.treasurePools) 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["materialMiningSound"] = luaFunctionN("materialMiningSound", ::materialMiningSound)
table["materialFootstepSound"] = luaFunctionN("materialFootstepSound", ::materialFootstepSound) table["materialFootstepSound"] = luaFunctionN("materialFootstepSound", ::materialFootstepSound)
table["materialHealth"] = luaFunctionN("materialHealth", ::materialHealth) table["materialHealth"] = materialHealth
table["materialConfig"] = registryDef2(Registries.tiles) table["materialConfig"] = registryDef2(Registries.tiles)
table["modConfig"] = registryDef2(Registries.tileModifiers) table["modConfig"] = registryDef2(Registries.tileModifiers)
table["liquidName"] = luaFunctionN("liquidName", ::liquidName) table["liquidName"] = liquidName
table["liquidId"] = luaFunctionN("liquidId", ::liquidId) table["liquidId"] = liquidId
table["createBiome"] = createBiome
table["createBiome"] = luaStub("createBiome")
table["monsterSkillParameter"] = luaStub("monsterSkillParameter") table["monsterSkillParameter"] = luaStub("monsterSkillParameter")
table["monsterParameters"] = luaStub("monsterParameters") table["monsterParameters"] = luaStub("monsterParameters")
table["monsterMovementSettings"] = luaStub("monsterMovementSettings") table["monsterMovementSettings"] = luaStub("monsterMovementSettings")
table["hasTech"] = registryDefExists(Registries.techs) table["hasTech"] = registryDefExists(Registries.techs)
table["techType"] = luaFunctionN("techType", ::techType) table["techType"] = techType
table["techConfig"] = luaFunctionN("techConfig", ::techConfig) table["techConfig"] = techConfig
table["treeStemDirectory"] = luaFunction { name: ByteString -> table["treeStemDirectory"] = treeStemDirectory
returnBuffer.setTo(Registries.treeStemVariants[name.decode()]?.file?.computeDirectory(true) ?: "/") table["treeFoliageDirectory"] = treeFoliageDirectory
}
table["treeFoliageDirectory"] = luaFunction { name: ByteString ->
returnBuffer.setTo(Registries.treeFoliageVariants[name.decode()]?.file?.computeDirectory(true))
}
table["collection"] = luaStub("collection") table["collection"] = luaStub("collection")
table["collectables"] = luaStub("collectables") table["collectables"] = luaStub("collectables")
table["elementalResistance"] = luaStub("elementalResistance") table["elementalResistance"] = luaStub("elementalResistance")
table["dungeonMetadata"] = luaStub("dungeonMetadata") table["dungeonMetadata"] = luaStub("dungeonMetadata")
table["behavior"] = luaStub("behavior") table["behavior"] = luaStub("behavior")
lua.globals["jobject"] = jobject
lua.globals["jarray"] = jarray
lua.globals["jremove"] = jremove
lua.globals["jsize"] = jsize
lua.globals["jresize"] = jresize
} }

View File

@ -4,6 +4,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
import it.unimi.dsi.fastutil.io.FastByteArrayInputStream import it.unimi.dsi.fastutil.io.FastByteArrayInputStream
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream 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.readByteArray
import ru.dbotthepony.kommons.io.readVarInt import ru.dbotthepony.kommons.io.readVarInt
import ru.dbotthepony.kommons.io.writeByteArray import ru.dbotthepony.kommons.io.writeByteArray
@ -68,7 +69,7 @@ class NetworkedDynamicGroup<T : Any>(private val factory: () -> T, private val a
} }
private var listenedVersion = 0L private var listenedVersion = 0L
private val nonListenableElements = ArrayList<NetworkedElement>() private val nonListenableElements = ObjectArrayList<NetworkedElement>()
private val listenersOnElements = Int2ObjectAVLTreeMap<Listenable.L>() private val listenersOnElements = Int2ObjectAVLTreeMap<Listenable.L>()
private fun setupElement(element: NetworkedElement, id: Int) { private fun setupElement(element: NetworkedElement, id: Int) {

View File

@ -1,5 +1,6 @@
package ru.dbotthepony.kstarbound.network.syncher package ru.dbotthepony.kstarbound.network.syncher
import it.unimi.dsi.fastutil.objects.ObjectArrayList
import ru.dbotthepony.kommons.io.readVarInt import ru.dbotthepony.kommons.io.readVarInt
import ru.dbotthepony.kommons.io.writeVarInt import ru.dbotthepony.kommons.io.writeVarInt
import ru.dbotthepony.kommons.util.Listenable import ru.dbotthepony.kommons.util.Listenable
@ -16,8 +17,8 @@ class NetworkedGroup() : NetworkedElement() {
// element -> propagateInterpolation // element -> propagateInterpolation
private data class Element(val element: NetworkedElement, val propagateInterpolation: Boolean, val listener: Listenable.L?) private data class Element(val element: NetworkedElement, val propagateInterpolation: Boolean, val listener: Listenable.L?)
private val elements = ArrayList<Element>() private val elements = ObjectArrayList<Element>()
private val nonListenableElements = ArrayList<NetworkedElement>() private val nonListenableElements = ObjectArrayList<NetworkedElement>()
private var listenedVersion = 0L private var listenedVersion = 0L
var isInterpolating = false var isInterpolating = false
@ -26,7 +27,7 @@ class NetworkedGroup() : NetworkedElement() {
private set private set
override fun toString(): String { override fun toString(): String {
if (elements.isEmpty()) if (elements.isEmpty)
return "NetworkedGroup[]" return "NetworkedGroup[]"
return "NetworkedGroup[\n${elements.joinToString("\n") { "\t" + it.element.toString() }}]" return "NetworkedGroup[\n${elements.joinToString("\n") { "\t" + it.element.toString() }}]"

View File

@ -522,6 +522,7 @@ class ServerConnection(val server: StarboundServer, type: ConnectionType) : Conn
} }
override fun disconnect(reason: String) { override fun disconnect(reason: String) {
LOGGER.info("${alias()} disconnect initiated with reason $reason")
announceDisconnect(reason) announceDisconnect(reason)
if (channel.isOpen) { if (channel.isOpen) {

View File

@ -29,6 +29,9 @@ import kotlin.math.absoluteValue
open class BlockableEventLoop(name: String) : Thread(name), ScheduledExecutorService { open class BlockableEventLoop(name: String) : Thread(name), ScheduledExecutorService {
private class ScheduledTask<T>(callable: Callable<T>, var executeAt: Long, val repeat: Boolean, val timeDelay: Long, val isFixedDelay: Boolean) : FutureTask<T>(callable), ScheduledFuture<T> { private class ScheduledTask<T>(callable: Callable<T>, var executeAt: Long, val repeat: Boolean, val timeDelay: Long, val isFixedDelay: Boolean) : FutureTask<T>(callable), ScheduledFuture<T> {
override fun compareTo(other: Delayed): Int { override fun compareTo(other: Delayed): Int {
if (other is ScheduledTask<*>)
return executeAt.compareTo(other.executeAt)
return getDelay(TimeUnit.NANOSECONDS).compareTo(other.getDelay(TimeUnit.NANOSECONDS)) return getDelay(TimeUnit.NANOSECONDS).compareTo(other.getDelay(TimeUnit.NANOSECONDS))
} }

View File

@ -67,7 +67,7 @@ class EntityIndex(val geometry: WorldGeometry) {
inner class Entry(val value: AbstractEntity) : Comparable<Entry> { inner class Entry(val value: AbstractEntity) : Comparable<Entry> {
private val sectors = Object2IntAVLTreeMap<Sector>() private val sectors = Object2IntAVLTreeMap<Sector>()
val id = counter.getAndIncrement() val id = counter.getAndIncrement()
private val fixtures = ArrayList<Fixture>(1) private val fixtures = ObjectArrayList<Fixture>(1)
// default fixture since in most cases it should be enough // default fixture since in most cases it should be enough
val fixture = Fixture() val fixture = Fixture()

View File

@ -190,9 +190,20 @@ class ContainerObject(config: Registry.Entry<ObjectDefinition>) : WorldObject(co
override fun handleMessage(connection: Int, message: String, arguments: JsonArray): JsonElement? { override fun handleMessage(connection: Int, message: String, arguments: JsonArray): JsonElement? {
return when (message.lowercase()) { return when (message.lowercase()) {
"startcrafting" -> startCrafting() "startcrafting" -> {
"stopcrafting" -> stopCrafting() startCrafting()
"burncontainercontents" -> burnContainerContents() JsonNull.INSTANCE
}
"stopcrafting" -> {
stopCrafting()
JsonNull.INSTANCE
}
"burncontainercontents" -> {
burnContainerContents()
JsonNull.INSTANCE
}
// returns not inserted items // returns not inserted items
"additems" -> items.add(ItemDescriptor(arguments[0]).build()).toJson() "additems" -> items.add(ItemDescriptor(arguments[0]).build()).toJson()
@ -219,16 +230,13 @@ class ContainerObject(config: Registry.Entry<ObjectDefinition>) : WorldObject(co
} }
} }
private fun startCrafting(): JsonElement { private fun startCrafting() {
return JsonNull.INSTANCE
} }
private fun stopCrafting(): JsonElement { private fun stopCrafting() {
return JsonNull.INSTANCE
} }
private fun burnContainerContents(): JsonElement { private fun burnContainerContents() {
return JsonNull.INSTANCE
} }
// Networking of this container to legacy clients is incredibly stupid, // Networking of this container to legacy clients is incredibly stupid,

View File

@ -306,11 +306,6 @@ abstract class TileEntity : AbstractEntity() {
} else { } else {
clear = false 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
} }
} }

View File

@ -15,6 +15,7 @@ import ru.dbotthepony.kommons.gson.value
import ru.dbotthepony.kstarbound.Registries import ru.dbotthepony.kstarbound.Registries
import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.defs.world.TerrainSelectorParameters import ru.dbotthepony.kstarbound.defs.world.TerrainSelectorParameters
import ru.dbotthepony.kstarbound.json.fromJsonTreeFast
private inline fun <reified D : Any, T : AbstractTerrainSelector<D>> data(noinline factory: (D, TerrainSelectorParameters) -> T): Data<D, T> { private inline fun <reified D : Any, T : AbstractTerrainSelector<D>> data(noinline factory: (D, TerrainSelectorParameters) -> T): Data<D, T> {
return Data(TypeToken.get(D::class.java), factory) 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) { for (value in entries) {
if (value.lowercase == type) { if (value.lowercase == type) {
return Factory(value.data.adapter.fromJsonTree(config), value.data.factory as ((Any, TerrainSelectorParameters) -> AbstractTerrainSelector<Any>)) return Factory(value.data.adapter.fromJsonTreeFast(config), value.data.factory as ((Any, TerrainSelectorParameters) -> AbstractTerrainSelector<Any>))
} }
} }
} else { } else {
for (value in entries) { for (value in entries) {
if (value.lowercase == type) { if (value.lowercase == type) {
return Factory(value.data.adapter.fromJsonTree(json), value.data.factory as ((Any, TerrainSelectorParameters) -> AbstractTerrainSelector<Any>)) return Factory(value.data.adapter.fromJsonTreeFast(json), value.data.factory as ((Any, TerrainSelectorParameters) -> AbstractTerrainSelector<Any>))
} }
} }
} }