catching error when creating lua state due to missing libraries in factory and logging it right there, to allow printing error to chat the first time, too; different name the lib is saved to the tmp folder to reduce the chance of collisions; explicit message for windows users reminding them to install the vc2012 runtimes on library load errors

This commit is contained in:
Florian Nücke 2013-12-27 00:18:22 +01:00
parent fb6f0a8866
commit 55524c4a78

View File

@ -5,6 +5,7 @@ import com.naef.jnlua.{LuaState, NativeSupport}
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 li.cil.oc.server.component.Computer import li.cil.oc.server.component.Computer
import li.cil.oc.util.ExtendedLuaState._ import li.cil.oc.util.ExtendedLuaState._
import li.cil.oc.{OpenComputers, Settings} import li.cil.oc.{OpenComputers, Settings}
@ -27,6 +28,8 @@ object LuaStateFactory {
/** Set to true in initialization code below if available. */ /** Set to true in initialization code below if available. */
private var haveNativeLibrary = false private var haveNativeLibrary = false
private var isWindows = false
private var _is64Bit = false private var _is64Bit = false
def is64Bit = _is64Bit def is64Bit = _is64Bit
@ -76,6 +79,7 @@ object LuaStateFactory {
break() break()
} }
} }
isWindows = extension == ".dll"
val libPath = "/assets/" + Settings.resourceDomain + "/lib/" val libPath = "/assets/" + Settings.resourceDomain + "/lib/"
val tmpPath = { val tmpPath = {
@ -92,7 +96,7 @@ object LuaStateFactory {
} }
// Found file with proper extension. Create a temporary file. // Found file with proper extension. Create a temporary file.
val file = new File(tmpPath + library) val file = new File(tmpPath + "OpenComputersMod-" + library)
// Try to delete an old instance of the library, in case we have an update // Try to delete an old instance of the library, in case we have an update
// and deleteOnExit fails (which it regularly does on Windows it seems). // and deleteOnExit fails (which it regularly does on Windows it seems).
try { try {
@ -145,145 +149,160 @@ object LuaStateFactory {
def createState(): Option[LuaState] = { def createState(): Option[LuaState] = {
if (!haveNativeLibrary) return None if (!haveNativeLibrary) return None
val state = new LuaState(Int.MaxValue)
try { try {
// Load all libraries. val state = new LuaState(Int.MaxValue)
state.openLib(LuaState.Library.BASE) try {
state.openLib(LuaState.Library.BIT32) // Load all libraries.
state.openLib(LuaState.Library.COROUTINE) state.openLib(LuaState.Library.BASE)
state.openLib(LuaState.Library.DEBUG) state.openLib(LuaState.Library.BIT32)
state.openLib(LuaState.Library.ERIS) state.openLib(LuaState.Library.COROUTINE)
state.openLib(LuaState.Library.MATH) state.openLib(LuaState.Library.DEBUG)
state.openLib(LuaState.Library.STRING) state.openLib(LuaState.Library.ERIS)
state.openLib(LuaState.Library.TABLE) state.openLib(LuaState.Library.MATH)
state.pop(8) state.openLib(LuaState.Library.STRING)
state.openLib(LuaState.Library.TABLE)
state.pop(8)
// Prepare table for os stuff. // Prepare table for os stuff.
state.newTable() state.newTable()
state.setGlobal("os") state.setGlobal("os")
// Kill compat entries. // Kill compat entries.
state.pushNil() state.pushNil()
state.setGlobal("unpack") state.setGlobal("unpack")
state.pushNil() state.pushNil()
state.setGlobal("loadstring") state.setGlobal("loadstring")
state.getGlobal("math") state.getGlobal("math")
state.pushNil() state.pushNil()
state.setField(-2, "log10") state.setField(-2, "log10")
state.pop(1) state.pop(1)
state.getGlobal("table") state.getGlobal("table")
state.pushNil() state.pushNil()
state.setField(-2, "maxn") state.setField(-2, "maxn")
state.pop(1) state.pop(1)
// Remove some other functions we don't need and are dangerous. // Remove some other functions we don't need and are dangerous.
state.pushNil() state.pushNil()
state.setGlobal("dofile") state.setGlobal("dofile")
state.pushNil() state.pushNil()
state.setGlobal("loadfile") state.setGlobal("loadfile")
state.getGlobal("math") state.getGlobal("math")
// We give each Lua state it's own randomizer, since otherwise they'd // We give each Lua state it's own randomizer, since otherwise they'd
// use the good old rand() from C. Which can be terrible, and isn't // use the good old rand() from C. Which can be terrible, and isn't
// necessarily thread-safe. // necessarily thread-safe.
val random = new Random val random = new Random
state.pushScalaFunction(lua => { state.pushScalaFunction(lua => {
lua.getTop match { lua.getTop match {
case 0 => lua.pushNumber(random.nextDouble()) case 0 => lua.pushNumber(random.nextDouble())
case 1 => case 1 =>
val u = lua.checkInteger(1) val u = lua.checkInteger(1)
lua.checkArg(1, 1 < u, "interval is empty") lua.checkArg(1, 1 < u, "interval is empty")
lua.pushInteger(1 + random.nextInt(u)) lua.pushInteger(1 + random.nextInt(u))
case 2 => case 2 =>
val l = lua.checkInteger(1) val l = lua.checkInteger(1)
val u = lua.checkInteger(2) val u = lua.checkInteger(2)
lua.checkArg(1, l < u, "interval is empty") lua.checkArg(1, l < u, "interval is empty")
lua.pushInteger(l + random.nextInt(u - (l - 1))) lua.pushInteger(l + random.nextInt(u - (l - 1)))
case _ => throw new IllegalArgumentException("wrong number of arguments") case _ => throw new IllegalArgumentException("wrong number of arguments")
} }
1 1
})
state.setField(-2, "random")
state.pushScalaFunction(lua => {
random.setSeed(lua.checkInteger(1))
0
})
state.setField(-2, "randomseed")
// Pop the math table.
state.pop(1)
// Provide some better Unicode support.
state.newTable()
// TODO find (probably not necessary?)
// TODO format (probably not necessary?)
// TODO gmatch (probably not necessary?)
// TODO gsub (probably not necessary?)
// TODO match (probably not necessary?)
state.pushScalaFunction(lua => {
lua.pushString(lua.checkString(1).toLowerCase)
1
})
state.setField(-2, "lower")
state.pushScalaFunction(lua => {
lua.pushString(lua.checkString(1).toUpperCase)
1
})
state.setField(-2, "upper")
state.pushScalaFunction(lua => {
lua.pushString(String.valueOf((1 to lua.getTop).map(lua.checkInteger).map(_.toChar).toArray))
1
})
state.setField(-2, "char")
state.pushScalaFunction(lua => {
lua.pushInteger(lua.checkString(1).length)
1
})
state.setField(-2, "len")
state.pushScalaFunction(lua => {
lua.pushString(lua.checkString(1).reverse)
1
})
state.setField(-2, "reverse")
state.pushScalaFunction(lua => {
val string = lua.checkString(1)
val start = math.max(0, lua.checkInteger(2) match {
case i if i < 0 => string.length + i
case i => i - 1
}) })
val end = state.setField(-2, "random")
if (lua.getTop > 2) math.min(string.length, lua.checkInteger(3) match {
case i if i < 0 => string.length + i + 1 state.pushScalaFunction(lua => {
case i => i random.setSeed(lua.checkInteger(1))
0
})
state.setField(-2, "randomseed")
// Pop the math table.
state.pop(1)
// Provide some better Unicode support.
state.newTable()
// TODO find (probably not necessary?)
// TODO format (probably not necessary?)
// TODO gmatch (probably not necessary?)
// TODO gsub (probably not necessary?)
// TODO match (probably not necessary?)
state.pushScalaFunction(lua => {
lua.pushString(lua.checkString(1).toLowerCase)
1
})
state.setField(-2, "lower")
state.pushScalaFunction(lua => {
lua.pushString(lua.checkString(1).toUpperCase)
1
})
state.setField(-2, "upper")
state.pushScalaFunction(lua => {
lua.pushString(String.valueOf((1 to lua.getTop).map(lua.checkInteger).map(_.toChar).toArray))
1
})
state.setField(-2, "char")
state.pushScalaFunction(lua => {
lua.pushInteger(lua.checkString(1).length)
1
})
state.setField(-2, "len")
state.pushScalaFunction(lua => {
lua.pushString(lua.checkString(1).reverse)
1
})
state.setField(-2, "reverse")
state.pushScalaFunction(lua => {
val string = lua.checkString(1)
val start = math.max(0, lua.checkInteger(2) match {
case i if i < 0 => string.length + i
case i => i - 1
}) })
else string.length val end =
if (end <= start) lua.pushString("") if (lua.getTop > 2) math.min(string.length, lua.checkInteger(3) match {
else lua.pushString(string.substring(start, end)) case i if i < 0 => string.length + i + 1
1 case i => i
}) })
state.setField(-2, "sub") else string.length
if (end <= start) lua.pushString("")
else lua.pushString(string.substring(start, end))
1
})
state.setField(-2, "sub")
state.setGlobal("unicode") state.setGlobal("unicode")
Some(state) return Some(state)
} catch { }
case ex: Throwable => catch {
ex.printStackTrace() case t: Throwable =>
state.close() OpenComputers.log.log(Level.WARNING, "Failed creating Lua state.", t)
return None state.close()
}
} }
catch {
case _: UnsatisfiedLinkError =>
OpenComputers.log.severe("Failed loading the native libraries.")
if (isWindows) {
OpenComputers.log.severe(
"Please ensure you have the Visual C++ 2012 Runtime installed " +
"(when on 64 Bit, both the 32 bit and 64 bit version of the " +
"runtime).")
}
case t: Throwable =>
OpenComputers.log.log(Level.WARNING, "Failed creating Lua state.", t)
}
None
} }
} }