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

View File

@ -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<ImmutableList<MetaMaterialDef>>() {}).fromJsonTree(read)
val read2 = Starbound.gson.getAdapter(object : TypeToken<ImmutableList<MetaMaterialDef>>() {}).fromJsonTreeFast(read)
for (def in read2) {
tiles.add {

View File

@ -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 = ""

View File

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

View File

@ -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<JsonObject, Table>, Either<JsonObject, Table>> {
val builder = ref.json["builder"]?.asString ?: return Either.left<JsonObject, Table>(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<JsonObject, Table>(config as Table) to Either.right(parameters as Table)
}
fun write(stream: DataOutputStream) {

View File

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

View File

@ -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<Entry>()
private val recipesInternal = ObjectArrayList<Entry>()
private val group2recipesInternal = Object2ObjectOpenHashMap<String, ArrayList<Entry>>()
private val group2recipesBacking = Object2ObjectOpenHashMap<String, List<Entry>>()
private val output2recipesInternal = Object2ObjectOpenHashMap<String, ArrayList<Entry>>()

View File

@ -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 <T : Any> lookup(registry: Registry<T>, key: Any?): Registry.Entry<T>? {
if (key is ByteString) {
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")
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<ByteString, *,
}
}
private fun recipesForItem(context: ExecutionContext, name: ByteString) {
private val recipesForItem = luaFunction { name: ByteString ->
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
}

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.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<T : Any>(private val factory: () -> T, private val a
}
private var listenedVersion = 0L
private val nonListenableElements = ArrayList<NetworkedElement>()
private val nonListenableElements = ObjectArrayList<NetworkedElement>()
private val listenersOnElements = Int2ObjectAVLTreeMap<Listenable.L>()
private fun setupElement(element: NetworkedElement, id: Int) {

View File

@ -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<Element>()
private val nonListenableElements = ArrayList<NetworkedElement>()
private val elements = ObjectArrayList<Element>()
private val nonListenableElements = ObjectArrayList<NetworkedElement>()
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() }}]"

View File

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

View File

@ -29,6 +29,9 @@ import kotlin.math.absoluteValue
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> {
override fun compareTo(other: Delayed): Int {
if (other is ScheduledTask<*>)
return executeAt.compareTo(other.executeAt)
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> {
private val sectors = Object2IntAVLTreeMap<Sector>()
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
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? {
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<ObjectDefinition>) : 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,

View File

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

View File

@ -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 <reified D : Any, T : AbstractTerrainSelector<D>> data(noinline factory: (D, TerrainSelectorParameters) -> T): Data<D, T> {
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<Any>))
return Factory(value.data.adapter.fromJsonTreeFast(config), value.data.factory as ((Any, TerrainSelectorParameters) -> AbstractTerrainSelector<Any>))
}
}
} else {
for (value in entries) {
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>))
}
}
}