Lua message handlers

This commit is contained in:
DBotThePony 2024-05-03 16:52:00 +07:00
parent 639aafce50
commit 6ed51b6ae9
Signed by: DBot
GPG Key ID: DCC23B5715498507
5 changed files with 72 additions and 82 deletions

View File

@ -1,73 +0,0 @@
package ru.dbotthepony.kstarbound.lua
import com.google.gson.JsonArray
import com.google.gson.JsonElement
import com.google.gson.JsonNull
import org.apache.logging.log4j.LogManager
import org.classdump.luna.ByteString
import org.classdump.luna.exec.CallPausedException
import org.classdump.luna.runtime.LuaFunction
class LuaMessageHandler(val lua: LuaEnvironment) {
private val handlers = HashMap<String, LuaFunction<*, *, *, *, *>>()
init {
val table = lua.newTable()
table["setHandler"] = luaFunction { name: ByteString, handler: LuaFunction<*, *, *, *, *>? ->
if (handler == null) {
handlers.remove(name.decode())
} else {
handlers[name.decode()] = handler
}
}
lua.globals["message"] = table
}
fun handle(message: String, isLocal: Boolean, parameters: JsonArray): JsonElement {
if (lua.errorState)
return JsonNull.INSTANCE
val handler = handlers[message] ?: return JsonNull.INSTANCE
try {
val unpacked = arrayOfNulls<Any>(parameters.size() + 2)
unpacked[0] = ByteString.of(message)
unpacked[1] = isLocal
for ((i, v) in parameters.withIndex()) {
unpacked[i + 2] = lua.from(v)
}
val result = lua.executor.call(lua, handler, *unpacked)
if (result.isEmpty()) {
return JsonNull.INSTANCE
} else if (result.size == 1) {
return toJsonFromLua(result[0])
} else {
val array = JsonArray()
for (v in result) {
array.add(toJsonFromLua(v))
}
return array
}
} catch (err: CallPausedException) {
lua.markErrored()
LOGGER.error("Message handler for $message tried to yield", err)
return JsonNull.INSTANCE
} catch (err: Throwable) {
lua.markErrored()
LOGGER.error("Message handler for $message errored", err)
return JsonNull.INSTANCE
}
}
companion object {
private val LOGGER = LogManager.getLogger()
}
}

View File

@ -0,0 +1,61 @@
package ru.dbotthepony.kstarbound.lua
import com.google.gson.JsonArray
import com.google.gson.JsonElement
import com.google.gson.JsonNull
import org.apache.logging.log4j.LogManager
import org.classdump.luna.ByteString
import org.classdump.luna.exec.CallPausedException
import org.classdump.luna.runtime.LuaFunction
import ru.dbotthepony.kommons.util.Either
import ru.dbotthepony.kstarbound.util.ActionPacer
import ru.dbotthepony.kstarbound.util.sbIntern
import ru.dbotthepony.kstarbound.world.World
class LuaMessageHandlerComponent(val lua: LuaEnvironment, val nameProvider: () -> String) {
private val handlers = HashMap<String, LuaFunction<*, *, *, *, *>>()
init {
val table = lua.newTable()
lua.globals["message"] = table
table["setHandler"] = luaFunction { message: ByteString, handler: LuaFunction<*, *, *, *, *>? ->
if (handler == null) {
handlers.remove(message.decode())
} else {
handlers[message.decode().sbIntern()] = handler
}
}
}
private val logPacer = ActionPacer(1, 5)
fun handle(message: String, isLocal: Boolean, arguments: JsonArray): JsonElement {
val handler = handlers[message] ?: throw World.MessageCallException("No registered handler for $message")
try {
val unpack = arguments.map { lua.from(it) }.toTypedArray()
val result = lua.executor.call(lua, handler, isLocal, *unpack)
if (result.isEmpty()) {
return JsonNull.INSTANCE
} else {
return toJsonFromLua(result[0])
}
} catch (err: CallPausedException) {
if (logPacer.consumeAndReturnDeadline() <= 0L)
LOGGER.error("${nameProvider.invoke()}: '$message' handler attempted to yield across C boundary", err)
throw World.MessageCallException("$message handler attempted to yield across C boundary")
} catch (err: Throwable) {
if (logPacer.consumeAndReturnDeadline() <= 0L)
LOGGER.error("${nameProvider.invoke()}: Exception while handling message '$message'", err)
throw err
}
}
companion object {
private val LOGGER = LogManager.getLogger()
}
}

View File

