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] ?: return null try { val unpack = arguments.map { lua.from(it) }.toTypedArray() val result = lua.executor.call(lua, handler, isLocal, *unpack) if (result.isEmpty()) { return null } 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() } }