mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-17 11:15:12 -04:00
fgsfds
This commit is contained in:
parent
14f7037e46
commit
1335536c5d
@ -6,7 +6,7 @@ import li.cil.oc.api.network._
|
||||
import li.cil.oc.common.tileentity
|
||||
import li.cil.oc.server
|
||||
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.ThreadPoolFactory
|
||||
import li.cil.oc.{OpenComputers, Settings}
|
||||
|
@ -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
|
||||
}
|
@ -2,15 +2,396 @@ package li.cil.oc.server.component.machine
|
||||
|
||||
import li.cil.oc.server.component.Machine
|
||||
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) {
|
||||
protected def createState() = {
|
||||
class LuaJLuaArchitecture(machine: Machine) extends Architecture {
|
||||
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()
|
||||
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
|
||||
}
|
||||
|
||||
def recomputeMemory() {}
|
||||
def close() = ???
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
def load(nbt: NBTTagCompound) {}
|
||||
|
||||
|
@ -1,19 +1,40 @@
|
||||
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.{GameTimeFormatter, LuaStateFactory}
|
||||
import li.cil.oc.{OpenComputers, server, Settings}
|
||||
import net.minecraft.nbt.NBTTagCompound
|
||||
import li.cil.oc.OpenComputers
|
||||
import scala.Some
|
||||
import scala.collection.convert.WrapAsScala._
|
||||
import scala.collection.mutable
|
||||
import li.cil.oc.util.LuaStateFactory
|
||||
import com.naef.jnlua
|
||||
|
||||
class NativeLuaArchitecture(machine: Machine) extends LuaArchitecture(machine) {
|
||||
override var lua: jnlua.LuaState = _
|
||||
class NativeLuaArchitecture(val machine: Machine) extends Architecture {
|
||||
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 {
|
||||
case Some(l) =>
|
||||
l.setTotalMemory(Int.MaxValue)
|
||||
l.gc(jnlua.LuaState.GcAction.COLLECT, 0)
|
||||
l.gc(LuaState.GcAction.COLLECT, 0)
|
||||
if (kernelMemory > 0) {
|
||||
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 = {
|
||||
if (super.init()) {
|
||||
initPerms()
|
||||
true
|
||||
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, 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) {
|
||||
lua.setTotalMemory(Integer.MAX_VALUE)
|
||||
lua.close()
|
||||
}
|
||||
lua = null
|
||||
super.close()
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
kernelMemory = 0
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
@ -82,7 +579,7 @@ class NativeLuaArchitecture(machine: Machine) extends LuaArchitecture(machine) {
|
||||
|
||||
kernelMemory = (nbt.getInteger("kernelMemory") * ramScale).toInt
|
||||
} catch {
|
||||
case e: jnlua.LuaRuntimeException =>
|
||||
case e: LuaRuntimeException =>
|
||||
OpenComputers.log.warning("Could not unpersist computer.\n" + e.toString + "\tat " + e.getLuaStackTrace.mkString("\n\tat "))
|
||||
state.push(Machine.State.Stopping)
|
||||
}
|
||||
@ -109,7 +606,7 @@ class NativeLuaArchitecture(machine: Machine) extends LuaArchitecture(machine) {
|
||||
|
||||
nbt.setInteger("kernelMemory", math.ceil(kernelMemory / ramScale).toInt)
|
||||
} catch {
|
||||
case e: jnlua.LuaRuntimeException =>
|
||||
case e: LuaRuntimeException =>
|
||||
OpenComputers.log.warning("Could not persist computer.\n" + e.toString + "\tat " + e.getLuaStackTrace.mkString("\n\tat "))
|
||||
nbt.removeTag("state")
|
||||
}
|
||||
@ -183,15 +680,15 @@ class NativeLuaArchitecture(machine: Machine) extends LuaArchitecture(machine) {
|
||||
lua.getGlobal("_G") /* ... perms uperms k v */
|
||||
|
||||
flattenAndStore() /* ... perms uperms */
|
||||
lua.setField(jnlua.LuaState.REGISTRYINDEX, "uperms") /* ... perms */
|
||||
lua.setField(jnlua.LuaState.REGISTRYINDEX, "perms") /* ... */
|
||||
lua.setField(LuaState.REGISTRYINDEX, "uperms") /* ... perms */
|
||||
lua.setField(LuaState.REGISTRYINDEX, "perms") /* ... */
|
||||
}
|
||||
|
||||
private def persist(index: Int): Array[Byte] = {
|
||||
lua.getGlobal("eris") /* ... eris */
|
||||
lua.getField(-1, "persist") /* ... eris persist */
|
||||
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
|
||||
try {
|
||||
lua.call(2, 1) // ... eris str?
|
||||
@ -215,7 +712,7 @@ class NativeLuaArchitecture(machine: Machine) extends LuaArchitecture(machine) {
|
||||
lua.getGlobal("eris") // ... eris
|
||||
lua.getField(-1, "unpersist") // ... eris unpersist
|
||||
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.call(2, 1) // ... eris obj
|
||||
lua.insert(-2) // ... obj eris
|
||||
|
@ -1,19 +1,22 @@
|
||||
package li.cil.oc.util
|
||||
|
||||
import com.naef.jnlua.LuaType
|
||||
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.math.ScalaNumber
|
||||
import scala.runtime.BoxedUnit
|
||||
import li.cil.oc.util.LuaState
|
||||
|
||||
object ExtendedLuaState {
|
||||
|
||||
implicit def extendLuaState(state: LuaState) = new ExtendedLuaState(state)
|
||||
|
||||
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) {
|
||||
(value match {
|
||||
case number: ScalaNumber => number.underlying
|
||||
@ -73,7 +76,7 @@ object ExtendedLuaState {
|
||||
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.toMap(index)
|
||||
case LuaType.TABLE => lua.toJavaObject(index, classOf[java.util.Map[_, _]])
|
||||
case _ => null
|
||||
}
|
||||
|
||||
|
@ -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() = ???
|
||||
}
|
||||
}
|
@ -1,13 +1,14 @@
|
||||
package li.cil.oc.util
|
||||
|
||||
import LuaState._
|
||||
import com.naef.jnlua
|
||||
import com.naef.jnlua.LuaState
|
||||
import com.naef.jnlua.NativeSupport.Loader
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.nio.channels.Channels
|
||||
import java.util.logging.Level
|
||||
import li.cil.oc.server.component.Machine
|
||||
import li.cil.oc.util.ExtendedLuaState._
|
||||
import li.cil.oc.{OpenComputers, Settings}
|
||||
import org.apache.commons.lang3.SystemUtils
|
||||
import org.lwjgl.LWJGLUtil
|
||||
|
Loading…
x
Reference in New Issue
Block a user