@ -16,10 +16,6 @@ fun String.sbIntern(): String {
return Starbound.STRINGS.intern(this) return Starbound.STRINGS.intern(this)
} }
fun String.sbIntern2(): String {
return Starbound.STRINGS.intern(this.intern())
}
val JsonElement.asStringOrNull: String? val JsonElement.asStringOrNull: String?
get() = if (isJsonNull) null else asString get() = if (isJsonNull) null else asString

View File

@ -644,7 +644,7 @@ class Animator() {
fun setPartTag(partName: String, tagKey: String, tagValue: String) { fun setPartTag(partName: String, tagKey: String, tagValue: String) {
partTags.computeIfAbsent(partName) { partTags.computeIfAbsent(partName) {
LOGGER.warn("Creating part tags for $it after initialization, this can cause client-server desyncs") LOGGER.warn("Creating animated part tags for '$it' after initialization, this can cause client-server desyncs")
NetworkedMap(InternedStringCodec, InternedStringCodec) NetworkedMap(InternedStringCodec, InternedStringCodec)
}.put(tagKey, tagValue) }.put(tagKey, tagValue)
} }

View File

@ -30,11 +30,9 @@ import ru.dbotthepony.kommons.io.StreamCodec
import ru.dbotthepony.kommons.io.map import ru.dbotthepony.kommons.io.map
import ru.dbotthepony.kommons.io.writeBinaryString import ru.dbotthepony.kommons.io.writeBinaryString
import ru.dbotthepony.kstarbound.math.AABB import ru.dbotthepony.kstarbound.math.AABB
import ru.dbotthepony.kommons.util.KOptional
import ru.dbotthepony.kommons.util.getValue import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.kommons.util.setValue import ru.dbotthepony.kommons.util.setValue
import ru.dbotthepony.kstarbound.Globals import ru.dbotthepony.kstarbound.Globals
import ru.dbotthepony.kstarbound.defs.DamageData
import ru.dbotthepony.kstarbound.defs.DamageNotification import ru.dbotthepony.kstarbound.defs.DamageNotification
import ru.dbotthepony.kstarbound.math.vector.Vector2d import ru.dbotthepony.kstarbound.math.vector.Vector2d
import ru.dbotthepony.kstarbound.defs.DamageSource import ru.dbotthepony.kstarbound.defs.DamageSource
@ -66,7 +64,7 @@ import ru.dbotthepony.kstarbound.network.syncher.networkedPointer
import ru.dbotthepony.kstarbound.network.syncher.networkedString import ru.dbotthepony.kstarbound.network.syncher.networkedString
import ru.dbotthepony.kstarbound.json.writeJsonElement import ru.dbotthepony.kstarbound.json.writeJsonElement
import ru.dbotthepony.kstarbound.lua.LuaEnvironment import ru.dbotthepony.kstarbound.lua.LuaEnvironment
import ru.dbotthepony.kstarbound.lua.LuaMessageHandler import ru.dbotthepony.kstarbound.lua.LuaMessageHandlerComponent
import ru.dbotthepony.kstarbound.lua.LuaUpdateComponent import ru.dbotthepony.kstarbound.lua.LuaUpdateComponent
import ru.dbotthepony.kstarbound.lua.bindings.provideAnimatorBindings import ru.dbotthepony.kstarbound.lua.bindings.provideAnimatorBindings
import ru.dbotthepony.kstarbound.lua.bindings.provideEntityBindings import ru.dbotthepony.kstarbound.lua.bindings.provideEntityBindings
@ -368,7 +366,7 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
val lua = LuaEnvironment() val lua = LuaEnvironment()
val luaUpdate = LuaUpdateComponent(lua) val luaUpdate = LuaUpdateComponent(lua)
val luaMessageHandler = LuaMessageHandler(lua) val luaMessageHandler = LuaMessageHandlerComponent(lua) { toString() }
init { init {
lua.globals["storage"] = lua.newTable() lua.globals["storage"] = lua.newTable()
@ -796,6 +794,10 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
) )
} }
override fun handleMessage(connection: Int, message: String, arguments: JsonArray): JsonElement? {
return luaMessageHandler.handle(message, connectionID == connection, arguments)
}
override fun callScript(fnName: String, vararg arguments: Any?): Array<Any?> { override fun callScript(fnName: String, vararg arguments: Any?): Array<Any?> {
return lua.invokeGlobal(fnName, *arguments) return lua.invokeGlobal(fnName, *arguments)
} }
@ -804,6 +806,10 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
return lua.eval(code) return lua.eval(code)
} }
init {
isInteractive = !interactAction.isJsonNull
}
companion object { companion object {
private val LOGGER = LogManager.getLogger() private val LOGGER = LogManager.getLogger()
private val lightColorPath = JsonPath("lightColor") private val lightColorPath = JsonPath("lightColor")