diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/lua/LuaMessageHandler.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/lua/LuaMessageHandler.kt deleted file mode 100644 index 112aa2c8..00000000 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/lua/LuaMessageHandler.kt +++ /dev/null @@ -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>() - - 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(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() - } -} \ No newline at end of file diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/lua/LuaMessageHandlerComponent.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/lua/LuaMessageHandlerComponent.kt new file mode 100644 index 00000000..ad315fde --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/lua/LuaMessageHandlerComponent.kt @@ -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>() + + 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() + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/util/Utils.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/util/Utils.kt index 8c5e4ea2..6758050b 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/util/Utils.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/util/Utils.kt @@ -16,10 +16,6 @@ fun String.sbIntern(): String { return Starbound.STRINGS.intern(this) } -fun String.sbIntern2(): String { - return Starbound.STRINGS.intern(this.intern()) -} - val JsonElement.asStringOrNull: String? get() = if (isJsonNull) null else asString diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/Animator.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/Animator.kt index 02934275..a7cf64c8 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/Animator.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/Animator.kt @@ -644,7 +644,7 @@ class Animator() { fun setPartTag(partName: String, tagKey: String, tagValue: String) { 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) }.put(tagKey, tagValue) } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/tile/WorldObject.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/tile/WorldObject.kt index 83196634..c97779e6 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/tile/WorldObject.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/tile/WorldObject.kt @@ -30,11 +30,9 @@ import ru.dbotthepony.kommons.io.StreamCodec import ru.dbotthepony.kommons.io.map import ru.dbotthepony.kommons.io.writeBinaryString import ru.dbotthepony.kstarbound.math.AABB -import ru.dbotthepony.kommons.util.KOptional import ru.dbotthepony.kommons.util.getValue import ru.dbotthepony.kommons.util.setValue import ru.dbotthepony.kstarbound.Globals -import ru.dbotthepony.kstarbound.defs.DamageData import ru.dbotthepony.kstarbound.defs.DamageNotification import ru.dbotthepony.kstarbound.math.vector.Vector2d 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.json.writeJsonElement 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.bindings.provideAnimatorBindings import ru.dbotthepony.kstarbound.lua.bindings.provideEntityBindings @@ -368,7 +366,7 @@ open class WorldObject(val config: Registry.Entry) : TileEntit val lua = LuaEnvironment() val luaUpdate = LuaUpdateComponent(lua) - val luaMessageHandler = LuaMessageHandler(lua) + val luaMessageHandler = LuaMessageHandlerComponent(lua) { toString() } init { lua.globals["storage"] = lua.newTable() @@ -796,6 +794,10 @@ open class WorldObject(val config: Registry.Entry) : 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 { return lua.invokeGlobal(fnName, *arguments) } @@ -804,6 +806,10 @@ open class WorldObject(val config: Registry.Entry) : TileEntit return lua.eval(code) } + init { + isInteractive = !interactAction.isJsonNull + } + companion object { private val LOGGER = LogManager.getLogger() private val lightColorPath = JsonPath("lightColor")