mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-16 18:55:03 -04:00
added setting to allow logging of tracebacks for exceptions that occurred in Lua callbacks (disabled per default to avoid spamming the log with InvalidArgumentExceptions and the like); moved argument parsing and result pushing to extended lua state and added capability to push maps as tables, meaning callbacks can now return maps, too
This commit is contained in:
parent
9478907a58
commit
3b59b53d5c
@ -43,6 +43,7 @@ class Settings(config: Config) {
|
|||||||
val maxUsers = config.getInt("computer.maxUsers") max 0
|
val maxUsers = config.getInt("computer.maxUsers") max 0
|
||||||
val maxUsernameLength = config.getInt("computer.maxUsernameLength") max 0
|
val maxUsernameLength = config.getInt("computer.maxUsernameLength") max 0
|
||||||
val allowBytecode = config.getBoolean("computer.allowBytecode")
|
val allowBytecode = config.getBoolean("computer.allowBytecode")
|
||||||
|
val logLuaCallbackErrors = config.getBoolean("computer.logCallbackErrors")
|
||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
// robot
|
// robot
|
||||||
|
@ -20,8 +20,6 @@ import scala.Array.canBuildFrom
|
|||||||
import scala.Some
|
import scala.Some
|
||||||
import scala.collection.convert.WrapAsScala._
|
import scala.collection.convert.WrapAsScala._
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
import scala.math.ScalaNumber
|
|
||||||
import scala.runtime.BoxedUnit
|
|
||||||
|
|
||||||
class Computer(val owner: tileentity.Computer) extends ManagedComponent with Context with Runnable {
|
class Computer(val owner: tileentity.Computer) extends ManagedComponent with Context with Runnable {
|
||||||
val node = api.Network.newNode(this, Visibility.Network).
|
val node = api.Network.newNode(this, Visibility.Network).
|
||||||
@ -665,57 +663,6 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
|
|||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
private def init(): Boolean = {
|
private def init(): Boolean = {
|
||||||
// Utility functions for varargs callbacks.
|
|
||||||
def parseArgument(lua: LuaState, index: Int): AnyRef = lua.`type`(index) match {
|
|
||||||
case LuaType.BOOLEAN => Boolean.box(lua.toBoolean(index))
|
|
||||||
case LuaType.NUMBER => Double.box(lua.toNumber(index))
|
|
||||||
case LuaType.STRING => lua.toByteArray(index)
|
|
||||||
case LuaType.TABLE => lua.toJavaObject(index, classOf[java.util.Map[_, _]])
|
|
||||||
case _ => Unit
|
|
||||||
}
|
|
||||||
|
|
||||||
def parseArguments(lua: LuaState, start: Int) =
|
|
||||||
for (index <- start to lua.getTop) yield parseArgument(lua, index)
|
|
||||||
|
|
||||||
def pushList(value: Iterator[(Any, Int)]) {
|
|
||||||
lua.newTable()
|
|
||||||
var count = 0
|
|
||||||
value.foreach {
|
|
||||||
case (x, index) => x match {
|
|
||||||
case (entry: ScalaNumber) =>
|
|
||||||
pushResult(lua, entry.underlying())
|
|
||||||
case (entry) =>
|
|
||||||
pushResult(lua, entry.asInstanceOf[AnyRef])
|
|
||||||
}
|
|
||||||
lua.rawSet(-2, index + 1)
|
|
||||||
count = count + 1
|
|
||||||
}
|
|
||||||
lua.pushString("n")
|
|
||||||
lua.pushInteger(count)
|
|
||||||
lua.rawSet(-3)
|
|
||||||
}
|
|
||||||
|
|
||||||
def pushResult(lua: LuaState, value: AnyRef): Unit = value match {
|
|
||||||
case null | Unit | _: BoxedUnit => lua.pushNil()
|
|
||||||
case value: java.lang.Boolean => lua.pushBoolean(value.booleanValue)
|
|
||||||
case value: java.lang.Byte => lua.pushNumber(value.byteValue)
|
|
||||||
case value: java.lang.Character => lua.pushString(String.valueOf(value))
|
|
||||||
case value: java.lang.Short => lua.pushNumber(value.shortValue)
|
|
||||||
case value: java.lang.Integer => lua.pushNumber(value.intValue)
|
|
||||||
case value: java.lang.Long => lua.pushNumber(value.longValue)
|
|
||||||
case value: java.lang.Float => lua.pushNumber(value.floatValue)
|
|
||||||
case value: java.lang.Double => lua.pushNumber(value.doubleValue)
|
|
||||||
case value: java.lang.String => lua.pushString(value)
|
|
||||||
case value: Array[Byte] => lua.pushByteArray(value)
|
|
||||||
case value: Array[_] => pushList(value.zipWithIndex.iterator)
|
|
||||||
case value: Product => pushList(value.productIterator.zipWithIndex)
|
|
||||||
case value: Seq[_] => pushList(value.zipWithIndex.iterator)
|
|
||||||
// TODO maps?
|
|
||||||
case _ =>
|
|
||||||
OpenComputers.log.warning("A component callback tried to return an unsupported value of type " + value.getClass.getName + ".")
|
|
||||||
lua.pushNil()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset error state.
|
// Reset error state.
|
||||||
message = None
|
message = None
|
||||||
|
|
||||||
@ -862,7 +809,7 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
|
|||||||
lua.setField(-2, "totalMemory")
|
lua.setField(-2, "totalMemory")
|
||||||
|
|
||||||
lua.pushScalaFunction(lua => {
|
lua.pushScalaFunction(lua => {
|
||||||
lua.pushBoolean(signal(lua.checkString(1), parseArguments(lua, 2): _*))
|
lua.pushBoolean(signal(lua.checkString(1), lua.toSimpleJavaObjects(2): _*))
|
||||||
1
|
1
|
||||||
})
|
})
|
||||||
lua.setField(-2, "pushSignal")
|
lua.setField(-2, "pushSignal")
|
||||||
@ -1029,7 +976,7 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
|
|||||||
lua.pushScalaFunction(lua => {
|
lua.pushScalaFunction(lua => {
|
||||||
val address = lua.checkString(1)
|
val address = lua.checkString(1)
|
||||||
val method = lua.checkString(2)
|
val method = lua.checkString(2)
|
||||||
val args = parseArguments(lua, 3)
|
val args = lua.toSimpleJavaObjects(3)
|
||||||
try {
|
try {
|
||||||
(Option(node.network.node(address)) match {
|
(Option(node.network.node(address)) match {
|
||||||
case Some(component: server.network.Component) if component.canBeSeenFrom(node) || component == node =>
|
case Some(component: server.network.Component) if component.canBeSeenFrom(node) || component == node =>
|
||||||
@ -1048,57 +995,68 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
|
|||||||
}) match {
|
}) match {
|
||||||
case results: Array[_] =>
|
case results: Array[_] =>
|
||||||
lua.pushBoolean(true)
|
lua.pushBoolean(true)
|
||||||
results.foreach(pushResult(lua, _))
|
results.foreach(result => lua.pushValue(result))
|
||||||
1 + results.length
|
1 + results.length
|
||||||
case _ =>
|
case _ =>
|
||||||
lua.pushBoolean(true)
|
lua.pushBoolean(true)
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
} catch {
|
}
|
||||||
case _: LimitReachedException =>
|
catch {
|
||||||
0
|
|
||||||
case e: IllegalArgumentException if e.getMessage != null =>
|
|
||||||
lua.pushBoolean(false)
|
|
||||||
lua.pushString(e.getMessage)
|
|
||||||
2
|
|
||||||
case e: Throwable if e.getMessage != null =>
|
|
||||||
lua.pushBoolean(true)
|
|
||||||
lua.pushNil()
|
|
||||||
lua.pushString(e.getMessage)
|
|
||||||
3
|
|
||||||
case _: ArrayIndexOutOfBoundsException =>
|
|
||||||
lua.pushBoolean(false)
|
|
||||||
lua.pushString("index out of bounds")
|
|
||||||
2
|
|
||||||
case _: IllegalArgumentException =>
|
|
||||||
lua.pushBoolean(false)
|
|
||||||
lua.pushString("bad argument")
|
|
||||||
2
|
|
||||||
case _: NoSuchMethodException =>
|
|
||||||
lua.pushBoolean(false)
|
|
||||||
lua.pushString("no such method")
|
|
||||||
2
|
|
||||||
case _: FileNotFoundException =>
|
|
||||||
lua.pushBoolean(true)
|
|
||||||
lua.pushNil()
|
|
||||||
lua.pushString("file not found")
|
|
||||||
3
|
|
||||||
case _: SecurityException =>
|
|
||||||
lua.pushBoolean(true)
|
|
||||||
lua.pushNil()
|
|
||||||
lua.pushString("access denied")
|
|
||||||
3
|
|
||||||
case _: IOException =>
|
|
||||||
lua.pushBoolean(true)
|
|
||||||
lua.pushNil()
|
|
||||||
lua.pushString("i/o error")
|
|
||||||
3
|
|
||||||
case e: Throwable =>
|
case e: Throwable =>
|
||||||
OpenComputers.log.log(Level.WARNING, "Unexpected error in Lua callback.", e)
|
if (Settings.get.logLuaCallbackErrors) {
|
||||||
lua.pushBoolean(true)
|
OpenComputers.log.log(Level.WARNING, "Exception in Lua callback.", e)
|
||||||
lua.pushNil()
|
}
|
||||||
lua.pushString("unknown error")
|
e match {
|
||||||
3
|
case _: LimitReachedException =>
|
||||||
|
0
|
||||||
|
case e: IllegalArgumentException if e.getMessage != null =>
|
||||||
|
lua.pushBoolean(false)
|
||||||
|
lua.pushString(e.getMessage)
|
||||||
|
2
|
||||||
|
case e: Throwable if e.getMessage != null =>
|
||||||
|
lua.pushBoolean(true)
|
||||||
|
lua.pushNil()
|
||||||
|
lua.pushString(e.getMessage)
|
||||||
|
if (true) {
|
||||||
|
lua.pushString(e.getStackTraceString)
|
||||||
|
4
|
||||||
|
}
|
||||||
|
else 3
|
||||||
|
case _: IndexOutOfBoundsException =>
|
||||||
|
lua.pushBoolean(false)
|
||||||
|
lua.pushString("index out of bounds")
|
||||||
|
2
|
||||||
|
case _: IllegalArgumentException =>
|
||||||
|
lua.pushBoolean(false)
|
||||||
|
lua.pushString("bad argument")
|
||||||
|
2
|
||||||
|
case _: NoSuchMethodException =>
|
||||||
|
lua.pushBoolean(false)
|
||||||
|
lua.pushString("no such method")
|
||||||
|
2
|
||||||
|
case _: FileNotFoundException =>
|
||||||
|
lua.pushBoolean(true)
|
||||||
|
lua.pushNil()
|
||||||
|
lua.pushString("file not found")
|
||||||
|
3
|
||||||
|
case _: SecurityException =>
|
||||||
|
lua.pushBoolean(true)
|
||||||
|
lua.pushNil()
|
||||||
|
lua.pushString("access denied")
|
||||||
|
3
|
||||||
|
case _: IOException =>
|
||||||
|
lua.pushBoolean(true)
|
||||||
|
lua.pushNil()
|
||||||
|
lua.pushString("i/o error")
|
||||||
|
3
|
||||||
|
case e: Throwable =>
|
||||||
|
OpenComputers.log.log(Level.WARNING, "Unexpected error in Lua callback.", e)
|
||||||
|
lua.pushBoolean(true)
|
||||||
|
lua.pushNil()
|
||||||
|
lua.pushString("unknown error")
|
||||||
|
3
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
lua.setField(-2, "invoke")
|
lua.setField(-2, "invoke")
|
||||||
@ -1293,19 +1251,7 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
|
|||||||
else (signals.synchronized(if (signals.isEmpty) None else Some(signals.dequeue())) match {
|
else (signals.synchronized(if (signals.isEmpty) None else Some(signals.dequeue())) match {
|
||||||
case Some(signal) =>
|
case Some(signal) =>
|
||||||
lua.pushString(signal.name)
|
lua.pushString(signal.name)
|
||||||
signal.args.foreach {
|
signal.args.foreach(arg => lua.pushValue(arg))
|
||||||
case Unit => lua.pushNil()
|
|
||||||
case arg: Boolean => lua.pushBoolean(arg)
|
|
||||||
case arg: Double => lua.pushNumber(arg)
|
|
||||||
case arg: String => lua.pushString(arg)
|
|
||||||
case arg: Array[Byte] => lua.pushByteArray(arg)
|
|
||||||
case arg: Map[String, String] =>
|
|
||||||
lua.newTable(0, arg.size)
|
|
||||||
for ((key, value) <- arg if key != null && value != null) {
|
|
||||||
lua.pushString(value)
|
|
||||||
lua.setField(-2, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lua.resume(1, 1 + signal.args.length)
|
lua.resume(1, 1 + signal.args.length)
|
||||||
case _ =>
|
case _ =>
|
||||||
lua.resume(1, 0)
|
lua.resume(1, 0)
|
||||||
|
@ -3,7 +3,6 @@ package li.cil.oc.server.network
|
|||||||
import cpw.mods.fml.common.FMLCommonHandler
|
import cpw.mods.fml.common.FMLCommonHandler
|
||||||
import cpw.mods.fml.relauncher.Side
|
import cpw.mods.fml.relauncher.Side
|
||||||
import java.lang.reflect.{Method, InvocationTargetException}
|
import java.lang.reflect.{Method, InvocationTargetException}
|
||||||
import java.util
|
|
||||||
import li.cil.oc.api
|
import li.cil.oc.api
|
||||||
import li.cil.oc.api.network._
|
import li.cil.oc.api.network._
|
||||||
import li.cil.oc.server.component
|
import li.cil.oc.server.component
|
||||||
@ -249,7 +248,7 @@ object Component {
|
|||||||
|
|
||||||
def isTable(index: Int) =
|
def isTable(index: Int) =
|
||||||
index >= 0 && index < count && (args(index) match {
|
index >= 0 && index < count && (args(index) match {
|
||||||
case value: util.Map[_, _] => true
|
case value: java.util.Map[_, _] => true
|
||||||
case value: Map[_, _] => true
|
case value: Map[_, _] => true
|
||||||
case value: mutable.Map[_, _] => true
|
case value: mutable.Map[_, _] => true
|
||||||
case _ => false
|
case _ => false
|
||||||
@ -272,7 +271,7 @@ object Component {
|
|||||||
case _: java.lang.Double => "double"
|
case _: java.lang.Double => "double"
|
||||||
case _: java.lang.String => "string"
|
case _: java.lang.String => "string"
|
||||||
case _: Array[Byte] => "string"
|
case _: Array[Byte] => "string"
|
||||||
case value: util.Map[_, _] => "table"
|
case value: java.util.Map[_, _] => "table"
|
||||||
case value: Map[_, _] => "table"
|
case value: Map[_, _] => "table"
|
||||||
case value: mutable.Map[_, _] => "table"
|
case value: mutable.Map[_, _] => "table"
|
||||||
case _ => value.getClass.getSimpleName
|
case _ => value.getClass.getSimpleName
|
||||||
|
@ -1,16 +1,87 @@
|
|||||||
package li.cil.oc.util
|
package li.cil.oc.util
|
||||||
|
|
||||||
import com.naef.jnlua.{JavaFunction, LuaState}
|
import com.naef.jnlua.{LuaType, JavaFunction, LuaState}
|
||||||
|
import li.cil.oc.OpenComputers
|
||||||
|
import scala.collection.convert.WrapAsScala._
|
||||||
|
import scala.collection.mutable
|
||||||
import scala.language.implicitConversions
|
import scala.language.implicitConversions
|
||||||
|
import scala.math.ScalaNumber
|
||||||
|
import scala.runtime.BoxedUnit
|
||||||
|
|
||||||
object ExtendedLuaState {
|
object ExtendedLuaState {
|
||||||
|
|
||||||
implicit def extendLuaState(state: LuaState) = new ExtendedLuaState(state)
|
implicit def extendLuaState(state: LuaState) = new ExtendedLuaState(state)
|
||||||
|
|
||||||
class ExtendedLuaState(val state: LuaState) {
|
class ExtendedLuaState(val lua: LuaState) {
|
||||||
def pushScalaFunction(f: (LuaState) => Int) = state.pushJavaFunction(new JavaFunction {
|
def pushScalaFunction(f: (LuaState) => Int) = lua.pushJavaFunction(new JavaFunction {
|
||||||
override def invoke(state: LuaState) = f(state)
|
override def invoke(state: LuaState) = f(state)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
def pushValue(value: Any) {
|
||||||
|
(value match {
|
||||||
|
case number: ScalaNumber => number.underlying
|
||||||
|
case reference: AnyRef => reference
|
||||||
|
case null => null
|
||||||
|
case primitive => primitive.asInstanceOf[AnyRef]
|
||||||
|
}) match {
|
||||||
|
case null | Unit | _: BoxedUnit => lua.pushNil()
|
||||||
|
case value: java.lang.Boolean => lua.pushBoolean(value.booleanValue)
|
||||||
|
case value: java.lang.Byte => lua.pushNumber(value.byteValue)
|
||||||
|
case value: java.lang.Character => lua.pushString(String.valueOf(value))
|
||||||
|
case value: java.lang.Short => lua.pushNumber(value.shortValue)
|
||||||
|
case value: java.lang.Integer => lua.pushNumber(value.intValue)
|
||||||
|
case value: java.lang.Long => lua.pushNumber(value.longValue)
|
||||||
|
case value: java.lang.Float => lua.pushNumber(value.floatValue)
|
||||||
|
case value: java.lang.Double => lua.pushNumber(value.doubleValue)
|
||||||
|
case value: java.lang.String => lua.pushString(value)
|
||||||
|
case value: Array[Byte] => lua.pushByteArray(value)
|
||||||
|
case value: Array[_] => pushList(value.zipWithIndex.iterator)
|
||||||
|
case value: Product => pushList(value.productIterator.zipWithIndex)
|
||||||
|
case value: Seq[_] => pushList(value.zipWithIndex.iterator)
|
||||||
|
case value: java.util.Map[_, _] => pushTable(value.toMap)
|
||||||
|
case value: Map[_, _] => pushTable(value)
|
||||||
|
case value: mutable.Map[_, _] => pushTable(value.toMap)
|
||||||
|
case _ =>
|
||||||
|
OpenComputers.log.warning("Tried to push an unsupported value of type to Lua: " + value.getClass.getName + ".")
|
||||||
|
lua.pushNil()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def pushList(list: Iterator[(Any, Int)]) {
|
||||||
|
lua.newTable()
|
||||||
|
var count = 0
|
||||||
|
list.foreach {
|
||||||
|
case (value, index) =>
|
||||||
|
pushValue(value)
|
||||||
|
lua.rawSet(-2, index + 1)
|
||||||
|
count = count + 1
|
||||||
|
}
|
||||||
|
lua.pushString("n")
|
||||||
|
lua.pushInteger(count)
|
||||||
|
lua.rawSet(-3)
|
||||||
|
}
|
||||||
|
|
||||||
|
def pushTable(map: Map[_, _]) {
|
||||||
|
lua.newTable(0, map.size)
|
||||||
|
for ((key: AnyRef, value: AnyRef) <- map) {
|
||||||
|
if (key != null && key != Unit && !key.isInstanceOf[BoxedUnit]) {
|
||||||
|
pushValue(key)
|
||||||
|
pushValue(value)
|
||||||
|
lua.setTable(-3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def toSimpleJavaObject(index: Int): AnyRef = lua.`type`(index) match {
|
||||||
|
case LuaType.BOOLEAN => Boolean.box(lua.toBoolean(index))
|
||||||
|
case LuaType.NUMBER => Double.box(lua.toNumber(index))
|
||||||
|
case LuaType.STRING => lua.toByteArray(index)
|
||||||
|
case LuaType.TABLE => lua.toJavaObject(index, classOf[java.util.Map[_, _]])
|
||||||
|
case _ => null
|
||||||
|
}
|
||||||
|
|
||||||
|
def toSimpleJavaObjects(start: Int) =
|
||||||
|
for (index <- start to lua.getTop) yield toSimpleJavaObject(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -150,6 +150,15 @@ opencomputers {
|
|||||||
# list of registered users on the Lua side.
|
# list of registered users on the Lua side.
|
||||||
# See also: `canComputersBeOwned`.
|
# See also: `canComputersBeOwned`.
|
||||||
maxUsernameLength: 32
|
maxUsernameLength: 32
|
||||||
|
|
||||||
|
# This setting is meant for debugging errors that occur in Lua callbacks.
|
||||||
|
# Per default, if an error occurs and it has a message set, only the
|
||||||
|
# message is pushed back to Lua, and that's it. If you encounter weird
|
||||||
|
# errors or are developing an addon you'll want the stacktrace for those
|
||||||
|
# errors. Enabling this setting will log them to the game log. This is
|
||||||
|
# disabled per default to avoid spamming the log with inconsequentual
|
||||||
|
# exceptions such as IllegalArgumentExceptions and the like.
|
||||||
|
logCallbackErrors: false
|
||||||
}
|
}
|
||||||
|
|
||||||
# Robot related settings, what they may do and general balancing.
|
# Robot related settings, what they may do and general balancing.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user