Working line-of-text and single key input

Former-commit-id: 339650979ff2cb5ec18b52a9f3f38b281c7862c9
Former-commit-id: d5ebd860afc8d569ba9ab741b6ca7872380af949
This commit is contained in:
Song Minjae
2016-09-21 22:13:48 +09:00
parent 0dabe3971c
commit cbff53ad23
16 changed files with 481 additions and 381 deletions

View File

@@ -19,7 +19,7 @@ import org.newdawn.slick.state.StateBasedGame
*/
class StateVTTest : BasicGameState() {
val vt = SimpleTextTerminal(SimpleTextTerminal.WHITE, 80, 25, colour = true)
val vt = SimpleTextTerminal(SimpleTextTerminal.IBM_GREEN, 80, 25, colour = false)
val computerInside = BaseTerrarumComputer(vt)
val vtUI = Image(vt.displayW, vt.displayH)
@@ -29,7 +29,7 @@ class StateVTTest : BasicGameState() {
}
override fun init(container: GameContainer, game: StateBasedGame) {
vt.openInput()
//vt.openInput()
}
override fun update(container: GameContainer, game: StateBasedGame, delta: Int) {
@@ -57,15 +57,13 @@ class StateVTTest : BasicGameState() {
super.keyPressed(key, c)
vt.keyPressed(key, c)
if (key == Key.RETURN) {
val input = vt.closeInput()
computerInside.runCommand(input, "=prompt")
vt.openInput()
if (!computerInside.isHalted)
computerInside.runCommand("io.write(_COMPUTER.prompt)", "=prompt")
if (!computerInside.isHalted) {
if (key == Key.RETURN && computerInside.luaJ_globals["__scanMode__"].checkjstring() == "line") {
vt.closeInputString() // cut input by pressing Key.RETURN
}
else if (computerInside.luaJ_globals["__scanMode__"].checkjstring() == "a_key") {
vt.closeInputKey(key) // cut input by pressing any key
}
}
}
}

View File

@@ -0,0 +1,74 @@
--[[
Must be loaded VERY FIRST!
Created by minjaesong on 16-09-13.
--]]
-- path for any ingame libraries
package.path = "/net/torvald/terrarum/virtualcomputer/assets/lua/?.lua;" .. package.path
-- global variables
_G._VERSION = "Luaj-jse 5.2"
_G.MONEYSYM = string.char(0x9D) -- currency sign
_G.MIDDOT = string.char(0xFA) -- middle dot sign
_G.DC1 = string.char(17) -- black
_G.DC2 = string.char(18) -- white
_G.DC3 = string.char(19) -- dim grey
_G.DC4 = string.char(20) -- light grey
_G.DLE = string.char(16) -- default error colour
_G.getMem = function() collectgarbage() return collectgarbage("count") * 1024 end
-- getTotalMem: implemented in Kotlin class
_G.getFreeMem = function() return getTotalMem() - getMem() end
_G.runscript = function(s, env)
local code, reason = load(s, env)
if _G.getFreeMem() <= 0 then
print("out of memory")
__haltsystemexplicit__()
return
end
if code then
xpcall(code, eprint)
else
print(DLE..tostring(reason))
end
end
_G.__scanMode__ = "UNINIT" -- part of inputstream implementation
_COMPUTER = {} -- standard console colours
_COMPUTER.prompt = DC3.."> "..DC4
_COMPUTER.verbose = true -- print debug info
_COMPUTER.loadedCLayer = {} -- list of loaded compatibility layers
_COMPUTER.bootloader = "/boot/efi"
_COMPUTER.OEM = ""
-- failsafe
if getTotalMem() == 0 then print("no RAM installed") __haltsystemexplicit__() return end
if _G.getFreeMem() <= 0 then print("out of memory") __haltsystemexplicit__() return end
-- load libraries that coded in Lua
require("ROMLIB")
-- load bios, if any
if fs.exists(_COMPUTER.bootloader) then shell.run(_COMPUTER.bootloader) end
-- halt/run luaprompt upon the termination of bios.
-- Valid BIOS should load OS and modify 'shell.status' to 'shell.halt' before terminating itself.
if shell.status == shell.halt then
__haltsystemexplicit__()
end
-- load Lua prompt, if bios is not found
if (#_COMPUTER.OEM > 0) then print(_COMPUTER.OEM) end
print("Rom basic "..DC2.._VERSION..DC4)
print(DC2..tostring(math.floor(getFreeMem()/1024+0.5))..DC4.." Kbytes free")
print("Ok")
while not native.isHalted() do
io.write(_COMPUTER.prompt)
local s = io.read()
runscript(s, "=stdin")
end
native.closeInputString()
return

View File

@@ -55,7 +55,9 @@ colors.yellow = 0x10
colors.lime = 0x20
colors.pink = 0x40
colors.gray = 0x80
colors.grey = 0x80
colors.lightGray = 0x100
colors.lightGrey = 0x100
colors.cyan = 0x200
colors.purple = 0x400
colors.blue = 0x800
@@ -64,28 +66,6 @@ colors.green = 0x2000
colors.red = 0x4000
colors.black = 0x8000
colors.combine = function(...)
local ret = 0
for _, c in ipairs(...) do
ret = bor(ret, c)
end
return ret
end
local function containsCol(target, cccol)
return bit32.band(target, cccol) > 0
end
colors.subtract = function(cccol, ...)
for _, c in ipairs(...) do
if not containsCol(cccol, c) then
cccol = bit32.bxor(cccol, c)
end
end
return cccol
end
local function normaliseCCcol(cccol)
if cccol >= 0x1 and cccol <= 0xFFFF then
return intLog2(cccol)
@@ -95,6 +75,9 @@ local function normaliseCCcol(cccol)
end
_G.colours = _G.colors
--------------
-- TERM API --
--------------
@@ -172,6 +155,7 @@ fs.copy = function(a, b) fs.cp(a, b) end
fs.delete = function(p) fs.rm(p) end
fs.combine = function(a, b) return fs.concat(a, b) end
fs.getDir = function(p) return fs.parent(p) end
fs.run = function(p) fs.dofile(p) end
------------------

View File

@@ -1,41 +0,0 @@
--[[
Must be loaded VERY FIRST!
Created by minjaesong on 16-09-13.
--]]
-- path for any ingame libraries
package.path = "/net/torvald/terrarum/virtualcomputer/assets/lua/?.lua;" .. package.path
-- global variables
_G.MONEYSYM = string.char(0x9D) -- currency sign
_G.MIDDOT = string.char(0xFA) -- middle dot sign
_COMPUTER = {} -- standard console colours
_COMPUTER.DC1 = string.char(17) -- black
_COMPUTER.DC2 = string.char(18) -- white
_COMPUTER.DC3 = string.char(19) -- dim grey
_COMPUTER.DC4 = string.char(20) -- light grey
_COMPUTER.prompt = function()
io.write(_COMPUTER.DC3.."> ".._COMPUTER.DC4)
end
_COMPUTER.verbose = true -- print debug info
_COMPUTER.loadedCLayer = {} -- list of loaded compatibility layers
_COMPUTER.bootloader = "/boot/efi"
_COMPUTER.OEM = ""
-- load libraries that coded in Lua
require("ROMLIB")
-- load bios, if any
if fs.exists(_COMPUTER.bootloader) then shell.run(_COMPUTER.bootloader) end
-- halt/run luaprompt upon the termination of bios.
-- Valid BIOS should load OS and modify 'shell.status' to 'shell.halt' before terminated.
if shell.status == shell.halt then
__haltsystemexplicit__()
else
-- load Lua prompt, if bios is not found
if (#_COMPUTER.OEM > 0) then print(_COMPUTER.OEM) end
print("Rom basic ".._COMPUTER.DC2.._VERSION.._COMPUTER.DC4)
-- print(_COMPUTER.DC2..freemem.._COMPUTER.DC4.." bytes free"
print("Ok")
end

View File

@@ -6,16 +6,108 @@
-- ALIASES --
-------------
fs.run = function(p)
fs.dofile = function(p)
local f = fs.open(p, "r")
local s = f.readAll()
load(s)()
_G.runscript(s, "="..p)
end
_G.loadstring = _G.load
_G.print = term.print
--_G.dofile = function(f) fs.dofile(f) end
-----------------------------------------
-- INPUTSTREAM AND SCANNER (java-like) --
-----------------------------------------
--[[
In whatever code that actually runs everything (computer),
there must be:
override fun keyPressed(key: Int, c: Char) {
super.keyPressed(key, c)
vt.keyPressed(key, c)
if (key == Key.RETURN) {
val input = vt.closeInputString()
}
}
...it basically says to close the input if RETURN is hit,
and THIS exact part will close the input for this function.
]]
_G.__scanForLine__ = function()
native.closeInputString()
native.openInput()
_G.__scanMode__ = "line"
local s
repeat -- we can do this ONLY IF lua execution process is SEPARATE THREAD
s = native.getLastStreamInput()
until s
-- input is closed when RETURN is hit. See above comments.
return s
end
-- use Keys API to identify the keycode
_G.__scanForChar__ = function()
native.closeInputString()
native.openInput()
_G.__scanMode__ = "a_key"
local key
repeat -- we can do this ONLY IF lua execution process is SEPARATE THREAD
key = native.getLastKeyPress()
until key
-- input is closed when any key is hit. See above comments.
return key
end
io.read = _G.__scanForLine__
-----------------
-- PRINTSTREAM --
-----------------
io.write = function(...)
local args = {...}
for _, v in ipairs(args) do
local s = tostring(v)
term.write(s)
end
end
-- for some reason, inputstream above kills 'print' function.
-- So we rewrite it.
_G.print = function(...)
local args = {...}
io.write(args[1])
if (#args > 1) then
for i = 2, #args do
io.write("\t")
io.write(args[i])
end
end
io.write("\n")
end
---------------
-- SHELL API --
---------------
_G.shell = {}
shell.status = shell.ok
shell.run = function(p) fs.dofile(p) end
shell.ok = 0
shell.halt = 127
--------------
-- HEXUTILS --
@@ -62,20 +154,6 @@ _G.hexutils.toHexString = function(byteString)
end
---------------
-- SHELL API --
---------------
_G.shell = {}
shell.status = shell.ok
shell.run = function(p) fs.run(p) end
shell.ok = 0
shell.halt = 127
--------------
-- KEYS API --
--------------

View File

@@ -0,0 +1 @@
// TODO Fill in from work_files/romapidoc/romapidoc.tex

View File

@@ -3,13 +3,16 @@ package net.torvald.terrarum.virtualcomputer.computer
import li.cil.repack.org.luaj.vm2.Globals
import li.cil.repack.org.luaj.vm2.LuaError
import li.cil.repack.org.luaj.vm2.LuaValue
import li.cil.repack.org.luaj.vm2.lib.ZeroArgFunction
import li.cil.repack.org.luaj.vm2.lib.jse.JsePlatform
import net.torvald.terrarum.KVHashMap
import net.torvald.terrarum.gameactors.ActorValue
import net.torvald.terrarum.virtualcomputer.luaapi.Filesystem
import net.torvald.terrarum.virtualcomputer.luaapi.HostAccessProvider
import net.torvald.terrarum.virtualcomputer.luaapi.Security
import net.torvald.terrarum.virtualcomputer.luaapi.Term
import net.torvald.terrarum.virtualcomputer.terminal.*
import net.torvald.terrarum.virtualcomputer.worldobject.ComputerPartsCodex
import net.torvald.terrarum.virtualcomputer.worldobject.FixtureComputerBase
import org.newdawn.slick.GameContainer
import java.io.*
@@ -22,7 +25,9 @@ import java.io.*
* @param term : terminal that is connected to the computer fixtures, null if not connected any.
* Created by minjaesong on 16-09-10.
*/
class BaseTerrarumComputer(term: Teletype? = null) {
class BaseTerrarumComputer(val term: Teletype? = null) {
val DEBUG_UNLIMITED_MEM = false
val luaJ_globals: Globals = JsePlatform.standardGlobals()
@@ -33,6 +38,20 @@ class BaseTerrarumComputer(term: Teletype? = null) {
var termIn: InputStream? = null
private set
val processorCycle: Int // number of Lua statement to process per tick (1/100 s)
get() = ComputerPartsCodex.getProcessorCycles(computerValue.getAsInt("processor") ?: 0)
val memSize: Int // max: 8 GB
get() {
if (DEBUG_UNLIMITED_MEM) return 1.shl(30)// 1 GB
var size = 0
for (i in 0..3)
size += ComputerPartsCodex.getRamSize(computerValue.getAsInt("memSlot$i") ?: 0)
return 16.shl(20)
return size
}
val UUID = java.util.UUID.randomUUID().toString()
val computerValue = KVHashMap()
@@ -40,7 +59,7 @@ class BaseTerrarumComputer(term: Teletype? = null) {
var isHalted = false
init {
computerValue["memslot0"] = -1 // -1 indicates mem slot is empty
computerValue["memslot0"] = 4864 // -1 indicates mem slot is empty
computerValue["memslot1"] = -1 // put index of item here
computerValue["memslot2"] = -1 // ditto.
computerValue["memslot3"] = -1 // do.
@@ -77,11 +96,15 @@ class BaseTerrarumComputer(term: Teletype? = null) {
Term(luaJ_globals, term)
Security(luaJ_globals)
Filesystem(luaJ_globals, this)
HostAccessProvider(luaJ_globals, this)
}
// ROM BASIC
val inputStream = javaClass.getResourceAsStream("/net/torvald/terrarum/virtualcomputer/assets/lua/ROMBASIC.lua")
runCommand(InputStreamReader(inputStream), "rombasic")
val inputStream = javaClass.getResourceAsStream("/net/torvald/terrarum/virtualcomputer/assets/lua/BOOT.lua")
runCommand(InputStreamReader(inputStream), "=boot")
// computer-related global functions
luaJ_globals["getTotalMem"] = LuaFunGetTotalMem(this)
}
var threadTimer = 0
@@ -104,6 +127,10 @@ class BaseTerrarumComputer(term: Teletype? = null) {
}
}
fun keyPressed(key: Int, c: Char) {
}
var currentExecutionThread = Thread()
var threadRun = false
@@ -165,10 +192,14 @@ class BaseTerrarumComputer(term: Teletype? = null) {
lua.STDERR.println("${SimpleTextTerminal.ASCII_DLE}${e.message}${SimpleTextTerminal.ASCII_DC4}")
if (DEBUGTHRE) e.printStackTrace(System.err)
}
lua.load("_COMPUTER.prompt()").call()
}
val DEBUGTHRE = true
}
class LuaFunGetTotalMem(val computer: BaseTerrarumComputer) : ZeroArgFunction() {
override fun call(): LuaValue {
return LuaValue.valueOf(computer.memSize)
}
}
}

View File

@@ -24,25 +24,28 @@ internal class Filesystem(globals: Globals, computer: BaseTerrarumComputer) {
init {
// load things. WARNING: THIS IS MANUAL!
globals["fs"] = LuaValue.tableOf()
globals["fs"]["list"] = ListFiles(computer)
globals["fs"]["exists"] = FileExists(computer)
globals["fs"]["isDir"] = IsDirectory(computer)
globals["fs"]["list"] = ListFiles(computer) // CC compliant
globals["fs"]["exists"] = FileExists(computer) // CC/OC compliant
globals["fs"]["isDir"] = IsDirectory(computer) // CC compliant
globals["fs"]["isFile"] = IsFile(computer)
globals["fs"]["isReadOnly"] = IsReadOnly(computer)
globals["fs"]["getSize"] = GetSize(computer)
globals["fs"]["listFiles"] = ListFiles(computer)
globals["fs"]["isReadOnly"] = IsReadOnly(computer) // CC compliant
globals["fs"]["getSize"] = GetSize(computer) // CC compliant
globals["fs"]["mkdir"] = Mkdir(computer)
globals["fs"]["mv"] = Mv(computer)
globals["fs"]["cp"] = Cp(computer)
globals["fs"]["rm"] = Rm(computer)
globals["fs"]["concat"] = ConcatPath(computer)
globals["fs"]["open"] = OpenFile(computer)
globals["fs"]["concat"] = ConcatPath(computer) // OC compliant
globals["fs"]["open"] = OpenFile(computer) //CC compliant
globals["fs"]["parent"] = GetParentDir(computer)
globals["__haltsystemexplicit__"] = HaltComputer(computer)
// fs.run defined in ROMLIB
// fs.dofile defined in ROMLIB
}
companion object {
fun ensurePathSanity(path: LuaValue) {
if (path.checkIBM437().contains(Regex("""\.\.""")))
throw LuaError("'..' on path is not supported.")
}
val isCaseInsensitive: Boolean
get() {
// TODO add: force case insensitive in config
@@ -72,7 +75,7 @@ internal class Filesystem(globals: Globals, computer: BaseTerrarumComputer) {
fun isValidFilename(name: String) = !name.contains(invalidChars)
fun validatePath(path: String): String {
fun validatePath(path: String) : String {
if (!isValidFilename(path)) {
throw IOException("path contains invalid characters")
}
@@ -82,7 +85,7 @@ internal class Filesystem(globals: Globals, computer: BaseTerrarumComputer) {
/** actual directory: <appdata>/Saves/<savename>/computers/<drivename>/
* directs media/ directory to /<uuid> directory
*/
fun BaseTerrarumComputer.getRealPath(luapath: LuaValue): String {
fun BaseTerrarumComputer.getRealPath(luapath: LuaValue) : String {
// direct mounted paths to real path
val computerDir = Terrarum.currentSaveDir.absolutePath + "/computers/"
/* if not begins with "(/?)media/", direct to boot
@@ -96,7 +99,7 @@ internal class Filesystem(globals: Globals, computer: BaseTerrarumComputer) {
*/
// remove first '/' in path
var path = luapath.checkjstring()
var path = luapath.checkIBM437()
if (path.startsWith('/')) path = path.substring(1)
// replace '\' with '/'
path.replace('\\', '/')
@@ -111,7 +114,7 @@ internal class Filesystem(globals: Globals, computer: BaseTerrarumComputer) {
}
}
fun combinePath(base: String, local: String): String {
fun combinePath(base: String, local: String) : String {
return "$base$local".replace("//", "/").replace("\\\\", "\\")
}
}
@@ -121,8 +124,10 @@ internal class Filesystem(globals: Globals, computer: BaseTerrarumComputer) {
*
* actual directory: <appdata>/Saves/<savename>/computers/<drivename>/
*/
class ListFiles(val computer: BaseTerrarumComputer): OneArgFunction() {
override fun call(path: LuaValue): LuaValue {
class ListFiles(val computer: BaseTerrarumComputer) : OneArgFunction() {
override fun call(path: LuaValue) : LuaValue {
Filesystem.ensurePathSanity(path)
val table = LuaTable()
val file = File(computer.getRealPath(path))
file.list().forEachIndexed { i, s -> table.insert(i, LuaValue.valueOf(s)) }
@@ -130,33 +135,43 @@ internal class Filesystem(globals: Globals, computer: BaseTerrarumComputer) {
}
}
class FileExists(val computer: BaseTerrarumComputer): OneArgFunction() {
override fun call(path: LuaValue): LuaValue {
class FileExists(val computer: BaseTerrarumComputer) : OneArgFunction() {
override fun call(path: LuaValue) : LuaValue {
Filesystem.ensurePathSanity(path)
return LuaValue.valueOf(File(computer.getRealPath(path)).exists())
}
}
class IsDirectory(val computer: BaseTerrarumComputer): OneArgFunction() {
override fun call(path: LuaValue): LuaValue {
class IsDirectory(val computer: BaseTerrarumComputer) : OneArgFunction() {
override fun call(path: LuaValue) : LuaValue {
Filesystem.ensurePathSanity(path)
return LuaValue.valueOf(File(computer.getRealPath(path)).isDirectory)
}
}
class IsFile(val computer: BaseTerrarumComputer): OneArgFunction() {
override fun call(path: LuaValue): LuaValue {
class IsFile(val computer: BaseTerrarumComputer) : OneArgFunction() {
override fun call(path: LuaValue) : LuaValue {
Filesystem.ensurePathSanity(path)
return LuaValue.valueOf(File(computer.getRealPath(path)).isFile)
}
}
class IsReadOnly(val computer: BaseTerrarumComputer): OneArgFunction() {
override fun call(path: LuaValue): LuaValue {
class IsReadOnly(val computer: BaseTerrarumComputer) : OneArgFunction() {
override fun call(path: LuaValue) : LuaValue {
Filesystem.ensurePathSanity(path)
return LuaValue.valueOf(!File(computer.getRealPath(path)).canWrite())
}
}
/** we have 4GB file size limit */
class GetSize(val computer: BaseTerrarumComputer): OneArgFunction() {
override fun call(path: LuaValue): LuaValue {
class GetSize(val computer: BaseTerrarumComputer) : OneArgFunction() {
override fun call(path: LuaValue) : LuaValue {
Filesystem.ensurePathSanity(path)
return LuaValue.valueOf(File(computer.getRealPath(path)).length().toInt())
}
}
@@ -166,8 +181,10 @@ internal class Filesystem(globals: Globals, computer: BaseTerrarumComputer) {
/**
* difference with ComputerCraft: it returns boolean, true on successful.
*/
class Mkdir(val computer: BaseTerrarumComputer): OneArgFunction() {
override fun call(path: LuaValue): LuaValue {
class Mkdir(val computer: BaseTerrarumComputer) : OneArgFunction() {
override fun call(path: LuaValue) : LuaValue {
Filesystem.ensurePathSanity(path)
return LuaValue.valueOf(File(computer.getRealPath(path)).mkdir())
}
}
@@ -175,14 +192,17 @@ internal class Filesystem(globals: Globals, computer: BaseTerrarumComputer) {
/**
* moves a directory, overwrites the target
*/
class Mv(val computer: BaseTerrarumComputer): TwoArgFunction() {
override fun call(from: LuaValue, to: LuaValue): LuaValue {
class Mv(val computer: BaseTerrarumComputer) : TwoArgFunction() {
override fun call(from: LuaValue, to: LuaValue) : LuaValue {
Filesystem.ensurePathSanity(from)
Filesystem.ensurePathSanity(to)
val fromFile = File(computer.getRealPath(from))
fromFile.copyRecursively(
var success = fromFile.copyRecursively(
File(computer.getRealPath(to)), overwrite = true
)
fromFile.deleteRecursively()
return LuaValue.NONE
) // ignore IntelliJ's observation; it's opposite from redundant
success = fromFile.deleteRecursively()
return LuaValue.valueOf(success)
}
}
@@ -190,8 +210,11 @@ internal class Filesystem(globals: Globals, computer: BaseTerrarumComputer) {
* copies a directory, overwrites the target
* difference with ComputerCraft: it returns boolean, true on successful.
*/
class Cp(val computer: BaseTerrarumComputer): TwoArgFunction() {
override fun call(from: LuaValue, to: LuaValue): LuaValue {
class Cp(val computer: BaseTerrarumComputer) : TwoArgFunction() {
override fun call(from: LuaValue, to: LuaValue) : LuaValue {
Filesystem.ensurePathSanity(from)
Filesystem.ensurePathSanity(to)
return LuaValue.valueOf(
File(computer.getRealPath(from)).copyRecursively(
File(computer.getRealPath(to)), overwrite = true
@@ -203,17 +226,22 @@ internal class Filesystem(globals: Globals, computer: BaseTerrarumComputer) {
/**
* difference with ComputerCraft: it returns boolean, true on successful.
*/
class Rm(val computer: BaseTerrarumComputer): OneArgFunction() {
override fun call(path: LuaValue): LuaValue {
class Rm(val computer: BaseTerrarumComputer) : OneArgFunction() {
override fun call(path: LuaValue) : LuaValue {
Filesystem.ensurePathSanity(path)
return LuaValue.valueOf(
File(computer.getRealPath(path)).deleteRecursively()
)
}
}
class ConcatPath(val computer: BaseTerrarumComputer): TwoArgFunction() {
override fun call(base: LuaValue, local: LuaValue): LuaValue {
val combinedPath = combinePath(base.checkjstring(), local.checkjstring())
class ConcatPath(val computer: BaseTerrarumComputer) : TwoArgFunction() {
override fun call(base: LuaValue, local: LuaValue) : LuaValue {
Filesystem.ensurePathSanity(base)
Filesystem.ensurePathSanity(local)
val combinedPath = combinePath(base.checkIBM437(), local.checkIBM437())
return LuaValue.valueOf(combinedPath)
}
}
@@ -244,9 +272,13 @@ internal class Filesystem(globals: Globals, computer: BaseTerrarumComputer) {
* writeBytes = function(string as bytearray)
* }
*/
class OpenFile(val computer: BaseTerrarumComputer): TwoArgFunction() {
override fun call(path: LuaValue, mode: LuaValue): LuaValue {
val mode = mode.checkjstring().toLowerCase()
class OpenFile(val computer: BaseTerrarumComputer) : TwoArgFunction() {
override fun call(path: LuaValue, mode: LuaValue) : LuaValue {
Filesystem.ensurePathSanity(path)
val mode = mode.checkIBM437().toLowerCase()
val luaClass = LuaTable()
val file = File(computer.getRealPath(path))
@@ -255,6 +287,9 @@ internal class Filesystem(globals: Globals, computer: BaseTerrarumComputer) {
"${if (mode.startsWith('w')) "read" else "append"} mode" +
": is readonly.")
when (mode) {
"r" -> {
try {
@@ -312,9 +347,11 @@ internal class Filesystem(globals: Globals, computer: BaseTerrarumComputer) {
}
}
class GetParentDir(val computer: BaseTerrarumComputer): OneArgFunction() {
override fun call(path: LuaValue): LuaValue {
var pathSB = StringBuilder(path.checkjstring())
class GetParentDir(val computer: BaseTerrarumComputer) : OneArgFunction() {
override fun call(path: LuaValue) : LuaValue {
Filesystem.ensurePathSanity(path)
var pathSB = StringBuilder(path.checkIBM437())
// backward travel, drop chars until '/' has encountered
while (!pathSB.endsWith('/') && !pathSB.endsWith('\\'))
@@ -328,22 +365,13 @@ internal class Filesystem(globals: Globals, computer: BaseTerrarumComputer) {
}
}
class HaltComputer(val computer: BaseTerrarumComputer): ZeroArgFunction() {
override fun call(): LuaValue {
computer.isHalted = true
computer.luaJ_globals.load("""print("system halted.")""", "=").call()
return LuaValue.NONE
}
}
//////////////////////////////
// OpenFile implementations //
//////////////////////////////
private class FileClassClose(val fo: Any): ZeroArgFunction() {
override fun call(): LuaValue {
private class FileClassClose(val fo: Any) : ZeroArgFunction() {
override fun call() : LuaValue {
if (fo is FileOutputStream)
fo.close()
else if (fo is FileWriter)
@@ -359,16 +387,16 @@ internal class Filesystem(globals: Globals, computer: BaseTerrarumComputer) {
}
}
private class FileClassWriteByte(val fos: FileOutputStream): OneArgFunction() {
override fun call(byte: LuaValue): LuaValue {
private class FileClassWriteByte(val fos: FileOutputStream) : OneArgFunction() {
override fun call(byte: LuaValue) : LuaValue {
fos.write(byte.checkint())
return LuaValue.NONE
}
}
private class FileClassWriteBytes(val fos: FileOutputStream): OneArgFunction() {
override fun call(byteString: LuaValue): LuaValue {
private class FileClassWriteBytes(val fos: FileOutputStream) : OneArgFunction() {
override fun call(byteString: LuaValue) : LuaValue {
val byteString = byteString.checkIBM437()
val bytearr = ByteArray(byteString.length, { byteString[it].toByte() })
fos.write(bytearr)
@@ -377,24 +405,24 @@ internal class Filesystem(globals: Globals, computer: BaseTerrarumComputer) {
}
}
private class FileClassPrintText(val fw: FileWriter): OneArgFunction() {
override fun call(string: LuaValue): LuaValue {
private class FileClassPrintText(val fw: FileWriter) : OneArgFunction() {
override fun call(string: LuaValue) : LuaValue {
val text = string.checkIBM437()
fw.write(text)
return LuaValue.NONE
}
}
private class FileClassPrintlnText(val fw: FileWriter): OneArgFunction() {
override fun call(string: LuaValue): LuaValue {
private class FileClassPrintlnText(val fw: FileWriter) : OneArgFunction() {
override fun call(string: LuaValue) : LuaValue {
val text = string.checkIBM437() + "\n"
fw.write(text)
return LuaValue.NONE
}
}
private class FileClassFlush(val fo: Any): ZeroArgFunction() {
override fun call(): LuaValue {
private class FileClassFlush(val fo: Any) : ZeroArgFunction() {
override fun call() : LuaValue {
if (fo is FileOutputStream)
fo.flush()
else if (fo is FileWriter)
@@ -406,31 +434,31 @@ internal class Filesystem(globals: Globals, computer: BaseTerrarumComputer) {
}
}
private class FileClassReadByte(val fis: FileInputStream): ZeroArgFunction() {
override fun call(): LuaValue {
private class FileClassReadByte(val fis: FileInputStream) : ZeroArgFunction() {
override fun call() : LuaValue {
val readByte = fis.read()
return if (readByte == -1) LuaValue.NIL else LuaValue.valueOf(readByte)
}
}
private class FileClassReadAllBytes(val path: Path): ZeroArgFunction() {
override fun call(): LuaValue {
private class FileClassReadAllBytes(val path: Path) : ZeroArgFunction() {
override fun call() : LuaValue {
val byteArr = Files.readAllBytes(path)
val s: String = java.lang.String(byteArr, "IBM437").toString()
return LuaValue.valueOf(s)
}
}
private class FileClassReadAll(val path: Path): ZeroArgFunction() {
override fun call(): LuaValue {
private class FileClassReadAll(val path: Path) : ZeroArgFunction() {
override fun call() : LuaValue {
return FileClassReadAllBytes(path).call()
}
}
private class FileClassReadLine(val fr: FileReader): ZeroArgFunction() {
private class FileClassReadLine(val fr: FileReader) : ZeroArgFunction() {
val scanner = Scanner(fr.readText()) // no closing; keep the scanner status persistent
override fun call(): LuaValue {
override fun call() : LuaValue {
return if (scanner.hasNextLine()) LuaValue.valueOf(scanner.nextLine())
else LuaValue.NIL
}

View File

@@ -0,0 +1,92 @@
package net.torvald.terrarum.virtualcomputer.luaapi
import li.cil.repack.org.luaj.vm2.Globals
import li.cil.repack.org.luaj.vm2.LuaTable
import li.cil.repack.org.luaj.vm2.LuaValue
import li.cil.repack.org.luaj.vm2.lib.OneArgFunction
import li.cil.repack.org.luaj.vm2.lib.ZeroArgFunction
import net.torvald.terrarum.virtualcomputer.computer.BaseTerrarumComputer
import net.torvald.terrarum.virtualcomputer.luaapi.Term.Companion.checkIBM437
import net.torvald.terrarum.virtualcomputer.terminal.Teletype
/**
* Provide Lua an access to computer object that is in Java
*
* Created by minjaesong on 16-09-19.
*/
internal class HostAccessProvider(globals: Globals, computer: BaseTerrarumComputer) {
init {
globals["native"] = LuaTable()
globals["native"]["println"] = PrintLn()
globals["native"]["isHalted"] = IsHalted(computer)
globals["native"]["closeInputString"] = NativeCloseInputString(computer.term!!)
globals["native"]["closeInputKey"] = NativeCloseInputKey(computer.term!!)
globals["native"]["openInput"] = NativeOpenInput(computer.term!!)
globals["native"]["getLastStreamInput"] = NativeGetLastStreamInput(computer.term!!)
globals["native"]["getLastKeyPress"] = NativeGetLastKeyPress(computer.term!!)
// while lua's dofile/require is fixiated to fs, this command allows
// libraries in JAR to be loaded.
//globals["native"]["loadBuiltInLib"] = NativeLoadBuiltInLib()
globals["__haltsystemexplicit__"] = HaltComputer(computer)
}
class PrintLn(): OneArgFunction() {
override fun call(p0: LuaValue): LuaValue {
println(p0.checkIBM437())
return LuaValue.NONE
}
}
class IsHalted(val computer: BaseTerrarumComputer): ZeroArgFunction() {
override fun call(): LuaValue {
return LuaValue.valueOf(computer.isHalted)
}
}
class NativeCloseInputString(val term: Teletype) : ZeroArgFunction() {
override fun call(): LuaValue {
term.closeInputString()
return LuaValue.NONE
}
}
class NativeCloseInputKey(val term: Teletype) : ZeroArgFunction() {
override fun call(): LuaValue {
//term.closeInputKey()
return LuaValue.NONE
}
}
class NativeOpenInput(val term: Teletype) : ZeroArgFunction() {
override fun call(): LuaValue {
term.openInput()
return LuaValue.NONE
}
}
class NativeGetLastStreamInput(val term: Teletype) : ZeroArgFunction() {
override fun call(): LuaValue {
return if (term.lastStreamInput == null) LuaValue.NIL
else LuaValue.valueOf(term.lastStreamInput)
}
}
class NativeGetLastKeyPress(val term: Teletype) : ZeroArgFunction() {
override fun call(): LuaValue {
return if (term.lastKeyPress == null) LuaValue.NIL
else LuaValue.valueOf(term.lastKeyPress!!)
}
}
class HaltComputer(val computer: BaseTerrarumComputer) : ZeroArgFunction() {
override fun call() : LuaValue {
computer.isHalted = true
computer.luaJ_globals.load("""print("system halted")""").call()
return LuaValue.NONE
}
}
}

View File

@@ -32,7 +32,6 @@ internal class Term(globals: Globals, term: Teletype) {
globals["term"]["resetColour"] = Term.ResetColour(term)
globals["term"]["clear"] = Term.Clear(term)
globals["term"]["clearLine"] = Term.ClearLine(term)
globals["term"]["moveCursor"] = Term.SetCursorPos(term)
globals["term"]["getCursor"] = Term.GetCursorPos(term)
globals["term"]["getX"] = Term.GetCursorX(term)
globals["term"]["getY"] = Term.GetCursorY(term)
@@ -141,10 +140,10 @@ internal class Term(globals: Globals, term: Teletype) {
}
}
/** term.setCursorPos(number x, number y) */
/** term.setCursorPos(number x, number y), One-based */
class SetCursorPos(val term: Terminal) : TwoArgFunction() {
override fun call(x: LuaValue, y: LuaValue): LuaValue {
term.setCursor(x.checkint(), y.checkint())
term.setCursor(x.checkint() - 1, y.checkint() - 1)
return LuaValue.NONE
}
}
@@ -158,22 +157,23 @@ internal class Term(globals: Globals, term: Teletype) {
}
}
/** One-based */
class GetCursorPos(val term: Terminal) : VarArgFunction() {
override fun invoke(args: Varargs?): Varargs {
val ret = arrayOf(LuaValue.valueOf(term.cursorX), LuaValue.valueOf(term.cursorY))
val ret = arrayOf(LuaValue.valueOf(term.cursorX + 1), LuaValue.valueOf(term.cursorY + 1))
return LuaValue.varargsOf(ret)
}
}
class GetCursorX(val term: Terminal) : ZeroArgFunction() {
override fun call(): LuaValue {
return LuaValue.valueOf(term.cursorX)
return LuaValue.valueOf(term.cursorX + 1)
}
}
class GetCursorY(val term: Terminal) : ZeroArgFunction() {
override fun call(): LuaValue {
return LuaValue.valueOf(term.cursorY)
return LuaValue.valueOf(term.cursorY + 1)
}
}

View File

@@ -29,7 +29,7 @@ open class SimpleTextTerminal(
Color(0x00, 0x00, 0x00), // 0 black
Color(0xff, 0xff, 0xff), // 1 white
Color(0x55, 0x55, 0x55), // 2 dim grey
Color(0xaa, 0xaa, 0xaa), // 3 light grey
Color(0xaa, 0xaa, 0xaa), // 3 bright grey
Color(0xff, 0xff, 0x00), // 4 yellow
Color(0xff, 0x66, 0x00), // 5 orange
@@ -39,7 +39,7 @@ open class SimpleTextTerminal(
Color(0x33, 0x00, 0x99), // 8 purple
Color(0x00, 0x00, 0xcc), // 9 blue
Color(0x00, 0x99, 0xff), //10 cyan
Color(0x66, 0xff, 0x33), //11 lime
Color(0x55, 0xff, 0x00), //11 lime
Color(0x00, 0xaa, 0x00), //12 green
Color(0x00, 0x66, 0x00), //13 dark green
@@ -83,7 +83,7 @@ open class SimpleTextTerminal(
override val displayH = fontH * height
private val TABSIZE = 4
var TABSIZE = 4
private var cursorBlinkTimer = 0
private val cursorBlinkLen = 250
@@ -185,6 +185,7 @@ open class SimpleTextTerminal(
}
/** Unlike lua function, this one in Zero-based. */
override fun setCursor(x: Int, y: Int) {
cursorX = x
cursorY = y
@@ -387,18 +388,31 @@ open class SimpleTextTerminal(
/**
* Technically, this is different from Java's InputStream
*/
fun openInput() {
override fun openInput() {
lastStreamInput = null
lastKeyPress = null
inputOpen = true
if (DEBUG) println("[SimpleTextTerminal] openInput()")
}
fun closeInput(): String {
override fun closeInputKey(keyFromUI: Int): Int {
inputOpen = false
val ret = sb.toString()
lastKeyPress = keyFromUI
if (DEBUG) println("[SimpleTextTerminal] closeInputKey(), $keyFromUI")
return keyFromUI
}
override var lastStreamInput: String? = null
override var lastKeyPress: Int? = null
override fun closeInputString(): String {
inputOpen = false
lastStreamInput = sb.toString()
sb = StringBuilder()
if (DEBUG) println("[SimpleTextTerminal] closeInput(), $ret")
return ret
if (DEBUG) println("[SimpleTextTerminal] closeInputString(), $lastStreamInput")
return lastStreamInput!!
}
override fun keyPressed(key: Int, c: Char) {

View File

@@ -34,5 +34,12 @@ interface Teletype {
fun newLine()
fun scroll(amount: Int = 1)
fun openInput()
fun closeInputKey(keyFromUI: Int): Int
fun closeInputString(): String
fun bell(pattern: String = ".")
var lastStreamInput: String?
var lastKeyPress: Int?
}

View File

@@ -134,18 +134,31 @@ class TeletypeTerminal : Teletype {
/**
* Technically, this is different from Java's InputStream
*/
fun openInput() {
override fun openInput() {
lastStreamInput = null
lastKeyPress = null
inputOpen = true
if (DEBUG) println("[SimpleTextTerminal] openInput()")
if (DEBUG) println("[TeletypeTerminal] openInput()")
}
fun closeInput(): String {
override fun closeInputKey(keyFromUI: Int): Int {
inputOpen = false
val ret = sb.toString()
lastKeyPress = keyFromUI
if (DEBUG) println("[TeletypeTerminal] closeInputKey(), $keyFromUI")
return keyFromUI
}
override var lastStreamInput: String? = null
override var lastKeyPress: Int? = null
override fun closeInputString(): String {
inputOpen = false
lastStreamInput = sb.toString()
sb = StringBuilder()
if (DEBUG) println("[SimpleTextTerminal] closeInput(), $ret")
return ret
if (DEBUG) println("[TeletypeTerminal] closeInputString(), $lastStreamInput")
return lastStreamInput!!
}
override fun keyPressed(key: Int, c: Char) {

View File

@@ -1,170 +0,0 @@
package net.torvald.terrarum.virtualcomputer.terminal;
import li.cil.repack.com.naef.jnlua.LuaException;
import li.cil.repack.com.naef.jnlua.LuaRuntimeException;
import li.cil.repack.com.naef.jnlua.LuaState;
import java.io.*;
/**
* Created by minjaesong on 16-09-10.
*/
public class TerminalLuaConsole {
// -- Static
private static final String[] EMPTY_ARGS = new String[0];
/**
* Main routine.
*
* @param args
* the command line arguments
*/
public static void main(String[] args) {
LuaConsole luaConsole = new LuaConsole(args);
luaConsole.run();
System.exit(0);
}
private PrintStream out;
// -- State
private LuaState luaState;
// -- Construction
/**
* Creates a new instance.
*/
public TerminalLuaConsole(Terminal term) {
this(EMPTY_ARGS, term);
}
/**
* Creates a new instance with the specified command line arguments. The
* arguments are passed to Lua as the <code>argv</code> global variable.
*
* @param args
*/
public TerminalLuaConsole(String[] args, Terminal term) {
out = new TerminalPrintStream(term);
luaState = new LuaState();
// Process arguments
luaState.newTable(args.length, 0);
for (int i = 0; i < args.length; i++) {
luaState.pushString(args[i]);
luaState.rawSet(-2, i + 1);
}
luaState.setGlobal("argv");
// Open standard libraries
luaState.openLibs();
// Set buffer mode
luaState.load("io.stdout:setvbuf(\"no\")", "=consoleInitStdout");
luaState.call(0, 0);
luaState.load("io.stderr:setvbuf(\"no\")", "=consoleInitStderr");
luaState.call(0, 0);
}
// -- Properties
/**
* Returns the Lua state of this console.
*
* @return the Lua state
*/
public LuaState getLuaState() {
return luaState;
}
// -- Operations
/**
* Runs the console.
*/
public void run() {
// Banner
out.println(String.format("JNLua %s Console using Lua %s.",
LuaState.VERSION, LuaState.LUA_VERSION));
out.print("Type 'go' on an empty line to evaluate a chunk. ");
out.println("Type =<expression> to print an expression.");
// Prepare reader
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(System.in));
try {
// Process chunks
chunk: while (true) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
OutputStreamWriter outWriter = new OutputStreamWriter(out,
"UTF-8");
boolean firstLine = true;
// Process lines
while (true) {
String line = bufferedReader.readLine();
if (line == null) {
break chunk;
}
if (line.equals("go")) {
outWriter.flush();
InputStream in = new ByteArrayInputStream(out
.toByteArray());
runChunk(in);
continue chunk;
}
if (firstLine && line.startsWith("=")) {
outWriter.write("return " + line.substring(1));
outWriter.flush();
InputStream in = new ByteArrayInputStream(out
.toByteArray());
runChunk(in);
continue chunk;
}
outWriter.write(line);
outWriter.write('\n');
firstLine = false;
}
}
} catch (IOException e) {
out.print("IO error: ");
out.print(e.getMessage());
out.println();
}
}
/**
* Runs a chunk of Lua code from an input stream.
*/
protected void runChunk(InputStream in) throws IOException {
try {
long start = System.nanoTime();
luaState.setTop(0);
luaState.load(in, "=console", "t");
luaState.call(0, LuaState.MULTRET);
long stop = System.nanoTime();
for (int i = 1; i <= luaState.getTop(); i++) {
if (i > 1) {
out.print(", ");
}
switch (luaState.type(i)) {
case BOOLEAN:
out.print(Boolean.valueOf(luaState.toBoolean(i)));
break;
case NUMBER:
case STRING:
out.print(luaState.toString(i));
break;
default:
out.print(luaState.typeName(i));
}
}
out.print("\t#msec=");
out.print(String.format("%.3f", (stop - start) / 1000000.0));
out.println();
} catch (LuaRuntimeException e) {
e.printLuaStackTrace();
} catch (LuaException e) {
System.err.println(e.getMessage());
}
}
}

View File

@@ -14,14 +14,14 @@ object ComputerPartsCodex {
init {
// in kilobytes
rams.put(4864, 128.KiB()) // 64k is not enough for Lua, so we start here.
rams.put(4865, 192.KiB())
rams.put(4866, 256.KiB())
rams.put(4867, 320.KiB()) // 320 * 2 = "640k ought to be enough for anybody" --Someone that is NOT Bill Gates
rams.put(4868, 480.KiB())
rams.put(4869, 512.KiB())
rams.put(4870, 1024.KiB()) // server ops hate it
rams.put(4871, 2048.KiB()) // wait, we can multiplayer?
rams.put(4864, 16.MiB())
rams.put(4865, 24.MiB())
rams.put(4866, 32.MiB())
rams.put(4867, 64.MiB())
rams.put(4868, 96.MiB())
rams.put(4869, 128.MiB())
rams.put(4870, 160.MiB())
rams.put(4871, 256.MiB())
processors.put(4872, 1000)
processors.put(4873, 2000)
@@ -52,4 +52,5 @@ object ComputerPartsCodex {
private fun Int.MB() = this * 1000000 // 1 MB == 1 000 000 bytes, bitches!
private fun Int.kB() = this * 1000
private fun Int.KiB() = this.shr(10)
private fun Int.MiB() = this.shr(20)
}

View File

@@ -14,17 +14,6 @@ import java.util.*
*/
open class FixtureComputerBase() : FixtureBase() {
val processorCycle: Int // number of Lua statement to process per tick (1/100 s)
get() = ComputerPartsCodex.getProcessorCycles(computerInside!!.computerValue.getAsInt("processor") ?: -1)
val memSize: Int // max: 8 GB
get() {
var size = 0
for (i in 0..3)
size += ComputerPartsCodex.getRamSize(computerInside!!.computerValue.getAsInt("memSlot$i") ?: -1)
return size
}
/** Connected terminal */
var terminal: FixtureBasicTerminal? = null
@@ -70,6 +59,7 @@ open class FixtureComputerBase() : FixtureBase() {
fun keyPressed(key: Int, c: Char) {
if (terminal != null) {
terminal!!.vt.keyPressed(key, c)
computerInside!!.keyPressed(key, c)
}
}
}