This commit is contained in:
Florian Nücke 2014-01-18 16:54:12 +01:00
parent 14f7037e46
commit 1335536c5d
7 changed files with 960 additions and 916 deletions

View File

@ -6,7 +6,7 @@ import li.cil.oc.api.network._
import li.cil.oc.common.tileentity import li.cil.oc.common.tileentity
import li.cil.oc.server import li.cil.oc.server
import li.cil.oc.server.PacketSender import li.cil.oc.server.PacketSender
import li.cil.oc.server.component.machine.{NativeLuaArchitecture, ExecutionResult, LuaArchitecture} import li.cil.oc.server.component.machine.{NativeLuaArchitecture, ExecutionResult}
import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.ExtendedNBT._
import li.cil.oc.util.ThreadPoolFactory import li.cil.oc.util.ThreadPoolFactory
import li.cil.oc.{OpenComputers, Settings} import li.cil.oc.{OpenComputers, Settings}

View File

@ -1,527 +0,0 @@
package li.cil.oc.server.component.machine
import com.naef.jnlua
import java.io.{IOException, FileNotFoundException}
import java.util.logging.Level
import li.cil.oc.server.component.Machine
import li.cil.oc.util.ExtendedLuaState.extendLuaState
import li.cil.oc.util.{LuaState, GameTimeFormatter, LuaStateFactory}
import li.cil.oc.{OpenComputers, server, Settings}
import scala.Some
import scala.collection.convert.WrapAsScala._
abstract class LuaArchitecture(val machine: Machine) extends Architecture {
protected var lua: LuaState
protected var kernelMemory = 0
protected val ramScale = if (LuaStateFactory.is64Bit) Settings.get.ramScaleFor64Bit else 1.0
// ----------------------------------------------------------------------- //
protected def node = machine.node
protected def state = machine.state
protected def components = machine.components
// ----------------------------------------------------------------------- //
def isInitialized = kernelMemory > 0
// ----------------------------------------------------------------------- //
def runSynchronized() {
// These three asserts are all guaranteed by run().
assert(lua.getTop == 2)
assert(lua.isThread(1))
assert(lua.isFunction(2))
try {
// Synchronized call protocol requires the called function to return
// a table, which holds the results of the call, to be passed back
// to the coroutine.yield() that triggered the call.
lua.call(0, 1)
lua.checkType(2, jnlua.LuaType.TABLE)
}
catch {
case _: jnlua.LuaMemoryAllocationException =>
// This can happen if we run out of memory while converting a Java
// exception to a string (which we have to do to avoid keeping
// userdata on the stack, which cannot be persisted).
throw new java.lang.OutOfMemoryError("not enough memory")
}
}
def runThreaded(enterState: Machine.State.Value): ExecutionResult = {
try {
// The kernel thread will always be at stack index one.
assert(lua.isThread(1))
if (Settings.get.activeGC) {
// Help out the GC a little. The emergency GC has a few limitations
// that will make it free less memory than doing a full step manually.
lua.gc(jnlua.LuaState.GcAction.COLLECT, 0)
}
// Resume the Lua state and remember the number of results we get.
val results = enterState match {
case Machine.State.SynchronizedReturn =>
// If we were doing a synchronized call, continue where we left off.
assert(lua.getTop == 2)
assert(lua.isTable(2))
lua.resume(1, 1)
case Machine.State.Yielded =>
if (kernelMemory == 0) {
// We're doing the initialization run.
if (lua.resume(1, 0) > 0) {
// We expect to get nothing here, if we do we had an error.
0
}
else {
// Run the garbage collector to get rid of stuff left behind after
// the initialization phase to get a good estimate of the base
// memory usage the kernel has (including libraries). We remember
// that size to grant user-space programs a fixed base amount of
// memory, regardless of the memory need of the underlying system
// (which may change across releases).
lua.gc(jnlua.LuaState.GcAction.COLLECT, 0)
kernelMemory = math.max(lua.getTotalMemory - lua.getFreeMemory, 1)
recomputeMemory()
// Fake zero sleep to avoid stopping if there are no signals.
lua.pushInteger(0)
1
}
}
else machine.popSignal() match {
case Some(signal) =>
lua.pushString(signal.name)
signal.args.foreach(arg => lua.pushValue(arg))
lua.resume(1, 1 + signal.args.length)
case _ =>
lua.resume(1, 0)
}
case s => throw new AssertionError("Running computer from invalid state " + s.toString)
}
// Check if the kernel is still alive.
if (lua.status(1) == jnlua.LuaState.YIELD) {
// If we get one function it must be a wrapper for a synchronized
// call. The protocol is that a closure is pushed that is then called
// from the main server thread, and returns a table, which is in turn
// passed to the originating coroutine.yield().
if (results == 1 && lua.isFunction(2)) {
new ExecutionResult.SynchronizedCall()
}
// Check if we are shutting down, and if so if we're rebooting. This
// is signalled by boolean values, where `false` means shut down,
// `true` means reboot (i.e shutdown then start again).
else if (results == 1 && lua.isBoolean(2)) {
new ExecutionResult.Shutdown(lua.toBoolean(2))
}
else {
// If we have a single number, that's how long we may wait before
// resuming the state again. Note that the sleep may be interrupted
// early if a signal arrives in the meantime. If we have something
// else we just process the next signal or wait for one.
val ticks = if (results == 1 && lua.isNumber(2)) (lua.toNumber(2) * 20).toInt else Int.MaxValue
lua.pop(results)
new ExecutionResult.Sleep(ticks)
}
}
// The kernel thread returned. If it threw we'd be in the catch below.
else {
assert(lua.isThread(1))
// We're expecting the result of a pcall, if anything, so boolean + (result | string).
if (!lua.isBoolean(2) || !(lua.isString(3) || lua.isNil(3))) {
OpenComputers.log.warning("Kernel returned unexpected results.")
}
// The pcall *should* never return normally... but check for it nonetheless.
if (lua.toBoolean(2)) {
OpenComputers.log.warning("Kernel stopped unexpectedly.")
new ExecutionResult.Shutdown(false)
}
else {
lua.setTotalMemory(Int.MaxValue)
val error = lua.toString(3)
if (error != null) new ExecutionResult.Error(error)
else new ExecutionResult.Error("unknown error")
}
}
}
catch {
case e: jnlua.LuaRuntimeException =>
OpenComputers.log.warning("Kernel crashed. This is a bug!\n" + e.toString + "\tat " + e.getLuaStackTrace.mkString("\n\tat "))
new ExecutionResult.Error("kernel panic: this is a bug, check your log file and report it")
case e: jnlua.LuaGcMetamethodException =>
if (e.getMessage != null) new ExecutionResult.Error("kernel panic:\n" + e.getMessage)
else new ExecutionResult.Error("kernel panic:\nerror in garbage collection metamethod")
case e: jnlua.LuaMemoryAllocationException =>
new ExecutionResult.Error("not enough memory")
case e: java.lang.Error if e.getMessage == "not enough memory" =>
new ExecutionResult.Error("not enough memory")
case e: Throwable =>
OpenComputers.log.log(Level.WARNING, "Unexpected error in kernel. This is a bug!\n", e)
new ExecutionResult.Error("kernel panic: this is a bug, check your log file and report it")
}
}
// ----------------------------------------------------------------------- //
def init(): Boolean = {
if (!createState()) return false
// Push a couple of functions that override original Lua API functions or
// that add new functionality to it.
lua.getGlobal("os")
// Custom os.clock() implementation returning the time the computer has
// been actively running, instead of the native library...
lua.pushScalaFunction(lua => {
lua.pushNumber((machine.cpuTime + (System.nanoTime() - machine.cpuStart)) * 10e-10)
1
})
lua.setField(-2, "clock")
// Date formatting function.
lua.pushScalaFunction(lua => {
val format =
if (lua.getTop > 0 && lua.isString(1)) lua.toString(1)
else "%d/%m/%y %H:%M:%S"
val time =
if (lua.getTop > 1 && lua.isNumber(2)) lua.toNumber(2) * 1000 / 60 / 60
else machine.worldTime + 6000
val dt = GameTimeFormatter.parse(time)
def fmt(format: String) {
if (format == "*t") {
lua.newTable(0, 8)
lua.pushInteger(dt.year)
lua.setField(-2, "year")
lua.pushInteger(dt.month)
lua.setField(-2, "month")
lua.pushInteger(dt.day)
lua.setField(-2, "day")
lua.pushInteger(dt.hour)
lua.setField(-2, "hour")
lua.pushInteger(dt.minute)
lua.setField(-2, "min")
lua.pushInteger(dt.second)
lua.setField(-2, "sec")
lua.pushInteger(dt.weekDay)
lua.setField(-2, "wday")
lua.pushInteger(dt.yearDay)
lua.setField(-2, "yday")
}
else {
lua.pushString(GameTimeFormatter.format(format, dt))
}
}
// Just ignore the allowed leading '!', Minecraft has no time zones...
if (format.startsWith("!"))
fmt(format.substring(1))
else
fmt(format)
1
})
lua.setField(-2, "date")
// Return ingame time for os.time().
lua.pushScalaFunction(lua => {
// Game time is in ticks, so that each day has 24000 ticks, meaning
// one hour is game time divided by one thousand. Also, Minecraft
// starts days at 6 o'clock, so we add those six hours. Thus:
// timestamp = (time + 6000) * 60[kh] * 60[km] / 1000[s]
lua.pushNumber((machine.worldTime + 6000) * 60 * 60 / 1000)
1
})
lua.setField(-2, "time")
// Pop the os table.
lua.pop(1)
// Computer API, stuff that kinda belongs to os, but we don't want to
// clutter it.
lua.newTable()
// Allow getting the real world time for timeouts.
lua.pushScalaFunction(lua => {
lua.pushNumber(System.currentTimeMillis() / 1000.0)
1
})
lua.setField(-2, "realTime")
// The time the computer has been running, as opposed to the CPU time.
lua.pushScalaFunction(lua => {
// World time is in ticks, and each second has 20 ticks. Since we
// want uptime() to return real seconds, though, we'll divide it
// accordingly.
lua.pushNumber((machine.worldTime - machine.timeStarted) / 20.0)
1
})
lua.setField(-2, "uptime")
// Allow the computer to figure out its own id in the component network.
lua.pushScalaFunction(lua => {
Option(node.address) match {
case None => lua.pushNil()
case Some(address) => lua.pushString(address)
}
1
})
lua.setField(-2, "address")
// Are we a robot? (No this is not a CAPTCHA.)
lua.pushScalaFunction(lua => {
lua.pushBoolean(machine.isRobot)
1
})
lua.setField(-2, "isRobot")
lua.pushScalaFunction(lua => {
// This is *very* unlikely, but still: avoid this getting larger than
// what we report as the total memory.
lua.pushInteger(((lua.getFreeMemory min (lua.getTotalMemory - kernelMemory)) / ramScale).toInt)
1
})
lua.setField(-2, "freeMemory")
// Allow the system to read how much memory it uses and has available.
lua.pushScalaFunction(lua => {
lua.pushInteger(((lua.getTotalMemory - kernelMemory) / ramScale).toInt)
1
})
lua.setField(-2, "totalMemory")
lua.pushScalaFunction(lua => {
lua.pushBoolean(machine.signal(lua.checkString(1), lua.toSimpleJavaObjects(2): _*))
1
})
lua.setField(-2, "pushSignal")
// And its ROM address.
lua.pushScalaFunction(lua => {
machine.rom.foreach(rom => Option(rom.node.address) match {
case None => lua.pushNil()
case Some(address) => lua.pushString(address)
})
1
})
lua.setField(-2, "romAddress")
// And it's /tmp address...
lua.pushScalaFunction(lua => {
machine.tmp.foreach(tmp => Option(tmp.node.address) match {
case None => lua.pushNil()
case Some(address) => lua.pushString(address)
})
1
})
lua.setField(-2, "tmpAddress")
// User management.
lua.pushScalaFunction(lua => {
val users = machine.users
users.foreach(lua.pushString)
users.length
})
lua.setField(-2, "users")
lua.pushScalaFunction(lua => try {
machine.addUser(lua.checkString(1))
lua.pushBoolean(true)
1
} catch {
case e: Throwable =>
lua.pushNil()
lua.pushString(Option(e.getMessage).getOrElse(e.toString))
2
})
lua.setField(-2, "addUser")
lua.pushScalaFunction(lua => {
lua.pushBoolean(machine.removeUser(lua.checkString(1)))
1
})
lua.setField(-2, "removeUser")
lua.pushScalaFunction(lua => {
lua.pushNumber(node.globalBuffer)
1
})
lua.setField(-2, "energy")
lua.pushScalaFunction(lua => {
lua.pushNumber(node.globalBufferSize)
1
})
lua.setField(-2, "maxEnergy")
// Set the computer table.
lua.setGlobal("computer")
// Until we get to ingame screens we log to Java's stdout.
lua.pushScalaFunction(lua => {
println((1 to lua.getTop).map(i => lua.`type`(i) match {
case jnlua.LuaType.NIL => "nil"
case jnlua.LuaType.BOOLEAN => lua.toBoolean(i)
case jnlua.LuaType.NUMBER => lua.toNumber(i)
case jnlua.LuaType.STRING => lua.toString(i)
case jnlua.LuaType.TABLE => "table"
case jnlua.LuaType.FUNCTION => "function"
case jnlua.LuaType.THREAD => "thread"
case jnlua.LuaType.LIGHTUSERDATA | jnlua.LuaType.USERDATA => "userdata"
}).mkString(" "))
0
})
lua.setGlobal("print")
// Whether bytecode may be loaded directly.
lua.pushScalaFunction(lua => {
lua.pushBoolean(Settings.get.allowBytecode)
1
})
lua.setGlobal("allowBytecode")
// How long programs may run without yielding before we stop them.
lua.pushNumber(Settings.get.timeout)
lua.setGlobal("timeout")
// Component interaction stuff.
lua.newTable()
lua.pushScalaFunction(lua => components.synchronized {
val filter = if (lua.isString(1)) Option(lua.toString(1)) else None
lua.newTable(0, components.size)
for ((address, name) <- components) {
if (filter.isEmpty || name.contains(filter.get)) {
lua.pushString(address)
lua.pushString(name)
lua.rawSet(-3)
}
}
1
})
lua.setField(-2, "list")
lua.pushScalaFunction(lua => components.synchronized {
components.get(lua.checkString(1)) match {
case Some(name: String) =>
lua.pushString(name)
1
case _ =>
lua.pushNil()
lua.pushString("no such component")
2
}
})
lua.setField(-2, "type")
lua.pushScalaFunction(lua => {
Option(node.network.node(lua.checkString(1))) match {
case Some(component: server.network.Component) if component.canBeSeenFrom(node) || component == node =>
lua.newTable()
for (method <- component.methods()) {
lua.pushString(method)
lua.pushBoolean(component.isDirect(method))
lua.rawSet(-3)
}
1
case _ =>
lua.pushNil()
lua.pushString("no such component")
2
}
})
lua.setField(-2, "methods")
lua.pushScalaFunction(lua => {
val address = lua.checkString(1)
val method = lua.checkString(2)
val args = lua.toSimpleJavaObjects(3)
try {
machine.invoke(address, method, args) match {
case results: Array[_] =>
lua.pushBoolean(true)
results.foreach(result => lua.pushValue(result))
1 + results.length
case _ =>
lua.pushBoolean(true)
1
}
}
catch {
case e: Throwable =>
if (Settings.get.logLuaCallbackErrors && !e.isInstanceOf[Machine.LimitReachedException]) {
OpenComputers.log.log(Level.WARNING, "Exception in Lua callback.", e)
}
e match {
case _: Machine.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 (Settings.get.logLuaCallbackErrors) {
lua.pushString(e.getStackTraceString.replace("\r\n", "\n"))
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.setGlobal("component")
lua.load(classOf[Machine].getResourceAsStream(Settings.scriptPath + "kernel.lua"), "=kernel")
lua.newThread() // Left as the first value on the stack.
true
}
def close() {
kernelMemory = 0
}
protected def createState(): Boolean
}

View File

@ -2,15 +2,396 @@ package li.cil.oc.server.component.machine
import li.cil.oc.server.component.Machine import li.cil.oc.server.component.Machine
import net.minecraft.nbt.NBTTagCompound import net.minecraft.nbt.NBTTagCompound
import org.luaj.vm2.lib.jse.JsePlatform import org.luaj.vm2.lib.jse.{CoerceJavaToLua, JsePlatform}
import li.cil.oc.server.component.Machine.State
import org.luaj.vm2.{LuaFunction, LuaValue, Globals}
import li.cil.oc.util.{GameTimeFormatter, LuaStateFactory}
import li.cil.oc.{OpenComputers, server, Settings}
import java.util.logging.Level
import java.io.{IOException, FileNotFoundException}
class LuaJLuaArchitecture(machine: Machine) extends LuaArchitecture(machine) { class LuaJLuaArchitecture(machine: Machine) extends Architecture {
protected def createState() = { private var lua: Globals = _
// ----------------------------------------------------------------------- //
private def node = machine.node
private def state = machine.state
private def components = machine.components
// ----------------------------------------------------------------------- //
def isInitialized = ???
def recomputeMemory() {}
// ----------------------------------------------------------------------- //
def runSynchronized() {
try {
lua.set(0, lua.get(0).call())
}
catch {
case _: OutOfMemoryError =>
// This can happen if we run out of memory while converting a Java
// exception to a string (which we have to do to avoid keeping
// userdata on the stack, which cannot be persisted).
throw new java.lang.OutOfMemoryError("not enough memory")
}
}
def runThreaded(enterState: State.Value) = ???
// ----------------------------------------------------------------------- //
def init() = {
lua = JsePlatform.standardGlobals() lua = JsePlatform.standardGlobals()
CoerceJavaToLua.coerce()
val os = lua.get("os")
os.set("clock", () => (machine.cpuTime + (System.nanoTime() - machine.cpuStart)) * 10e-10)
// Date formatting function.
lua.pushScalaFunction(lua => {
val format =
if (lua.getTop > 0 && lua.isString(1)) lua.toString(1)
else "%d/%m/%y %H:%M:%S"
val time =
if (lua.getTop > 1 && lua.isNumber(2)) lua.toNumber(2) * 1000 / 60 / 60
else machine.worldTime + 6000
val dt = GameTimeFormatter.parse(time)
def fmt(format: String) {
if (format == "*t") {
lua.newTable(0, 8)
lua.pushInteger(dt.year)
lua.setField(-2, "year")
lua.pushInteger(dt.month)
lua.setField(-2, "month")
lua.pushInteger(dt.day)
lua.setField(-2, "day")
lua.pushInteger(dt.hour)
lua.setField(-2, "hour")
lua.pushInteger(dt.minute)
lua.setField(-2, "min")
lua.pushInteger(dt.second)
lua.setField(-2, "sec")
lua.pushInteger(dt.weekDay)
lua.setField(-2, "wday")
lua.pushInteger(dt.yearDay)
lua.setField(-2, "yday")
}
else {
lua.pushString(GameTimeFormatter.format(format, dt))
}
}
// Just ignore the allowed leading '!', Minecraft has no time zones...
if (format.startsWith("!"))
fmt(format.substring(1))
else
fmt(format)
1
})
lua.setField(-2, "date")
// Return ingame time for os.time().
lua.pushScalaFunction(lua => {
// Game time is in ticks, so that each day has 24000 ticks, meaning
// one hour is game time divided by one thousand. Also, Minecraft
// starts days at 6 o'clock, so we add those six hours. Thus:
// timestamp = (time + 6000) * 60[kh] * 60[km] / 1000[s]
lua.pushNumber((machine.worldTime + 6000) * 60 * 60 / 1000)
1
})
lua.setField(-2, "time")
// Pop the os table.
lua.pop(1)
// Computer API, stuff that kinda belongs to os, but we don't want to
// clutter it.
lua.newTable()
// Allow getting the real world time for timeouts.
lua.pushScalaFunction(lua => {
lua.pushNumber(System.currentTimeMillis() / 1000.0)
1
})
lua.setField(-2, "realTime")
// The time the computer has been running, as opposed to the CPU time.
lua.pushScalaFunction(lua => {
// World time is in ticks, and each second has 20 ticks. Since we
// want uptime() to return real seconds, though, we'll divide it
// accordingly.
lua.pushNumber((machine.worldTime - machine.timeStarted) / 20.0)
1
})
lua.setField(-2, "uptime")
// Allow the computer to figure out its own id in the component network.
lua.pushScalaFunction(lua => {
Option(node.address) match {
case None => lua.pushNil()
case Some(address) => lua.pushString(address)
}
1
})
lua.setField(-2, "address")
// Are we a robot? (No this is not a CAPTCHA.)
lua.pushScalaFunction(lua => {
lua.pushBoolean(machine.isRobot)
1
})
lua.setField(-2, "isRobot")
lua.pushScalaFunction(lua => {
// This is *very* unlikely, but still: avoid this getting larger than
// what we report as the total memory.
lua.pushInteger(((lua.getFreeMemory min (lua.getTotalMemory - kernelMemory)) / ramScale).toInt)
1
})
lua.setField(-2, "freeMemory")
// Allow the system to read how much memory it uses and has available.
lua.pushScalaFunction(lua => {
lua.pushInteger(((lua.getTotalMemory - kernelMemory) / ramScale).toInt)
1
})
lua.setField(-2, "totalMemory")
lua.pushScalaFunction(lua => {
lua.pushBoolean(machine.signal(lua.checkString(1), lua.toSimpleJavaObjects(2): _*))
1
})
lua.setField(-2, "pushSignal")
// And its ROM address.
lua.pushScalaFunction(lua => {
machine.rom.foreach(rom => Option(rom.node.address) match {
case None => lua.pushNil()
case Some(address) => lua.pushString(address)
})
1
})
lua.setField(-2, "romAddress")
// And it's /tmp address...
lua.pushScalaFunction(lua => {
machine.tmp.foreach(tmp => Option(tmp.node.address) match {
case None => lua.pushNil()
case Some(address) => lua.pushString(address)
})
1
})
lua.setField(-2, "tmpAddress")
// User management.
lua.pushScalaFunction(lua => {
val users = machine.users
users.foreach(lua.pushString)
users.length
})
lua.setField(-2, "users")
lua.pushScalaFunction(lua => try {
machine.addUser(lua.checkString(1))
lua.pushBoolean(true)
1
} catch {
case e: Throwable =>
lua.pushNil()
lua.pushString(Option(e.getMessage).getOrElse(e.toString))
2
})
lua.setField(-2, "addUser")
lua.pushScalaFunction(lua => {
lua.pushBoolean(machine.removeUser(lua.checkString(1)))
1
})
lua.setField(-2, "removeUser")
lua.pushScalaFunction(lua => {
lua.pushNumber(node.globalBuffer)
1
})
lua.setField(-2, "energy")
lua.pushScalaFunction(lua => {
lua.pushNumber(node.globalBufferSize)
1
})
lua.setField(-2, "maxEnergy")
// Set the computer table.
lua.setGlobal("computer")
// Until we get to ingame screens we log to Java's stdout.
lua.pushScalaFunction(lua => {
println((1 to lua.getTop).map(i => lua.`type`(i) match {
case LuaType.NIL => "nil"
case LuaType.BOOLEAN => lua.toBoolean(i)
case LuaType.NUMBER => lua.toNumber(i)
case LuaType.STRING => lua.toString(i)
case LuaType.TABLE => "table"
case LuaType.FUNCTION => "function"
case LuaType.THREAD => "thread"
case LuaType.LIGHTUSERDATA | LuaType.USERDATA => "userdata"
}).mkString(" "))
0
})
lua.setGlobal("print")
// Whether bytecode may be loaded directly.
lua.pushScalaFunction(lua => {
lua.pushBoolean(Settings.get.allowBytecode)
1
})
lua.setGlobal("allowBytecode")
// How long programs may run without yielding before we stop them.
lua.pushNumber(Settings.get.timeout)
lua.setGlobal("timeout")
// Component interaction stuff.
lua.newTable()
lua.pushScalaFunction(lua => components.synchronized {
val filter = if (lua.isString(1)) Option(lua.toString(1)) else None
lua.newTable(0, components.size)
for ((address, name) <- components) {
if (filter.isEmpty || name.contains(filter.get)) {
lua.pushString(address)
lua.pushString(name)
lua.rawSet(-3)
}
}
1
})
lua.setField(-2, "list")
lua.pushScalaFunction(lua => components.synchronized {
components.get(lua.checkString(1)) match {
case Some(name: String) =>
lua.pushString(name)
1
case _ =>
lua.pushNil()
lua.pushString("no such component")
2
}
})
lua.setField(-2, "type")
lua.pushScalaFunction(lua => {
Option(node.network.node(lua.checkString(1))) match {
case Some(component: server.network.Component) if component.canBeSeenFrom(node) || component == node =>
lua.newTable()
for (method <- component.methods()) {
lua.pushString(method)
lua.pushBoolean(component.isDirect(method))
lua.rawSet(-3)
}
1
case _ =>
lua.pushNil()
lua.pushString("no such component")
2
}
})
lua.setField(-2, "methods")
lua.pushScalaFunction(lua => {
val address = lua.checkString(1)
val method = lua.checkString(2)
val args = lua.toSimpleJavaObjects(3)
try {
machine.invoke(address, method, args) match {
case results: Array[_] =>
lua.pushBoolean(true)
results.foreach(result => lua.pushValue(result))
1 + results.length
case _ =>
lua.pushBoolean(true)
1
}
}
catch {
case e: Throwable =>
if (Settings.get.logLuaCallbackErrors && !e.isInstanceOf[Machine.LimitReachedException]) {
OpenComputers.log.log(Level.WARNING, "Exception in Lua callback.", e)
}
e match {
case _: Machine.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 (Settings.get.logLuaCallbackErrors) {
lua.pushString(e.getStackTraceString.replace("\r\n", "\n"))
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.setGlobal("component")
initPerms()
lua.load(classOf[Machine].getResourceAsStream(Settings.scriptPath + "kernel.lua"), "=kernel", "t")
lua.newThread() // Left as the first value on the stack.
true true
} }
def recomputeMemory() {} def close() = ???
// ----------------------------------------------------------------------- //
def load(nbt: NBTTagCompound) {} def load(nbt: NBTTagCompound) {}

View File

@ -1,19 +1,40 @@
package li.cil.oc.server.component.machine package li.cil.oc.server.component.machine
import com.naef.jnlua._
import java.io.{IOException, FileNotFoundException}
import java.util.logging.Level
import li.cil.oc.server.component.Machine import li.cil.oc.server.component.Machine
import li.cil.oc.util.ExtendedLuaState.extendLuaState
import li.cil.oc.util.{GameTimeFormatter, LuaStateFactory}
import li.cil.oc.{OpenComputers, server, Settings}
import net.minecraft.nbt.NBTTagCompound import net.minecraft.nbt.NBTTagCompound
import li.cil.oc.OpenComputers import scala.Some
import scala.collection.convert.WrapAsScala._
import scala.collection.mutable import scala.collection.mutable
import li.cil.oc.util.LuaStateFactory
import com.naef.jnlua
class NativeLuaArchitecture(machine: Machine) extends LuaArchitecture(machine) { class NativeLuaArchitecture(val machine: Machine) extends Architecture {
override var lua: jnlua.LuaState = _ private var lua: LuaState = null
private var kernelMemory = 0
private val ramScale = if (LuaStateFactory.is64Bit) Settings.get.ramScaleFor64Bit else 1.0
// ----------------------------------------------------------------------- //
private def node = machine.node
private def state = machine.state
private def components = machine.components
// ----------------------------------------------------------------------- //
def isInitialized = kernelMemory > 0
def recomputeMemory() = Option(lua) match { def recomputeMemory() = Option(lua) match {
case Some(l) => case Some(l) =>
l.setTotalMemory(Int.MaxValue) l.setTotalMemory(Int.MaxValue)
l.gc(jnlua.LuaState.GcAction.COLLECT, 0) l.gc(LuaState.GcAction.COLLECT, 0)
if (kernelMemory > 0) { if (kernelMemory > 0) {
l.setTotalMemory(kernelMemory + math.ceil(machine.owner.installedMemory * ramScale).toInt) l.setTotalMemory(kernelMemory + math.ceil(machine.owner.installedMemory * ramScale).toInt)
} }
@ -22,36 +43,512 @@ class NativeLuaArchitecture(machine: Machine) extends LuaArchitecture(machine) {
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
override def init(): Boolean = { def runSynchronized() {
if (super.init()) { // These three asserts are all guaranteed by run().
initPerms() assert(lua.getTop == 2)
true assert(lua.isThread(1))
assert(lua.isFunction(2))
try {
// Synchronized call protocol requires the called function to return
// a table, which holds the results of the call, to be passed back
// to the coroutine.yield() that triggered the call.
lua.call(0, 1)
lua.checkType(2, LuaType.TABLE)
}
catch {
case _: LuaMemoryAllocationException =>
// This can happen if we run out of memory while converting a Java
// exception to a string (which we have to do to avoid keeping
// userdata on the stack, which cannot be persisted).
throw new java.lang.OutOfMemoryError("not enough memory")
} }
else false
} }
override def close() { def runThreaded(enterState: Machine.State.Value): ExecutionResult = {
try {
// The kernel thread will always be at stack index one.
assert(lua.isThread(1))
if (Settings.get.activeGC) {
// Help out the GC a little. The emergency GC has a few limitations
// that will make it free less memory than doing a full step manually.
lua.gc(LuaState.GcAction.COLLECT, 0)
}
// Resume the Lua state and remember the number of results we get.
val results = enterState match {
case Machine.State.SynchronizedReturn =>
// If we were doing a synchronized call, continue where we left off.
assert(lua.getTop == 2)
assert(lua.isTable(2))
lua.resume(1, 1)
case Machine.State.Yielded =>
if (kernelMemory == 0) {
// We're doing the initialization run.
if (lua.resume(1, 0) > 0) {
// We expect to get nothing here, if we do we had an error.
0
}
else {
// Run the garbage collector to get rid of stuff left behind after
// the initialization phase to get a good estimate of the base
// memory usage the kernel has (including libraries). We remember
// that size to grant user-space programs a fixed base amount of
// memory, regardless of the memory need of the underlying system
// (which may change across releases).
lua.gc(LuaState.GcAction.COLLECT, 0)
kernelMemory = math.max(lua.getTotalMemory - lua.getFreeMemory, 1)
recomputeMemory()
// Fake zero sleep to avoid stopping if there are no signals.
lua.pushInteger(0)
1
}
}
else machine.popSignal() match {
case Some(signal) =>
lua.pushString(signal.name)
signal.args.foreach(arg => lua.pushValue(arg))
lua.resume(1, 1 + signal.args.length)
case _ =>
lua.resume(1, 0)
}
case s => throw new AssertionError("Running computer from invalid state " + s.toString)
}
// Check if the kernel is still alive.
if (lua.status(1) == LuaState.YIELD) {
// If we get one function it must be a wrapper for a synchronized
// call. The protocol is that a closure is pushed that is then called
// from the main server thread, and returns a table, which is in turn
// passed to the originating coroutine.yield().
if (results == 1 && lua.isFunction(2)) {
new ExecutionResult.SynchronizedCall()
}
// Check if we are shutting down, and if so if we're rebooting. This
// is signalled by boolean values, where `false` means shut down,
// `true` means reboot (i.e shutdown then start again).
else if (results == 1 && lua.isBoolean(2)) {
new ExecutionResult.Shutdown(lua.toBoolean(2))
}
else {
// If we have a single number, that's how long we may wait before
// resuming the state again. Note that the sleep may be interrupted
// early if a signal arrives in the meantime. If we have something
// else we just process the next signal or wait for one.
val ticks = if (results == 1 && lua.isNumber(2)) (lua.toNumber(2) * 20).toInt else Int.MaxValue
lua.pop(results)
new ExecutionResult.Sleep(ticks)
}
}
// The kernel thread returned. If it threw we'd be in the catch below.
else {
assert(lua.isThread(1))
// We're expecting the result of a pcall, if anything, so boolean + (result | string).
if (!lua.isBoolean(2) || !(lua.isString(3) || lua.isNil(3))) {
OpenComputers.log.warning("Kernel returned unexpected results.")
}
// The pcall *should* never return normally... but check for it nonetheless.
if (lua.toBoolean(2)) {
OpenComputers.log.warning("Kernel stopped unexpectedly.")
new ExecutionResult.Shutdown(false)
}
else {
lua.setTotalMemory(Int.MaxValue)
val error = lua.toString(3)
if (error != null) new ExecutionResult.Error(error)
else new ExecutionResult.Error("unknown error")
}
}
}
catch {
case e: LuaRuntimeException =>
OpenComputers.log.warning("Kernel crashed. This is a bug!\n" + e.toString + "\tat " + e.getLuaStackTrace.mkString("\n\tat "))
new ExecutionResult.Error("kernel panic: this is a bug, check your log file and report it")
case e: LuaGcMetamethodException =>
if (e.getMessage != null) new ExecutionResult.Error("kernel panic:\n" + e.getMessage)
else new ExecutionResult.Error("kernel panic:\nerror in garbage collection metamethod")
case e: LuaMemoryAllocationException =>
new ExecutionResult.Error("not enough memory")
case e: java.lang.Error if e.getMessage == "not enough memory" =>
new ExecutionResult.Error("not enough memory")
case e: Throwable =>
OpenComputers.log.log(Level.WARNING, "Unexpected error in kernel. This is a bug!\n", e)
new ExecutionResult.Error("kernel panic: this is a bug, check your log file and report it")
}
}
// ----------------------------------------------------------------------- //
def init(): Boolean = {
// Creates a new state with all base libraries and the persistence library
// loaded into it. This means the state has much more power than it
// rightfully should have, so we sandbox it a bit in the following.
LuaStateFactory.createState() match {
case None =>
lua = null
machine.message = Some("native libraries not available")
return false
case Some(value) => lua = value
}
// Push a couple of functions that override original Lua API functions or
// that add new functionality to it.
lua.getGlobal("os")
// Custom os.clock() implementation returning the time the computer has
// been actively running, instead of the native library...
lua.pushScalaFunction(lua => {
lua.pushNumber((machine.cpuTime + (System.nanoTime() - machine.cpuStart)) * 10e-10)
1
})
lua.setField(-2, "clock")
// Date formatting function.
lua.pushScalaFunction(lua => {
val format =
if (lua.getTop > 0 && lua.isString(1)) lua.toString(1)
else "%d/%m/%y %H:%M:%S"
val time =
if (lua.getTop > 1 && lua.isNumber(2)) lua.toNumber(2) * 1000 / 60 / 60
else machine.worldTime + 6000
val dt = GameTimeFormatter.parse(time)
def fmt(format: String) {
if (format == "*t") {
lua.newTable(0, 8)
lua.pushInteger(dt.year)
lua.setField(-2, "year")
lua.pushInteger(dt.month)
lua.setField(-2, "month")
lua.pushInteger(dt.day)
lua.setField(-2, "day")
lua.pushInteger(dt.hour)
lua.setField(-2, "hour")
lua.pushInteger(dt.minute)
lua.setField(-2, "min")
lua.pushInteger(dt.second)
lua.setField(-2, "sec")
lua.pushInteger(dt.weekDay)
lua.setField(-2, "wday")
lua.pushInteger(dt.yearDay)
lua.setField(-2, "yday")
}
else {
lua.pushString(GameTimeFormatter.format(format, dt))
}
}
// Just ignore the allowed leading '!', Minecraft has no time zones...
if (format.startsWith("!"))
fmt(format.substring(1))
else
fmt(format)
1
})
lua.setField(-2, "date")
// Return ingame time for os.time().
lua.pushScalaFunction(lua => {
// Game time is in ticks, so that each day has 24000 ticks, meaning
// one hour is game time divided by one thousand. Also, Minecraft
// starts days at 6 o'clock, so we add those six hours. Thus:
// timestamp = (time + 6000) * 60[kh] * 60[km] / 1000[s]
lua.pushNumber((machine.worldTime + 6000) * 60 * 60 / 1000)
1
})
lua.setField(-2, "time")
// Pop the os table.
lua.pop(1)
// Computer API, stuff that kinda belongs to os, but we don't want to
// clutter it.
lua.newTable()
// Allow getting the real world time for timeouts.
lua.pushScalaFunction(lua => {
lua.pushNumber(System.currentTimeMillis() / 1000.0)
1
})
lua.setField(-2, "realTime")
// The time the computer has been running, as opposed to the CPU time.
lua.pushScalaFunction(lua => {
// World time is in ticks, and each second has 20 ticks. Since we
// want uptime() to return real seconds, though, we'll divide it
// accordingly.
lua.pushNumber((machine.worldTime - machine.timeStarted) / 20.0)
1
})
lua.setField(-2, "uptime")
// Allow the computer to figure out its own id in the component network.
lua.pushScalaFunction(lua => {
Option(node.address) match {
case None => lua.pushNil()
case Some(address) => lua.pushString(address)
}
1
})
lua.setField(-2, "address")
// Are we a robot? (No this is not a CAPTCHA.)
lua.pushScalaFunction(lua => {
lua.pushBoolean(machine.isRobot)
1
})
lua.setField(-2, "isRobot")
lua.pushScalaFunction(lua => {
// This is *very* unlikely, but still: avoid this getting larger than
// what we report as the total memory.
lua.pushInteger(((lua.getFreeMemory min (lua.getTotalMemory - kernelMemory)) / ramScale).toInt)
1
})
lua.setField(-2, "freeMemory")
// Allow the system to read how much memory it uses and has available.
lua.pushScalaFunction(lua => {
lua.pushInteger(((lua.getTotalMemory - kernelMemory) / ramScale).toInt)
1
})
lua.setField(-2, "totalMemory")
lua.pushScalaFunction(lua => {
lua.pushBoolean(machine.signal(lua.checkString(1), lua.toSimpleJavaObjects(2): _*))
1
})
lua.setField(-2, "pushSignal")
// And its ROM address.
lua.pushScalaFunction(lua => {
machine.rom.foreach(rom => Option(rom.node.address) match {
case None => lua.pushNil()
case Some(address) => lua.pushString(address)
})
1
})
lua.setField(-2, "romAddress")
// And it's /tmp address...
lua.pushScalaFunction(lua => {
machine.tmp.foreach(tmp => Option(tmp.node.address) match {
case None => lua.pushNil()
case Some(address) => lua.pushString(address)
})
1
})
lua.setField(-2, "tmpAddress")
// User management.
lua.pushScalaFunction(lua => {
val users = machine.users
users.foreach(lua.pushString)
users.length
})
lua.setField(-2, "users")
lua.pushScalaFunction(lua => try {
machine.addUser(lua.checkString(1))
lua.pushBoolean(true)
1
} catch {
case e: Throwable =>
lua.pushNil()
lua.pushString(Option(e.getMessage).getOrElse(e.toString))
2
})
lua.setField(-2, "addUser")
lua.pushScalaFunction(lua => {
lua.pushBoolean(machine.removeUser(lua.checkString(1)))
1
})
lua.setField(-2, "removeUser")
lua.pushScalaFunction(lua => {
lua.pushNumber(node.globalBuffer)
1
})
lua.setField(-2, "energy")
lua.pushScalaFunction(lua => {
lua.pushNumber(node.globalBufferSize)
1
})
lua.setField(-2, "maxEnergy")
// Set the computer table.
lua.setGlobal("computer")
// Until we get to ingame screens we log to Java's stdout.
lua.pushScalaFunction(lua => {
println((1 to lua.getTop).map(i => lua.`type`(i) match {
case LuaType.NIL => "nil"
case LuaType.BOOLEAN => lua.toBoolean(i)
case LuaType.NUMBER => lua.toNumber(i)
case LuaType.STRING => lua.toString(i)
case LuaType.TABLE => "table"
case LuaType.FUNCTION => "function"
case LuaType.THREAD => "thread"
case LuaType.LIGHTUSERDATA | LuaType.USERDATA => "userdata"
}).mkString(" "))
0
})
lua.setGlobal("print")
// Whether bytecode may be loaded directly.
lua.pushScalaFunction(lua => {
lua.pushBoolean(Settings.get.allowBytecode)
1
})
lua.setGlobal("allowBytecode")
// How long programs may run without yielding before we stop them.
lua.pushNumber(Settings.get.timeout)
lua.setGlobal("timeout")
// Component interaction stuff.
lua.newTable()
lua.pushScalaFunction(lua => components.synchronized {
val filter = if (lua.isString(1)) Option(lua.toString(1)) else None
lua.newTable(0, components.size)
for ((address, name) <- components) {
if (filter.isEmpty || name.contains(filter.get)) {
lua.pushString(address)
lua.pushString(name)
lua.rawSet(-3)
}
}
1
})
lua.setField(-2, "list")
lua.pushScalaFunction(lua => components.synchronized {
components.get(lua.checkString(1)) match {
case Some(name: String) =>
lua.pushString(name)
1
case _ =>
lua.pushNil()
lua.pushString("no such component")
2
}
})
lua.setField(-2, "type")
lua.pushScalaFunction(lua => {
Option(node.network.node(lua.checkString(1))) match {
case Some(component: server.network.Component) if component.canBeSeenFrom(node) || component == node =>
lua.newTable()
for (method <- component.methods()) {
lua.pushString(method)
lua.pushBoolean(component.isDirect(method))
lua.rawSet(-3)
}
1
case _ =>
lua.pushNil()
lua.pushString("no such component")
2
}
})
lua.setField(-2, "methods")
lua.pushScalaFunction(lua => {
val address = lua.checkString(1)
val method = lua.checkString(2)
val args = lua.toSimpleJavaObjects(3)
try {
machine.invoke(address, method, args) match {
case results: Array[_] =>
lua.pushBoolean(true)
results.foreach(result => lua.pushValue(result))
1 + results.length
case _ =>
lua.pushBoolean(true)
1
}
}
catch {
case e: Throwable =>
if (Settings.get.logLuaCallbackErrors && !e.isInstanceOf[Machine.LimitReachedException]) {
OpenComputers.log.log(Level.WARNING, "Exception in Lua callback.", e)
}
e match {
case _: Machine.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 (Settings.get.logLuaCallbackErrors) {
lua.pushString(e.getStackTraceString.replace("\r\n", "\n"))
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.setGlobal("component")
initPerms()
lua.load(classOf[Machine].getResourceAsStream(Settings.scriptPath + "kernel.lua"), "=kernel", "t")
lua.newThread() // Left as the first value on the stack.
true
}
def close() {
if (lua != null) { if (lua != null) {
lua.setTotalMemory(Integer.MAX_VALUE) lua.setTotalMemory(Integer.MAX_VALUE)
lua.close() lua.close()
} }
lua = null lua = null
super.close() kernelMemory = 0
}
protected def createState() = {
// Creates a new state with all base libraries and the persistence library
// loaded into it. This means the state has much more power than it
// rightfully should have, so we sandbox it a bit in the following.
LuaStateFactory.createState() match {
case Some(value) =>
lua = value
true
case _ =>
lua = null
machine.message = Some("native libraries not available")
false
}
} }
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
@ -82,7 +579,7 @@ class NativeLuaArchitecture(machine: Machine) extends LuaArchitecture(machine) {
kernelMemory = (nbt.getInteger("kernelMemory") * ramScale).toInt kernelMemory = (nbt.getInteger("kernelMemory") * ramScale).toInt
} catch { } catch {
case e: jnlua.LuaRuntimeException => case e: LuaRuntimeException =>
OpenComputers.log.warning("Could not unpersist computer.\n" + e.toString + "\tat " + e.getLuaStackTrace.mkString("\n\tat ")) OpenComputers.log.warning("Could not unpersist computer.\n" + e.toString + "\tat " + e.getLuaStackTrace.mkString("\n\tat "))
state.push(Machine.State.Stopping) state.push(Machine.State.Stopping)
} }
@ -109,7 +606,7 @@ class NativeLuaArchitecture(machine: Machine) extends LuaArchitecture(machine) {
nbt.setInteger("kernelMemory", math.ceil(kernelMemory / ramScale).toInt) nbt.setInteger("kernelMemory", math.ceil(kernelMemory / ramScale).toInt)
} catch { } catch {
case e: jnlua.LuaRuntimeException => case e: LuaRuntimeException =>
OpenComputers.log.warning("Could not persist computer.\n" + e.toString + "\tat " + e.getLuaStackTrace.mkString("\n\tat ")) OpenComputers.log.warning("Could not persist computer.\n" + e.toString + "\tat " + e.getLuaStackTrace.mkString("\n\tat "))
nbt.removeTag("state") nbt.removeTag("state")
} }
@ -183,15 +680,15 @@ class NativeLuaArchitecture(machine: Machine) extends LuaArchitecture(machine) {
lua.getGlobal("_G") /* ... perms uperms k v */ lua.getGlobal("_G") /* ... perms uperms k v */
flattenAndStore() /* ... perms uperms */ flattenAndStore() /* ... perms uperms */
lua.setField(jnlua.LuaState.REGISTRYINDEX, "uperms") /* ... perms */ lua.setField(LuaState.REGISTRYINDEX, "uperms") /* ... perms */
lua.setField(jnlua.LuaState.REGISTRYINDEX, "perms") /* ... */ lua.setField(LuaState.REGISTRYINDEX, "perms") /* ... */
} }
private def persist(index: Int): Array[Byte] = { private def persist(index: Int): Array[Byte] = {
lua.getGlobal("eris") /* ... eris */ lua.getGlobal("eris") /* ... eris */
lua.getField(-1, "persist") /* ... eris persist */ lua.getField(-1, "persist") /* ... eris persist */
if (lua.isFunction(-1)) { if (lua.isFunction(-1)) {
lua.getField(jnlua.LuaState.REGISTRYINDEX, "perms") /* ... eris persist perms */ lua.getField(LuaState.REGISTRYINDEX, "perms") /* ... eris persist perms */
lua.pushValue(index) // ... eris persist perms obj lua.pushValue(index) // ... eris persist perms obj
try { try {
lua.call(2, 1) // ... eris str? lua.call(2, 1) // ... eris str?
@ -215,7 +712,7 @@ class NativeLuaArchitecture(machine: Machine) extends LuaArchitecture(machine) {
lua.getGlobal("eris") // ... eris lua.getGlobal("eris") // ... eris
lua.getField(-1, "unpersist") // ... eris unpersist lua.getField(-1, "unpersist") // ... eris unpersist
if (lua.isFunction(-1)) { if (lua.isFunction(-1)) {
lua.getField(jnlua.LuaState.REGISTRYINDEX, "uperms") /* ... eris persist uperms */ lua.getField(LuaState.REGISTRYINDEX, "uperms") /* ... eris persist uperms */
lua.pushByteArray(value) // ... eris unpersist uperms str lua.pushByteArray(value) // ... eris unpersist uperms str
lua.call(2, 1) // ... eris obj lua.call(2, 1) // ... eris obj
lua.insert(-2) // ... obj eris lua.insert(-2) // ... obj eris

View File

@ -1,19 +1,22 @@
package li.cil.oc.util package li.cil.oc.util
import com.naef.jnlua.LuaType import com.naef.jnlua.{LuaType, JavaFunction, LuaState}
import li.cil.oc.OpenComputers import li.cil.oc.OpenComputers
import scala.collection.convert.WrapAsScala._ import scala.collection.convert.WrapAsScala._
import scala.collection.mutable import scala.collection.mutable
import scala.language.implicitConversions import scala.language.implicitConversions
import scala.math.ScalaNumber import scala.math.ScalaNumber
import scala.runtime.BoxedUnit import scala.runtime.BoxedUnit
import li.cil.oc.util.LuaState
object ExtendedLuaState { object ExtendedLuaState {
implicit def extendLuaState(state: LuaState) = new ExtendedLuaState(state) implicit def extendLuaState(state: LuaState) = new ExtendedLuaState(state)
class ExtendedLuaState(val lua: LuaState) { class ExtendedLuaState(val lua: LuaState) {
def pushScalaFunction(f: (LuaState) => Int) = lua.pushJavaFunction(new JavaFunction {
override def invoke(state: LuaState) = f(state)
})
def pushValue(value: Any) { def pushValue(value: Any) {
(value match { (value match {
case number: ScalaNumber => number.underlying case number: ScalaNumber => number.underlying
@ -73,7 +76,7 @@ object ExtendedLuaState {
case LuaType.BOOLEAN => Boolean.box(lua.toBoolean(index)) case LuaType.BOOLEAN => Boolean.box(lua.toBoolean(index))
case LuaType.NUMBER => Double.box(lua.toNumber(index)) case LuaType.NUMBER => Double.box(lua.toNumber(index))
case LuaType.STRING => lua.toByteArray(index) case LuaType.STRING => lua.toByteArray(index)
case LuaType.TABLE => lua.toMap(index) case LuaType.TABLE => lua.toJavaObject(index, classOf[java.util.Map[_, _]])
case _ => null case _ => null
} }

View File

@ -1,311 +0,0 @@
package li.cil.oc.util
import com.naef.jnlua
import java.io.InputStream
import org.luaj
import com.naef.jnlua.LuaState.GcAction
import com.naef.jnlua.LuaType
trait LuaState {
def getTop: Int
def setTop(n: Int)
def pop(n: Int)
def `type`(index: Int): jnlua.LuaType
def isNil(index: Int): Boolean
def isBoolean(index: Int): Boolean
def isNumber(index: Int): Boolean
def isString(index: Int): Boolean
def isTable(index: Int): Boolean
def isFunction(index: Int): Boolean
def isThread(index: Int): Boolean
def checkType(index: Int, what: jnlua.LuaType)
def checkArg(index: Int, condition: Boolean, message: String)
def checkInteger(index: Int): Int
def checkString(index: Int): String
def toBoolean(index: Int): Boolean
def toNumber(index: Int): Double
def toByteArray(index: Int): Array[Byte]
def toString(index: Int): String
def toMap(index: Int): java.util.Map[_, _]
def insert(index: Int)
def pushNil()
def pushBoolean(value: Boolean)
def pushInteger(value: Int)
def pushNumber(value: Double)
def pushByteArray(value: Array[Byte])
def pushString(value: String)
def pushValue(index: Int)
def pushScalaFunction(f: LuaState => Int)
def load(stream: InputStream, name: String)
def call(index: Int, argCount: Int)
def resume(index: Int, argCount: Int): Int
def status(index: Int): Int
def gc(what: com.naef.jnlua.LuaState.GcAction, arg: Int)
def newTable(arrayCount: Int = 0, recordCount: Int = 0)
def newThread()
def getTable(index: Int)
def setTable(index: Int)
def setField(index: Int, key: String)
def getField(index: Int, key: String)
def rawSet(index: Int)
def rawSet(index: Int, key: Int)
def getGlobal(key: String)
def setGlobal(key: String)
def next(index: Int): Boolean
def getFreeMemory: Int
def getTotalMemory: Int
def setTotalMemory(value: Int)
def close()
}
object LuaState {
implicit def wrap(lua: jnlua.LuaState): LuaState = new LuaState {
def getTop = lua.getTop
def setTop(n: Int) = lua.setTop(n)
def pop(n: Int) = lua.pop(n)
def `type`(index: Int) = lua.`type`(index)
def isNil(index: Int) = lua.isNil(index)
def isBoolean(index: Int) = lua.isBoolean(index)
def isNumber(index: Int) = lua.isNumber(index)
def isString(index: Int) = lua.isString(index)
def isTable(index: Int) = lua.isTable(index)
def isFunction(index: Int) = lua.isFunction(index)
def isThread(index: Int) = lua.isThread(index)
def checkType(index: Int, what: jnlua.LuaType) = lua.checkType(index, what)
def checkArg(index: Int, condition: Boolean, message: String) = lua.checkArg(index, condition, message)
def checkInteger(index: Int) = lua.checkInteger(index)
def checkString(index: Int) = lua.checkString(index)
def toBoolean(index: Int) = lua.toBoolean(index)
def toNumber(index: Int) = lua.toNumber(index)
def toByteArray(index: Int) = lua.toByteArray(index)
def toString(index: Int) = lua.toString(index)
def toMap(index: Int) = lua.toJavaObject(index, classOf[java.util.Map[_, _]])
def insert(index: Int) = lua.insert(index)
def pushNil() = lua.pushNil()
def pushBoolean(value: Boolean) = lua.pushBoolean(value)
def pushInteger(value: Int) = lua.pushInteger(value)
def pushNumber(value: Double) = lua.pushNumber(value)
def pushByteArray(value: Array[Byte]) = lua.pushByteArray(value)
def pushString(value: String) = lua.pushString(value)
def pushValue(index: Int) = lua.pushValue(index)
def pushScalaFunction(f: (LuaState) => Int) = lua.pushJavaFunction(new jnlua.JavaFunction {
def invoke(state: jnlua.LuaState) = f(LuaState.wrap(state))
})
def load(stream: InputStream, name: String) = lua.load(stream, name, "t")
def call(index: Int, argCount: Int) = lua.call(index, argCount)
def resume(index: Int, argCount: Int) = lua.resume(index, argCount)
def status(index: Int) = lua.status(index)
def gc(what: jnlua.LuaState.GcAction, arg: Int) = lua.gc(what, arg)
def newTable(arrayCount: Int, recordCount: Int) = lua.newTable(arrayCount, recordCount)
def newThread() = lua.newThread()
def getTable(index: Int) = lua.getTable(index)
def setTable(index: Int) = lua.setTable(index)
def setField(index: Int, key: String) = lua.setField(index, key)
def getField(index: Int, key: String) = lua.getField(index, key)
def rawSet(index: Int) = lua.rawSet(index)
def rawSet(index: Int, key: Int) = lua.rawSet(index, key)
def getGlobal(key: String) = lua.getGlobal(key)
def setGlobal(key: String) = lua.setGlobal(key)
def next(index: Int) = lua.next(index)
def getFreeMemory = lua.getFreeMemory
def getTotalMemory = lua.getTotalMemory
def setTotalMemory(value: Int) = lua.setTotalMemory(value)
def close() = lua.close()
}
implicit def wrap(lua: luaj.vm2.Globals): LuaState = new LuaState {
def getTop = ???
def setTop(n: Int) = ???
def pop(n: Int) = ???
def `type`(index: Int) = ???
def isNil(index: Int) = ???
def isBoolean(index: Int) = ???
def isNumber(index: Int) = ???
def isString(index: Int) = ???
def isTable(index: Int) = ???
def isFunction(index: Int) = ???
def isThread(index: Int) = ???
def checkType(index: Int, what: LuaType) = ???
def checkArg(index: Int, condition: Boolean, message: String) = ???
def checkInteger(index: Int) = ???
def checkString(index: Int) = ???
def toBoolean(index: Int) = ???
def toNumber(index: Int) = ???
def toByteArray(index: Int) = ???
def toString(index: Int) = ???
def toMap(index: Int) = ???
def insert(index: Int) = ???
def pushNil() = ???
def pushBoolean(value: Boolean) = ???
def pushInteger(value: Int) = ???
def pushNumber(value: Double) = ???
def pushByteArray(value: Array[Byte]) = ???
def pushString(value: String) = ???
def pushValue(index: Int) = ???
def pushScalaFunction(f: (LuaState) => Int) = ???
def load(stream: InputStream, name: String) = ???
def call(index: Int, argCount: Int) = ???
def resume(index: Int, argCount: Int) = ???
def status(index: Int) = ???
def gc(what: GcAction, arg: Int) = ???
def newTable(arrayCount: Int, recordCount: Int) = ???
def newThread() = ???
def getTable(index: Int) = ???
def setTable(index: Int) = ???
def setField(index: Int, key: String) = ???
def getField(index: Int, key: String) = ???
def rawSet(index: Int) = ???
def rawSet(index: Int, key: Int) = ???
def getGlobal(key: String) = ???
def setGlobal(key: String) = ???
def next(index: Int) = ???
def getFreeMemory = ???
def getTotalMemory = ???
def setTotalMemory(value: Int) = ???
def close() = ???
}
}

View File

@ -1,13 +1,14 @@
package li.cil.oc.util package li.cil.oc.util
import LuaState._
import com.naef.jnlua import com.naef.jnlua
import com.naef.jnlua.LuaState
import com.naef.jnlua.NativeSupport.Loader import com.naef.jnlua.NativeSupport.Loader
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.nio.channels.Channels import java.nio.channels.Channels
import java.util.logging.Level import java.util.logging.Level
import li.cil.oc.server.component.Machine import li.cil.oc.server.component.Machine
import li.cil.oc.util.ExtendedLuaState._
import li.cil.oc.{OpenComputers, Settings} import li.cil.oc.{OpenComputers, Settings}
import org.apache.commons.lang3.SystemUtils import org.apache.commons.lang3.SystemUtils
import org.lwjgl.LWJGLUtil import org.lwjgl.LWJGLUtil