mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-19 12:17:17 -04:00
things are slowly beginning to work again... it's still all a big mess, though
This commit is contained in:
parent
af286d17fa
commit
a7ec5c0ec0
@ -87,7 +87,7 @@ local function wrap(f)
|
|||||||
-- resume, so we get it via the yield. Thus: result = pcall(f, ...)
|
-- resume, so we get it via the yield. Thus: result = pcall(f, ...)
|
||||||
if result[1] then
|
if result[1] then
|
||||||
-- API call was successful, return the results.
|
-- API call was successful, return the results.
|
||||||
return select(2, table.unpack(result, 1, result.n))
|
return table.unpack(result, 2, result.n)
|
||||||
else
|
else
|
||||||
-- API call failed, re-throw the error.
|
-- API call failed, re-throw the error.
|
||||||
error(result[2], 2)
|
error(result[2], 2)
|
||||||
@ -95,5 +95,5 @@ local function wrap(f)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
sendToAddress = wrap(sendToAddress)
|
componentMethodsSynchronized = wrap(componentMethods)
|
||||||
nodeName = wrap(nodeName)
|
componentInvokeSynchronized = wrap(componentInvoke)
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
driver.command = {}
|
|
||||||
|
|
||||||
function driver.command.value(block, value)
|
|
||||||
checkArg(1, block, "string")
|
|
||||||
if value then
|
|
||||||
checkArg(2, value, "string")
|
|
||||||
return send(block, "command.value=", value)
|
|
||||||
else
|
|
||||||
return send(block, "command.value")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.command.run(block)
|
|
||||||
checkArg(1, block, "string")
|
|
||||||
return send(block, "command.run")
|
|
||||||
end
|
|
@ -1,365 +0,0 @@
|
|||||||
local mtab = {children={}}
|
|
||||||
|
|
||||||
local function segments(path)
|
|
||||||
path = path:gsub("\\", "/")
|
|
||||||
repeat local n; path, n = path:gsub("//", "/") until n == 0
|
|
||||||
local parts = {}
|
|
||||||
for part in path:gmatch("[^/]+") do
|
|
||||||
table.insert(parts, part)
|
|
||||||
end
|
|
||||||
local i = 1
|
|
||||||
while i <= #parts do
|
|
||||||
if parts[i] == "." then
|
|
||||||
table.remove(parts, i)
|
|
||||||
elseif parts[i] == ".." then
|
|
||||||
table.remove(parts, i)
|
|
||||||
i = i - 1
|
|
||||||
if i > 0 then
|
|
||||||
table.remove(parts, i)
|
|
||||||
else
|
|
||||||
i = 1
|
|
||||||
end
|
|
||||||
else
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return parts
|
|
||||||
end
|
|
||||||
|
|
||||||
local function findNode(path, create)
|
|
||||||
checkArg(1, path, "string")
|
|
||||||
local parts = segments(path)
|
|
||||||
local node = mtab
|
|
||||||
for i = 1, #parts do
|
|
||||||
if not node.children[parts[i]] then
|
|
||||||
if create then
|
|
||||||
node.children[parts[i]] = {children={}, parent=node}
|
|
||||||
else
|
|
||||||
return node, table.concat(parts, "/", i)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
node = node.children[parts[i]]
|
|
||||||
end
|
|
||||||
return node
|
|
||||||
end
|
|
||||||
|
|
||||||
local function removeEmptyNodes(node)
|
|
||||||
while node and node.parent and not node.fs and not next(node.children) do
|
|
||||||
for k, c in pairs(node.parent.children) do
|
|
||||||
if c == node then
|
|
||||||
node.parent.children[k] = nil
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
node = node.parent
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
driver.filesystem = {}
|
|
||||||
|
|
||||||
function driver.filesystem.canonical(path)
|
|
||||||
local result = table.concat(segments(path), "/")
|
|
||||||
if path:usub(1, 1) == "/" then
|
|
||||||
return "/" .. result
|
|
||||||
else
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.filesystem.concat(pathA, pathB)
|
|
||||||
return driver.filesystem.canonical(pathA .. "/" .. pathB)
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.filesystem.name(path)
|
|
||||||
local parts = segments(path)
|
|
||||||
return parts[#parts]
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.filesystem.mount(fs, path)
|
|
||||||
if fs and path then
|
|
||||||
checkArg(1, fs, "string")
|
|
||||||
local node = findNode(path, true)
|
|
||||||
if node.fs then
|
|
||||||
return nil, "another filesystem is already mounted here"
|
|
||||||
end
|
|
||||||
node.fs = fs
|
|
||||||
else
|
|
||||||
local function path(node)
|
|
||||||
local result = "/"
|
|
||||||
while node and node.parent do
|
|
||||||
for name, child in pairs(node.parent.children) do
|
|
||||||
if child == node then
|
|
||||||
result = "/" .. name .. result
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
node = node.parent
|
|
||||||
end
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
local queue = {mtab}
|
|
||||||
return function()
|
|
||||||
if #queue == 0 then
|
|
||||||
return nil
|
|
||||||
else
|
|
||||||
while true do
|
|
||||||
local node = table.remove(queue)
|
|
||||||
for _, child in pairs(node.children) do
|
|
||||||
table.insert(queue, child)
|
|
||||||
end
|
|
||||||
if node.fs then
|
|
||||||
return node.fs, path(node)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.filesystem.umount(fsOrPath)
|
|
||||||
local node, rest = findNode(fsOrPath)
|
|
||||||
if not rest and node.fs then
|
|
||||||
node.fs = nil
|
|
||||||
removeEmptyNodes(node)
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
local queue = {mtab}
|
|
||||||
for fs, path in driver.filesystem.mount() do
|
|
||||||
if fs == fsOrPath then
|
|
||||||
local node = findNode(path)
|
|
||||||
node.fs = nil
|
|
||||||
removeEmptyNodes(node)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function driver.filesystem.label(fs, label)
|
|
||||||
if type(label) == "string" then
|
|
||||||
return send(fs, "fs.label=", label)
|
|
||||||
end
|
|
||||||
return send(fs, "fs.label")
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function driver.filesystem.spaceTotal(path)
|
|
||||||
local node, rest = findNode(path)
|
|
||||||
if node.fs then
|
|
||||||
return send(node.fs, "fs.spaceTotal")
|
|
||||||
else
|
|
||||||
return nil, "no such device"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.filesystem.spaceUsed(path)
|
|
||||||
local node, rest = findNode(path)
|
|
||||||
if node.fs then
|
|
||||||
return send(node.fs, "fs.spaceUsed")
|
|
||||||
else
|
|
||||||
return nil, "no such device"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function driver.filesystem.exists(path)
|
|
||||||
local node, rest = findNode(path)
|
|
||||||
if not rest then -- virtual directory
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
if node.fs then
|
|
||||||
return send(node.fs, "fs.exists", rest)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.filesystem.size(path)
|
|
||||||
local node, rest = findNode(path)
|
|
||||||
if node.fs and rest then
|
|
||||||
return send(node.fs, "fs.size", rest)
|
|
||||||
end
|
|
||||||
return 0 -- no such file or directory or it's a virtual directory
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.filesystem.isDirectory(path)
|
|
||||||
local node, rest = findNode(path)
|
|
||||||
if node.fs and rest then
|
|
||||||
return send(node.fs, "fs.isDirectory", rest)
|
|
||||||
else
|
|
||||||
return not rest or rest:ulen() == 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.filesystem.lastModified(path)
|
|
||||||
local node, rest = findNode(path)
|
|
||||||
if node.fs and rest then
|
|
||||||
return send(node.fs, "fs.lastModified", rest)
|
|
||||||
end
|
|
||||||
return 0 -- no such file or directory or it's a virtual directory
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.filesystem.dir(path)
|
|
||||||
local node, rest = findNode(path)
|
|
||||||
if not node.fs and rest then
|
|
||||||
return nil, "no such file or directory"
|
|
||||||
end
|
|
||||||
local result
|
|
||||||
if node.fs then
|
|
||||||
result = table.pack(send(node.fs, "fs.dir", rest or ""))
|
|
||||||
if not result[1] and result[2] then
|
|
||||||
return nil, result[2]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
result = {}
|
|
||||||
end
|
|
||||||
if not rest then
|
|
||||||
for k, _ in pairs(node.children) do
|
|
||||||
table.insert(result, k .. "/")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
table.sort(result)
|
|
||||||
local i = 0
|
|
||||||
return function()
|
|
||||||
i = i + 1
|
|
||||||
return result[i]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function driver.filesystem.makeDirectory(path)
|
|
||||||
local node, rest = findNode(path)
|
|
||||||
if node.fs and rest then
|
|
||||||
return send(node.fs, "fs.makeDirectory", rest)
|
|
||||||
end
|
|
||||||
return nil, "cannot create a directory in a virtual directory"
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.filesystem.remove(path)
|
|
||||||
local node, rest = findNode(path)
|
|
||||||
if node.fs and rest then
|
|
||||||
return send(node.fs, "fs.remove", rest)
|
|
||||||
end
|
|
||||||
return nil, "no such non-virtual directory"
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.filesystem.rename(oldPath, newPath)
|
|
||||||
local oldNode, oldRest = findNode(oldPath)
|
|
||||||
local newNode, newRest = findNode(newPath)
|
|
||||||
if oldNode.fs and oldRest and newNode.fs and newRest then
|
|
||||||
if oldNode.fs == newNode.fs then
|
|
||||||
return send(oldNode.fs, "fs.rename", oldRest, newRest)
|
|
||||||
else
|
|
||||||
local result, reason = driver.filesystem.copy(oldPath, newPath)
|
|
||||||
if result then
|
|
||||||
return driver.filesystem.remove(oldPath)
|
|
||||||
else
|
|
||||||
return nil, reason
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return nil, "trying to read from or write to virtual directory"
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.filesystem.copy(fromPath, toPath)
|
|
||||||
local input, reason = io.open(fromPath, "rb")
|
|
||||||
if not input then
|
|
||||||
error(reason)
|
|
||||||
end
|
|
||||||
local output, reason = io.open(toPath, "wb")
|
|
||||||
if not output then
|
|
||||||
input:close()
|
|
||||||
error(reason)
|
|
||||||
end
|
|
||||||
repeat
|
|
||||||
local buffer, reason = input:read(1024)
|
|
||||||
if not buffer and reason then
|
|
||||||
error(reason)
|
|
||||||
elseif buffer then
|
|
||||||
local result, reason = output:write(buffer)
|
|
||||||
if not result then
|
|
||||||
input:close()
|
|
||||||
output:close()
|
|
||||||
error(reason)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
until not buffer
|
|
||||||
input:close()
|
|
||||||
output:close()
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
local fileStream = {}
|
|
||||||
|
|
||||||
function fileStream:close()
|
|
||||||
send(self.fs, "fs.close", self.handle)
|
|
||||||
self.handle = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
function fileStream:read(n)
|
|
||||||
if not self.handle then
|
|
||||||
return nil, "file is closed"
|
|
||||||
end
|
|
||||||
return send(self.fs, "fs.read", self.handle, n)
|
|
||||||
end
|
|
||||||
|
|
||||||
function fileStream:seek(whence, offset)
|
|
||||||
if not self.handle then
|
|
||||||
return nil, "file is closed"
|
|
||||||
end
|
|
||||||
return send(self.fs, "fs.seek", self.handle, whence, offset)
|
|
||||||
end
|
|
||||||
|
|
||||||
function fileStream:write(str)
|
|
||||||
if not self.handle then
|
|
||||||
return nil, "file is closed"
|
|
||||||
end
|
|
||||||
return send(self.fs, "fs.write", self.handle, str)
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function driver.filesystem.open(path, mode)
|
|
||||||
mode = tostring(mode or "r")
|
|
||||||
checkArg(2, mode, "string")
|
|
||||||
assert(({r=true, rb=true, w=true, wb=true, a=true, ab=true})[mode],
|
|
||||||
"bad argument #2 (r[b], w[b] or a[b] expected, got " .. mode .. ")")
|
|
||||||
|
|
||||||
local node, rest = findNode(path)
|
|
||||||
if not node.fs or not rest then
|
|
||||||
return nil, "file not found"
|
|
||||||
end
|
|
||||||
|
|
||||||
local handle, reason = send(node.fs, "fs.open", rest, mode)
|
|
||||||
if not handle then
|
|
||||||
return nil, reason
|
|
||||||
end
|
|
||||||
|
|
||||||
local stream = {fs = node.fs, handle = handle}
|
|
||||||
|
|
||||||
-- stream:close does a syscall, which yields, and that's not possible in
|
|
||||||
-- the __gc metamethod. So we start a timer to do the yield/cleanup.
|
|
||||||
local function cleanup(self)
|
|
||||||
if not self.handle then return end
|
|
||||||
-- save non-gc'ed values as upvalues
|
|
||||||
local fs = self.fs
|
|
||||||
local handle = self.handle
|
|
||||||
local function close()
|
|
||||||
send(fs, "fs.close", handle)
|
|
||||||
end
|
|
||||||
event.timer(0, close)
|
|
||||||
end
|
|
||||||
local metatable = {__index = fileStream,
|
|
||||||
__gc = cleanup,
|
|
||||||
__metatable = "filestream"}
|
|
||||||
return setmetatable(stream, metatable)
|
|
||||||
end
|
|
@ -1,52 +0,0 @@
|
|||||||
driver.gpu = {}
|
|
||||||
|
|
||||||
function driver.gpu.bind(gpu, screen)
|
|
||||||
checkArg(1, gpu, "string")
|
|
||||||
checkArg(2, screen, "string")
|
|
||||||
return send(gpu, "gpu.bind", screen)
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.gpu.resolution(gpu, w, h)
|
|
||||||
checkArg(1, gpu, "string")
|
|
||||||
if w and h then
|
|
||||||
checkArg(2, w, "number")
|
|
||||||
checkArg(3, h, "number")
|
|
||||||
return send(gpu, "gpu.resolution=", w, h)
|
|
||||||
else
|
|
||||||
return send(gpu, "gpu.resolution")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.gpu.maxResolution(gpu)
|
|
||||||
checkArg(1, gpu, "string")
|
|
||||||
return send(gpu, "gpu.maxResolution")
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.gpu.set(gpu, col, row, value)
|
|
||||||
checkArg(1, gpu, "string")
|
|
||||||
checkArg(2, col, "number")
|
|
||||||
checkArg(3, row, "number")
|
|
||||||
checkArg(4, value, "string")
|
|
||||||
return send(gpu, "gpu.set", col, row, value)
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.gpu.fill(gpu, col, row, w, h, value)
|
|
||||||
checkArg(1, gpu, "string")
|
|
||||||
checkArg(2, col, "number")
|
|
||||||
checkArg(3, row, "number")
|
|
||||||
checkArg(4, w, "number")
|
|
||||||
checkArg(5, h, "number")
|
|
||||||
checkArg(6, value, "string")
|
|
||||||
return send(gpu, "gpu.fill", col, row, w, h, value:usub(1, 1))
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.gpu.copy(gpu, col, row, w, h, tx, ty)
|
|
||||||
checkArg(1, gpu, "string")
|
|
||||||
checkArg(2, col, "number")
|
|
||||||
checkArg(3, row, "number")
|
|
||||||
checkArg(4, w, "number")
|
|
||||||
checkArg(5, h, "number")
|
|
||||||
checkArg(6, tx, "number")
|
|
||||||
checkArg(7, ty, "number")
|
|
||||||
return send(gpu, "gpu.copy", col, row, w, h, tx, ty)
|
|
||||||
end
|
|
@ -1,140 +0,0 @@
|
|||||||
driver.keyboard = {}
|
|
||||||
|
|
||||||
driver.keyboard.keys = {
|
|
||||||
["1"] = 0x02,
|
|
||||||
["2"] = 0x03,
|
|
||||||
["3"] = 0x04,
|
|
||||||
["4"] = 0x05,
|
|
||||||
["5"] = 0x06,
|
|
||||||
["6"] = 0x07,
|
|
||||||
["7"] = 0x08,
|
|
||||||
["8"] = 0x09,
|
|
||||||
["9"] = 0x0A,
|
|
||||||
["0"] = 0x0B,
|
|
||||||
a = 0x1E,
|
|
||||||
b = 0x30,
|
|
||||||
c = 0x2E,
|
|
||||||
d = 0x20,
|
|
||||||
e = 0x12,
|
|
||||||
f = 0x21,
|
|
||||||
g = 0x22,
|
|
||||||
h = 0x23,
|
|
||||||
i = 0x17,
|
|
||||||
j = 0x24,
|
|
||||||
k = 0x25,
|
|
||||||
l = 0x26,
|
|
||||||
m = 0x32,
|
|
||||||
n = 0x31,
|
|
||||||
o = 0x18,
|
|
||||||
p = 0x19,
|
|
||||||
q = 0x10,
|
|
||||||
r = 0x13,
|
|
||||||
s = 0x1F,
|
|
||||||
t = 0x14,
|
|
||||||
u = 0x16,
|
|
||||||
v = 0x2F,
|
|
||||||
w = 0x11,
|
|
||||||
x = 0x2D,
|
|
||||||
y = 0x15,
|
|
||||||
z = 0x2C,
|
|
||||||
|
|
||||||
apostrophe = 0x28,
|
|
||||||
at = 0x91,
|
|
||||||
back = 0x0E, -- backspace
|
|
||||||
backslash = 0x2B,
|
|
||||||
colon = 0x92,
|
|
||||||
comma = 0x33,
|
|
||||||
enter = 0x1C,
|
|
||||||
equals = 0x0D,
|
|
||||||
grave = 0x29, -- accent grave
|
|
||||||
lbracket = 0x1A,
|
|
||||||
lcontrol = 0x1D,
|
|
||||||
lmenu = 0x38, -- left Alt
|
|
||||||
lshift = 0x2A,
|
|
||||||
minus = 0x0C,
|
|
||||||
numlock = 0x45,
|
|
||||||
pause = 0xC5,
|
|
||||||
period = 0x34,
|
|
||||||
rbracket = 0x1B,
|
|
||||||
rcontrol = 0x9D,
|
|
||||||
rmenu = 0xB8, -- right Alt
|
|
||||||
rshift = 0x36,
|
|
||||||
scroll = 0x46, -- Scroll Lock
|
|
||||||
semicolon = 0x27,
|
|
||||||
slash = 0x35, -- / on main keyboard
|
|
||||||
space = 0x39,
|
|
||||||
stop = 0x95,
|
|
||||||
tab = 0x0F,
|
|
||||||
underline = 0x93,
|
|
||||||
|
|
||||||
-- Keypad (and numpad with numlock off)
|
|
||||||
up = 0xC8,
|
|
||||||
down = 0xD0,
|
|
||||||
left = 0xCB,
|
|
||||||
right = 0xCD,
|
|
||||||
home = 0xC7,
|
|
||||||
["end"] = 0xCF,
|
|
||||||
pageUp = 0xC9,
|
|
||||||
pageDown = 0xD1,
|
|
||||||
insert = 0xD2,
|
|
||||||
delete = 0xD3,
|
|
||||||
|
|
||||||
-- Function keys
|
|
||||||
f1 = 0x3B,
|
|
||||||
f2 = 0x3C,
|
|
||||||
f3 = 0x3D,
|
|
||||||
f4 = 0x3E,
|
|
||||||
f5 = 0x3F,
|
|
||||||
f6 = 0x40,
|
|
||||||
f7 = 0x41,
|
|
||||||
f8 = 0x42,
|
|
||||||
f9 = 0x43,
|
|
||||||
f10 = 0x44,
|
|
||||||
f11 = 0x57,
|
|
||||||
f12 = 0x58,
|
|
||||||
f13 = 0x64,
|
|
||||||
f14 = 0x65,
|
|
||||||
f15 = 0x66,
|
|
||||||
f16 = 0x67,
|
|
||||||
f17 = 0x68,
|
|
||||||
f18 = 0x69,
|
|
||||||
f19 = 0x71,
|
|
||||||
|
|
||||||
-- Japanese keyboards
|
|
||||||
kana = 0x70,
|
|
||||||
kanji = 0x94,
|
|
||||||
convert = 0x79,
|
|
||||||
noconvert = 0x7B,
|
|
||||||
yen = 0x7D,
|
|
||||||
circumflex = 0x90,
|
|
||||||
ax = 0x96,
|
|
||||||
|
|
||||||
-- Numpad
|
|
||||||
numpad0 = 0x52,
|
|
||||||
numpad1 = 0x4F,
|
|
||||||
numpad2 = 0x50,
|
|
||||||
numpad3 = 0x51,
|
|
||||||
numpad4 = 0x4B,
|
|
||||||
numpad5 = 0x4C,
|
|
||||||
numpad6 = 0x4D,
|
|
||||||
numpad7 = 0x47,
|
|
||||||
numpad8 = 0x48,
|
|
||||||
numpad9 = 0x49,
|
|
||||||
numpadmul = 0x37,
|
|
||||||
numpaddiv = 0xB5,
|
|
||||||
numpadsub = 0x4A,
|
|
||||||
numpadadd = 0x4E,
|
|
||||||
numpaddecimal = 0x53,
|
|
||||||
numpadcomma = 0xB3,
|
|
||||||
numpadenter = 0x9C,
|
|
||||||
numpadequals = 0x8D,
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Create inverse mapping for name lookup.
|
|
||||||
for k, v in pairs(driver.keyboard.keys) do
|
|
||||||
driver.keyboard.keys[v] = k
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.keyboard.isControl(char)
|
|
||||||
return type(char) == "number" and (char < 0x20 or (char >= 0x7F and char <= 0x9F))
|
|
||||||
end
|
|
@ -1,36 +0,0 @@
|
|||||||
driver.network = {}
|
|
||||||
|
|
||||||
function driver.network.open(card, port)
|
|
||||||
checkArg(1, card, "string")
|
|
||||||
checkArg(2, port, "number")
|
|
||||||
return send(card, "network.open=", port)
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.network.isOpen(card, port)
|
|
||||||
checkArg(1, card, "string")
|
|
||||||
checkArg(2, port, "number")
|
|
||||||
return send(card, "network.open", port)
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.network.close(card, port)
|
|
||||||
checkArg(1, card, "string")
|
|
||||||
if port then
|
|
||||||
checkArg(2, port, "number")
|
|
||||||
return send(card, "network.close", port)
|
|
||||||
else
|
|
||||||
return send(card, "network.close")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.network.send(card, target, port, ...)
|
|
||||||
checkArg(1, card, "string")
|
|
||||||
checkArg(2, target, "string")
|
|
||||||
checkArg(3, port, "number")
|
|
||||||
return send(card, "network.send", target, port, ...)
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.network.broadcast(card, port, ...)
|
|
||||||
checkArg(1, card, "string")
|
|
||||||
checkArg(2, port, "number")
|
|
||||||
return send(card, "network.broadcast", port, ...)
|
|
||||||
end
|
|
@ -1,35 +0,0 @@
|
|||||||
driver.redstone = {}
|
|
||||||
|
|
||||||
function driver.redstone.analogInput(card, side)
|
|
||||||
checkArg(1, card, "string")
|
|
||||||
checkArg(2, side, "number")
|
|
||||||
return send(card, "redstone.input", side)
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.redstone.analogOutput(card, side, value)
|
|
||||||
checkArg(1, card, "string")
|
|
||||||
checkArg(2, side, "number")
|
|
||||||
checkArg(3, value, "number", "nil")
|
|
||||||
if value then
|
|
||||||
return send(card, "redstone.output=", side, value)
|
|
||||||
else
|
|
||||||
return send(card, "redstone.output", side)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.redstone.input(card, side)
|
|
||||||
checkArg(1, card, "string")
|
|
||||||
checkArg(2, side, "number")
|
|
||||||
return driver.redstone.analogInput(card, side) > 0
|
|
||||||
end
|
|
||||||
|
|
||||||
function driver.redstone.output(card, side, value)
|
|
||||||
checkArg(1, card, "string")
|
|
||||||
checkArg(2, side, "number")
|
|
||||||
checkArg(3, value, "boolean", "nil")
|
|
||||||
if value ~= nil then
|
|
||||||
return driver.redstone.analogOutput(card, side, value and 15 or 0)
|
|
||||||
else
|
|
||||||
return driver.redstone.analogOutput(card, side) > 0
|
|
||||||
end
|
|
||||||
end
|
|
@ -236,92 +236,101 @@ end
|
|||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
sandbox.driver = {}
|
sandbox.component = {}
|
||||||
|
|
||||||
function sandbox.driver.componentType(address)
|
function sandbox.component.list(filter)
|
||||||
checkArg(1, address, "string")
|
checkArg(1, filter, "string", "nil")
|
||||||
return nodeName(address)
|
return pairs(componentList(filter))
|
||||||
end
|
end
|
||||||
|
|
||||||
do
|
function sandbox.component.type(address)
|
||||||
local function send(address, name, ...)
|
checkArg(1, address, "string")
|
||||||
checkArg(1, address, "string")
|
return componentType(address)
|
||||||
checkArg(2, name, "string")
|
end
|
||||||
return sendToAddress(address, name, ...)
|
|
||||||
|
function sandbox.component.invoke(address, method, ...)
|
||||||
|
checkArg(1, address, "string")
|
||||||
|
checkArg(2, method, "string")
|
||||||
|
return componentInvokeSynchronized(address, method, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function sandbox.component.proxy(address)
|
||||||
|
checkArg(1, address, "string")
|
||||||
|
local type, reason = componentType(address)
|
||||||
|
if not type then
|
||||||
|
return nil, reason
|
||||||
end
|
end
|
||||||
local env = setmetatable({send = send},
|
local proxy = {address = address, type = type}
|
||||||
{ __index = sandbox, __newindex = sandbox })
|
local methods = componentMethodsSynchronized(address)
|
||||||
for name, code in pairs(drivers()) do
|
if methods then
|
||||||
local driver, reason = load(code, "=" .. name, "t", env)
|
for _, method in ipairs(methods) do
|
||||||
if not driver then
|
proxy[method] = function(...)
|
||||||
print("Failed loading driver '" .. name .. "': " .. reason)
|
return componentInvokeSynchronized(address, method, ...)
|
||||||
else
|
|
||||||
local result, reason = xpcall(driver, function(msg)
|
|
||||||
return debug.traceback(msg, 2)
|
|
||||||
end)
|
|
||||||
if not result then
|
|
||||||
print("Failed initializing driver '" .. name .. "': " ..
|
|
||||||
(reason or "unknown error"))
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
return proxy
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
local function main(args)
|
local function main()
|
||||||
local function bootstrap()
|
local function bootstrap()
|
||||||
-- Prepare low-level print logic to give boot progress feedback.
|
-- Prepare low-level print logic to give boot progress feedback.
|
||||||
local gpus = gpus()
|
-- local gpus = gpus()
|
||||||
for i = 1, #gpus do
|
-- for i = 1, #gpus do
|
||||||
local gpu = gpus[i]
|
-- local gpu = gpus[i]
|
||||||
gpus[i] = nil
|
-- gpus[i] = nil
|
||||||
local w, h = sandbox.driver.gpu.resolution(gpu)
|
-- local w, h = sandbox.driver.gpu.resolution(gpu)
|
||||||
if w then
|
-- if w then
|
||||||
if sandbox.driver.gpu.fill(gpu, 1, 1, w, h, " ") then
|
-- if sandbox.driver.gpu.fill(gpu, 1, 1, w, h, " ") then
|
||||||
gpus[i] = {gpu, w, h}
|
-- gpus[i] = {gpu, w, h}
|
||||||
end
|
-- end
|
||||||
end
|
-- end
|
||||||
end
|
-- end
|
||||||
local l = 0
|
-- local l = 0
|
||||||
local function print(s)
|
-- local function print(s)
|
||||||
l = l + 1
|
-- l = l + 1
|
||||||
for _, gpu in pairs(gpus) do
|
-- for _, gpu in pairs(gpus) do
|
||||||
if l > gpu[3] then
|
-- if l > gpu[3] then
|
||||||
sandbox.driver.gpu.copy(gpu[1], 1, 1, gpu[2], gpu[3], 0, -1)
|
-- sandbox.driver.gpu.copy(gpu[1], 1, 1, gpu[2], gpu[3], 0, -1)
|
||||||
sandbox.driver.gpu.fill(gpu[1], 1, gpu[3], gpu[2], 1, " ")
|
-- sandbox.driver.gpu.fill(gpu[1], 1, gpu[3], gpu[2], 1, " ")
|
||||||
end
|
-- end
|
||||||
sandbox.driver.gpu.set(gpu[1], 1, math.min(l, gpu[3]), s)
|
-- sandbox.driver.gpu.set(gpu[1], 1, math.min(l, gpu[3]), s)
|
||||||
end
|
-- end
|
||||||
end
|
-- end
|
||||||
print("Booting...")
|
print("Booting...")
|
||||||
|
|
||||||
print("Mounting ROM and temporary file system.")
|
print("Mounting ROM and temporary file system.")
|
||||||
local fs = sandbox.driver.filesystem
|
local function rom(method, ...)
|
||||||
fs.mount(os.romAddress(), "/")
|
return componentInvoke(os.romAddress(), method, ...)
|
||||||
fs.mount(os.tmpAddress(), "/tmp")
|
end
|
||||||
|
|
||||||
-- Custom dofile implementation since we don't have the baselib yet.
|
-- Custom dofile implementation since we don't have the baselib yet.
|
||||||
local function dofile(file)
|
local function dofile(file)
|
||||||
print(" " .. file)
|
print(" " .. file)
|
||||||
local stream, reason = fs.open(file)
|
local handle, reason = rom("open", file)
|
||||||
if not stream then
|
if not handle then
|
||||||
error(reason)
|
error(reason)
|
||||||
end
|
end
|
||||||
if stream then
|
if handle then
|
||||||
local buffer = ""
|
local buffer = ""
|
||||||
repeat
|
repeat
|
||||||
local data = stream:read(math.huge)
|
local data = rom("read", handle, math.huge)
|
||||||
if data then
|
if data then
|
||||||
buffer = buffer .. data
|
buffer = buffer .. data
|
||||||
end
|
end
|
||||||
until not data
|
until not data
|
||||||
stream:close()
|
rom("close", handle)
|
||||||
stream = nil
|
local program, reason = load(buffer, "=" .. file, "t", sandbox)
|
||||||
local program, reason = sandbox.load(buffer, "=" .. file)
|
|
||||||
buffer = nil
|
buffer = nil
|
||||||
if program then
|
if program then
|
||||||
return program()
|
local result = table.pack(pcall(program))
|
||||||
|
if result[1] then
|
||||||
|
return table.unpack(result, 2, result.n)
|
||||||
|
else
|
||||||
|
error("error initializing lib '" .. file .. "': " .. result[2])
|
||||||
|
end
|
||||||
else
|
else
|
||||||
error("error loading lib '" .. file .. "': " .. reason)
|
error("error loading lib '" .. file .. "': " .. reason)
|
||||||
end
|
end
|
||||||
@ -330,9 +339,9 @@ local function main(args)
|
|||||||
|
|
||||||
print("Loading libraries.")
|
print("Loading libraries.")
|
||||||
local init = {}
|
local init = {}
|
||||||
for api in fs.dir("lib") do
|
for _, api in ipairs(rom("list", "lib")) do
|
||||||
local path = fs.concat("lib", api)
|
local path = "lib/" .. api
|
||||||
if not fs.isDirectory(path) then
|
if not rom("isDirectory", path) then
|
||||||
local install = dofile(path)
|
local install = dofile(path)
|
||||||
if type(install) == "function" then
|
if type(install) == "function" then
|
||||||
table.insert(init, install)
|
table.insert(init, install)
|
||||||
@ -347,14 +356,25 @@ local function main(args)
|
|||||||
init = nil
|
init = nil
|
||||||
|
|
||||||
print("Starting shell.")
|
print("Starting shell.")
|
||||||
return coroutine.create(function(...)
|
return coroutine.create(load([[
|
||||||
sandbox.event.fire(...) -- handle the first signal
|
fs.mount(os.romAddress(), "/")
|
||||||
while true do
|
fs.mount(os.tmpAddress(), "/tmp")
|
||||||
sandbox.os.execute("/bin/sh")
|
for c, t in component.list() do
|
||||||
|
event.fire("component_added", c, t)
|
||||||
end
|
end
|
||||||
end)
|
while true do
|
||||||
|
local result, reason = os.execute("/bin/sh")
|
||||||
|
if not result then
|
||||||
|
error(reason)
|
||||||
|
end
|
||||||
|
end]], "=init", "t", sandbox))
|
||||||
end
|
end
|
||||||
local co = bootstrap()
|
local co = bootstrap()
|
||||||
|
|
||||||
|
-- Yield once to allow initializing up to here to get a memory baseline.
|
||||||
|
assert(coroutine.yield() == "dummy")
|
||||||
|
|
||||||
|
local args = {n=0}
|
||||||
while true do
|
while true do
|
||||||
deadline = os.realTime() + timeout -- timeout global is set by host
|
deadline = os.realTime() + timeout -- timeout global is set by host
|
||||||
if not debug.gethook(co) then
|
if not debug.gethook(co) then
|
||||||
@ -362,7 +382,7 @@ local function main(args)
|
|||||||
end
|
end
|
||||||
local result = table.pack(coroutine.resume(co, table.unpack(args, 1, args.n)))
|
local result = table.pack(coroutine.resume(co, table.unpack(args, 1, args.n)))
|
||||||
if not result[1] then
|
if not result[1] then
|
||||||
error(result[2] or "unknown error", 0)
|
error(tostring(result[2]), 0)
|
||||||
elseif coroutine.status(co) == "dead" then
|
elseif coroutine.status(co) == "dead" then
|
||||||
error("computer stopped unexpectedly", 0)
|
error("computer stopped unexpectedly", 0)
|
||||||
else
|
else
|
||||||
@ -373,5 +393,4 @@ end
|
|||||||
|
|
||||||
-- JNLua converts the coroutine to a string immediately, so we can't get the
|
-- JNLua converts the coroutine to a string immediately, so we can't get the
|
||||||
-- traceback later. Because of that we have to do the error handling here.
|
-- traceback later. Because of that we have to do the error handling here.
|
||||||
-- Also, yield once to allow initializing up to here to get a memory baseline.
|
return pcall(main)
|
||||||
return pcall(main, table.pack(coroutine.yield()))
|
|
@ -9,7 +9,7 @@ for i = 1, #dirs do
|
|||||||
if i > 1 then print() end
|
if i > 1 then print() end
|
||||||
print("/" .. path .. ":")
|
print("/" .. path .. ":")
|
||||||
end
|
end
|
||||||
local list, reason = fs.dir(path)
|
local list, reason = fs.list(path)
|
||||||
if not list then
|
if not list then
|
||||||
print(reason)
|
print(reason)
|
||||||
else
|
else
|
||||||
|
@ -1,29 +1,18 @@
|
|||||||
local components = {}
|
|
||||||
local removing = {}
|
local removing = {}
|
||||||
local primaries = {}
|
local primaries = {}
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
component = {}
|
|
||||||
|
|
||||||
function component.isAvailable(componentType)
|
function component.isAvailable(componentType)
|
||||||
return primaries[componentType] ~= nil
|
return primaries[componentType] ~= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
function component.list(filter)
|
|
||||||
local address, ctype = nil
|
|
||||||
return function()
|
|
||||||
repeat
|
|
||||||
address, ctype = next(components, address)
|
|
||||||
until not address or type(filter) ~= "string" or ctype:match(filter)
|
|
||||||
return address, ctype
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function component.isPrimary(address)
|
function component.isPrimary(address)
|
||||||
local componentType = component.type(address)
|
local componentType = component.type(address)
|
||||||
if componentType then
|
if componentType then
|
||||||
return primaries[componentType] == address
|
if component.isAvailable(componentType) then
|
||||||
|
return primaries[componentType].address == address
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
@ -44,10 +33,10 @@ function component.primary(componentType, ...)
|
|||||||
assert(address, "no such component")
|
assert(address, "no such component")
|
||||||
end
|
end
|
||||||
local wasAvailable = component.isAvailable(componentType)
|
local wasAvailable = component.isAvailable(componentType)
|
||||||
primaries[componentType] = address
|
primaries[componentType] = component.proxy(address)
|
||||||
if not wasAvailable and component.isAvailable(componentType) then
|
if component.isAvailable(componentType) then
|
||||||
event.fire("component_available", componentType)
|
event.fire("component_available", componentType)
|
||||||
elseif wasAvailable and not component.isAvailable(componentType) then
|
elseif wasAvailable then
|
||||||
event.fire("component_unavailable", componentType)
|
event.fire("component_unavailable", componentType)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@ -57,41 +46,22 @@ function component.primary(componentType, ...)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function component.type(address)
|
|
||||||
return components[address]
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
local function onComponentAdded(_, address)
|
local function onComponentAdded(_, address, componentType)
|
||||||
if components[address] then
|
|
||||||
return false -- cancel this event, it is invalid
|
|
||||||
end
|
|
||||||
local componentType = driver.componentType(address)
|
|
||||||
if not componentType then
|
|
||||||
return -- component was removed again before signal could be processed
|
|
||||||
end
|
|
||||||
components[address] = componentType
|
|
||||||
if not component.isAvailable(componentType) then
|
if not component.isAvailable(componentType) then
|
||||||
component.primary(componentType, address)
|
component.primary(componentType, address)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function onComponentRemoved(_, address)
|
local function onComponentRemoved(_, address, componentType)
|
||||||
if removing[address] then return end
|
if component.isAvailable(componentType) and
|
||||||
if not components[address] then return false end
|
component.primary(componentType).address == address
|
||||||
local componentType = component.type(address)
|
then
|
||||||
components[address] = nil
|
|
||||||
-- Redispatch with component type, since we already removed.
|
|
||||||
removing[address] = true -- don't cancel this one!
|
|
||||||
event.fire("component_removed", address, componentType)
|
|
||||||
removing[address] = false
|
|
||||||
if primaries[componentType] == address then
|
|
||||||
component.primary(componentType, nil)
|
component.primary(componentType, nil)
|
||||||
address = component.list(componentType)()
|
address = component.list(componentType)()
|
||||||
component.primary(componentType, address)
|
component.primary(componentType, address)
|
||||||
end
|
end
|
||||||
return false -- cancel this one
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return function()
|
return function()
|
||||||
|
@ -45,13 +45,11 @@ function event.fire(name, ...)
|
|||||||
-- Copy the listener lists because they may be changed by callbacks.
|
-- Copy the listener lists because they may be changed by callbacks.
|
||||||
local listeners = copy(listenersFor(name, false), listenersFor(name, true))
|
local listeners = copy(listenersFor(name, false), listenersFor(name, true))
|
||||||
for _, callback in ipairs(listeners) do
|
for _, callback in ipairs(listeners) do
|
||||||
local result, message = pcall(callback, name, ...)
|
local result, message = xpcall(callback, debug.traceback, name, ...)
|
||||||
if not result then
|
if not result then
|
||||||
if not (event.error and pcall(event.error, message)) then
|
if not (event.error and pcall(event.error, message)) then
|
||||||
os.shutdown()
|
os.shutdown()
|
||||||
end
|
end
|
||||||
elseif result and message == false then
|
|
||||||
break
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,18 +1,153 @@
|
|||||||
local isAutorunEnabled = true
|
local isAutorunEnabled = true
|
||||||
|
local mtab = {children={}}
|
||||||
|
|
||||||
|
local function segments(path)
|
||||||
|
path = path:gsub("\\", "/")
|
||||||
|
repeat local n; path, n = path:gsub("//", "/") until n == 0
|
||||||
|
local parts = {}
|
||||||
|
for part in path:gmatch("[^/]+") do
|
||||||
|
table.insert(parts, part)
|
||||||
|
end
|
||||||
|
local i = 1
|
||||||
|
while i <= #parts do
|
||||||
|
if parts[i] == "." then
|
||||||
|
table.remove(parts, i)
|
||||||
|
elseif parts[i] == ".." then
|
||||||
|
table.remove(parts, i)
|
||||||
|
i = i - 1
|
||||||
|
if i > 0 then
|
||||||
|
table.remove(parts, i)
|
||||||
|
else
|
||||||
|
i = 1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return parts
|
||||||
|
end
|
||||||
|
|
||||||
|
local function findNode(path, create)
|
||||||
|
checkArg(1, path, "string")
|
||||||
|
local parts = segments(path)
|
||||||
|
local node = mtab
|
||||||
|
for i = 1, #parts do
|
||||||
|
if not node.children[parts[i]] then
|
||||||
|
if create then
|
||||||
|
node.children[parts[i]] = {children={}, parent=node}
|
||||||
|
else
|
||||||
|
return node, table.concat(parts, "/", i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
node = node.children[parts[i]]
|
||||||
|
end
|
||||||
|
return node
|
||||||
|
end
|
||||||
|
|
||||||
|
local function removeEmptyNodes(node)
|
||||||
|
while node and node.parent and not node.fs and not next(node.children) do
|
||||||
|
for k, c in pairs(node.parent.children) do
|
||||||
|
if c == node then
|
||||||
|
node.parent.children[k] = nil
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
node = node.parent
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
filesystem = setmetatable({}, {__index=driver.filesystem})
|
filesystem = {}
|
||||||
fs = filesystem
|
|
||||||
|
|
||||||
fs.delete = fs.remove
|
function filesystem.canonical(path)
|
||||||
fs.isFolder = fs.isDirectory
|
local result = table.concat(segments(path), "/")
|
||||||
fs.list = fs.dir
|
if path:usub(1, 1) == "/" then
|
||||||
fs.mkdir = fs.makeDirectory
|
return "/" .. result
|
||||||
|
else
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function filesystem.concat(pathA, pathB)
|
||||||
|
return filesystem.canonical(pathA .. "/" .. pathB)
|
||||||
|
end
|
||||||
|
|
||||||
|
function filesystem.name(path)
|
||||||
|
local parts = segments(path)
|
||||||
|
return parts[#parts]
|
||||||
|
end
|
||||||
|
|
||||||
|
function filesystem.mount(fs, path)
|
||||||
|
if type(fs) == "string" then
|
||||||
|
fs = component.proxy(fs)
|
||||||
|
end
|
||||||
|
assert(type(fs) == "table", "bad argument #1 (file system proxy or address expected)")
|
||||||
|
if fs and path then
|
||||||
|
local node = findNode(path, true)
|
||||||
|
if node.fs then
|
||||||
|
return nil, "another filesystem is already mounted here"
|
||||||
|
end
|
||||||
|
node.fs = fs
|
||||||
|
else
|
||||||
|
local function path(node)
|
||||||
|
local result = "/"
|
||||||
|
while node and node.parent do
|
||||||
|
for name, child in pairs(node.parent.children) do
|
||||||
|
if child == node then
|
||||||
|
result = "/" .. name .. result
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
node = node.parent
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
local queue = {mtab}
|
||||||
|
return function()
|
||||||
|
if #queue == 0 then
|
||||||
|
return nil
|
||||||
|
else
|
||||||
|
while true do
|
||||||
|
local node = table.remove(queue)
|
||||||
|
for _, child in pairs(node.children) do
|
||||||
|
table.insert(queue, child)
|
||||||
|
end
|
||||||
|
if node.fs then
|
||||||
|
return node.fs, path(node)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function filesystem.umount(fsOrPath)
|
||||||
|
local node, rest = findNode(fsOrPath)
|
||||||
|
if not rest and node.fs then
|
||||||
|
node.fs = nil
|
||||||
|
removeEmptyNodes(node)
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
local function unmount(address)
|
||||||
|
local queue = {mtab}
|
||||||
|
for fs, path in filesystem.mount() do
|
||||||
|
if fs.address == address then
|
||||||
|
local node = findNode(path)
|
||||||
|
node.fs = nil
|
||||||
|
removeEmptyNodes(node)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local fs = type(fsOrPath) == "table" and fsOrPath.address or fsOrPath
|
||||||
|
while unmount(fs) do end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
function fs.autorun(...)
|
function filesystem.autorun(...)
|
||||||
local args = table.pack(...)
|
local args = table.pack(...)
|
||||||
if args.n > 0 then
|
if args.n > 0 then
|
||||||
checkArg(1, args[1], "boolean")
|
checkArg(1, args[1], "boolean")
|
||||||
@ -23,6 +158,226 @@ end
|
|||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function filesystem.spaceTotal(path)
|
||||||
|
local node, rest = findNode(path)
|
||||||
|
if node.fs then
|
||||||
|
return node.fs.spaceTotal()
|
||||||
|
else
|
||||||
|
return nil, "invalid path"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function filesystem.spaceUsed(path)
|
||||||
|
local node, rest = findNode(path)
|
||||||
|
if node.fs then
|
||||||
|
return node.fs.spaceUsed()
|
||||||
|
else
|
||||||
|
return nil, "invalid path"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function filesystem.exists(path)
|
||||||
|
local node, rest = findNode(path)
|
||||||
|
if not rest then -- virtual directory
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
if node.fs then
|
||||||
|
return node.fs.exists(rest)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function filesystem.size(path)
|
||||||
|
local node, rest = findNode(path)
|
||||||
|
if node.fs and rest then
|
||||||
|
return node.fs.size(rest)
|
||||||
|
end
|
||||||
|
return 0 -- no such file or directory or it's a virtual directory
|
||||||
|
end
|
||||||
|
|
||||||
|
function filesystem.isDirectory(path)
|
||||||
|
local node, rest = findNode(path)
|
||||||
|
if node.fs and rest then
|
||||||
|
return node.fs.isDirectory(rest)
|
||||||
|
else
|
||||||
|
return not rest or rest:ulen() == 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function filesystem.lastModified(path)
|
||||||
|
local node, rest = findNode(path)
|
||||||
|
if node.fs and rest then
|
||||||
|
return node.fs.lastModified(rest)
|
||||||
|
end
|
||||||
|
return 0 -- no such file or directory or it's a virtual directory
|
||||||
|
end
|
||||||
|
|
||||||
|
function filesystem.list(path)
|
||||||
|
local node, rest = findNode(path)
|
||||||
|
if not node.fs and rest then
|
||||||
|
return nil, "no such file or directory"
|
||||||
|
end
|
||||||
|
local result, reason
|
||||||
|
if node.fs then
|
||||||
|
result, reason = node.fs.list(rest or "")
|
||||||
|
if not result then
|
||||||
|
return nil, reason
|
||||||
|
end
|
||||||
|
else
|
||||||
|
result = {}
|
||||||
|
end
|
||||||
|
if not rest then
|
||||||
|
for k, _ in pairs(node.children) do
|
||||||
|
table.insert(result, k .. "/")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.sort(result)
|
||||||
|
local i = 0
|
||||||
|
return function()
|
||||||
|
i = i + 1
|
||||||
|
return result[i]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function filesystem.makeDirectory(path)
|
||||||
|
local node, rest = findNode(path)
|
||||||
|
if node.fs and rest then
|
||||||
|
return node.fs.makeDirectory(rest)
|
||||||
|
end
|
||||||
|
return nil, "cannot create a directory in a virtual directory"
|
||||||
|
end
|
||||||
|
|
||||||
|
function filesystem.remove(path)
|
||||||
|
local node, rest = findNode(path)
|
||||||
|
if node.fs and rest then
|
||||||
|
return node.fs.remove(rest)
|
||||||
|
end
|
||||||
|
return nil, "no such non-virtual directory"
|
||||||
|
end
|
||||||
|
|
||||||
|
function filesystem.rename(oldPath, newPath)
|
||||||
|
local oldNode, oldRest = findNode(oldPath)
|
||||||
|
local newNode, newRest = findNode(newPath)
|
||||||
|
if oldNode.fs and oldRest and newNode.fs and newRest then
|
||||||
|
if oldNode.fs.address == newNode.fs.address then
|
||||||
|
return oldNode.fs.rename(oldRest, newRest)
|
||||||
|
else
|
||||||
|
local result, reason = filesystem.copy(oldPath, newPath)
|
||||||
|
if result then
|
||||||
|
return filesystem.remove(oldPath)
|
||||||
|
else
|
||||||
|
return nil, reason
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil, "trying to read from or write to virtual directory"
|
||||||
|
end
|
||||||
|
|
||||||
|
function filesystem.copy(fromPath, toPath)
|
||||||
|
local input, reason = io.open(fromPath, "rb")
|
||||||
|
if not input then
|
||||||
|
error(reason)
|
||||||
|
end
|
||||||
|
local output, reason = io.open(toPath, "wb")
|
||||||
|
if not output then
|
||||||
|
input:close()
|
||||||
|
error(reason)
|
||||||
|
end
|
||||||
|
repeat
|
||||||
|
local buffer, reason = input:read(1024)
|
||||||
|
if not buffer and reason then
|
||||||
|
error(reason)
|
||||||
|
elseif buffer then
|
||||||
|
local result, reason = output:write(buffer)
|
||||||
|
if not result then
|
||||||
|
input:close()
|
||||||
|
output:close()
|
||||||
|
error(reason)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
until not buffer
|
||||||
|
input:close()
|
||||||
|
output:close()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local fileStream = {}
|
||||||
|
|
||||||
|
function fileStream:close()
|
||||||
|
self.fs.close(self.handle)
|
||||||
|
self.handle = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function fileStream:read(n)
|
||||||
|
if not self.handle then
|
||||||
|
return nil, "file is closed"
|
||||||
|
end
|
||||||
|
return self.fs.read(self.handle, n)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fileStream:seek(whence, offset)
|
||||||
|
if not self.handle then
|
||||||
|
return nil, "file is closed"
|
||||||
|
end
|
||||||
|
return self.fs.seek(self.handle, whence, offset)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fileStream:write(str)
|
||||||
|
if not self.handle then
|
||||||
|
return nil, "file is closed"
|
||||||
|
end
|
||||||
|
return self.fs.write(self.handle, str)
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function filesystem.open(path, mode)
|
||||||
|
mode = tostring(mode or "r")
|
||||||
|
checkArg(2, mode, "string")
|
||||||
|
assert(({r=true, rb=true, w=true, wb=true, a=true, ab=true})[mode],
|
||||||
|
"bad argument #2 (r[b], w[b] or a[b] expected, got " .. mode .. ")")
|
||||||
|
|
||||||
|
local node, rest = findNode(path)
|
||||||
|
if not node.fs or not rest then
|
||||||
|
return nil, "file not found"
|
||||||
|
end
|
||||||
|
|
||||||
|
local handle, reason = node.fs.open(rest, mode)
|
||||||
|
if not handle then
|
||||||
|
return nil, reason
|
||||||
|
end
|
||||||
|
|
||||||
|
local stream = {fs = node.fs, handle = handle}
|
||||||
|
|
||||||
|
-- stream:close does a syscall, which yields, and that's not possible in
|
||||||
|
-- the __gc metamethod. So we start a timer to do the yield/cleanup.
|
||||||
|
local function cleanup(self)
|
||||||
|
if not self.handle then return end
|
||||||
|
-- save non-gc'ed values as upvalues
|
||||||
|
local fs = self.fs
|
||||||
|
local handle = self.handle
|
||||||
|
local function close()
|
||||||
|
fs.close(handle)
|
||||||
|
end
|
||||||
|
event.timer(0, close)
|
||||||
|
end
|
||||||
|
local metatable = {__index = fileStream,
|
||||||
|
__gc = cleanup,
|
||||||
|
__metatable = "filestream"}
|
||||||
|
return setmetatable(stream, metatable)
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
fs = filesystem
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
local function onComponentAdded(_, address)
|
local function onComponentAdded(_, address)
|
||||||
local componentType = component.type(address)
|
local componentType = component.type(address)
|
||||||
if (componentType == "filesystem" or componentType == "disk_drive") and
|
if (componentType == "filesystem" or componentType == "disk_drive") and
|
||||||
|
@ -1,65 +1,22 @@
|
|||||||
local resolutionX, resolutionY = nil, nil
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
gpu = {}
|
|
||||||
|
|
||||||
function gpu.bind(screen)
|
|
||||||
return driver.gpu.bind(component.primary("gpu"), screen)
|
|
||||||
end
|
|
||||||
|
|
||||||
function gpu.resolution(w, h)
|
|
||||||
if w and h then
|
|
||||||
return driver.gpu.resolution(component.primary("gpu"), w, h)
|
|
||||||
elseif not resolutionX or not resolutionY then
|
|
||||||
resolutionX, resolutionY = driver.gpu.resolution(component.primary("gpu"))
|
|
||||||
end
|
|
||||||
return resolutionX, resolutionY
|
|
||||||
end
|
|
||||||
|
|
||||||
function gpu.maxResolution()
|
|
||||||
return driver.gpu.maxResolution(component.primary("gpu"))
|
|
||||||
end
|
|
||||||
|
|
||||||
function gpu.set(col, row, value)
|
|
||||||
return driver.gpu.set(component.primary("gpu"), col, row, value)
|
|
||||||
end
|
|
||||||
|
|
||||||
function gpu.fill(col, row, w, h, value)
|
|
||||||
return driver.gpu.fill(component.primary("gpu"), col, row, w, h, value)
|
|
||||||
end
|
|
||||||
|
|
||||||
function gpu.copy(col, row, w, h, tx, ty)
|
|
||||||
return driver.gpu.copy(component.primary("gpu"), col, row, w, h, tx, ty)
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
local function onComponentAvailable(_, componentType)
|
local function onComponentAvailable(_, componentType)
|
||||||
if (componentType == "screen" and component.isAvailable("gpu")) or
|
if (componentType == "screen" and component.isAvailable("gpu")) or
|
||||||
(componentType == "gpu" and component.isAvailable("screen"))
|
(componentType == "gpu" and component.isAvailable("screen"))
|
||||||
then
|
then
|
||||||
gpu.bind(component.primary("screen"))
|
component.primary("gpu").bind(component.primary("screen").address)
|
||||||
local maxX, maxY = gpu.maxResolution()
|
local maxX, maxY = gpu.maxResolution()
|
||||||
gpu.resolution(maxX, maxY)
|
component.primary("gpu").setResolution(maxX, maxY)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function onComponentUnavailable(_, componentType)
|
-- local function onScreenResized(_, address, width, height)
|
||||||
if componentType == "gpu" or componentType == "screen" then
|
-- if component.isPrimary(address) and component.isAvailable("gpu") then
|
||||||
resolutionX, resolutionY = nil, nil
|
-- component.primary("gpu").getResolution = function()
|
||||||
end
|
-- return width, height
|
||||||
end
|
-- end
|
||||||
|
-- end
|
||||||
local function onScreenResized(_, address, width, height)
|
-- end
|
||||||
if component.primary("screen") == address then
|
|
||||||
resolutionX = width
|
|
||||||
resolutionY = height
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return function()
|
return function()
|
||||||
event.listen("component_available", onComponentAvailable)
|
event.listen("component_available", onComponentAvailable)
|
||||||
event.listen("component_unavailable", onComponentUnavailable)
|
-- event.listen("screen_resized", onScreenResized)
|
||||||
event.listen("screen_resized", onScreenResized)
|
|
||||||
end
|
end
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
local file = {}
|
local file = {}
|
||||||
|
|
||||||
function file:close()
|
function file:close()
|
||||||
self:flush()
|
if self.mode ~= "r" and self.mode ~= "rb" then
|
||||||
|
self:flush()
|
||||||
|
end
|
||||||
return self.stream:close()
|
return self.stream:close()
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -252,7 +254,7 @@ end
|
|||||||
|
|
||||||
function file.new(mode, stream, nogc)
|
function file.new(mode, stream, nogc)
|
||||||
local result = {
|
local result = {
|
||||||
mode = mode,
|
mode = mode or "r",
|
||||||
stream = stream,
|
stream = stream,
|
||||||
buffer = "",
|
buffer = "",
|
||||||
bufferSize = math.max(128, math.min(8 * 1024, os.freeMemory() / 8)),
|
bufferSize = math.max(128, math.min(8 * 1024, os.freeMemory() / 8)),
|
||||||
@ -364,7 +366,7 @@ function io.lines(filename, ...)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function io.open(path, mode)
|
function io.open(path, mode)
|
||||||
local stream, result = driver.filesystem.open(path, mode)
|
local stream, result = fs.open(path, mode)
|
||||||
if stream then
|
if stream then
|
||||||
return file.new(mode, stream)
|
return file.new(mode, stream)
|
||||||
else
|
else
|
||||||
|
@ -1,10 +1,149 @@
|
|||||||
keyboard = setmetatable({}, {__index = driver.keyboard})
|
|
||||||
|
|
||||||
local pressedChars = {}
|
local pressedChars = {}
|
||||||
local pressedCodes = {}
|
local pressedCodes = {}
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
keyboard = {}
|
||||||
|
|
||||||
|
keyboard.keys = {
|
||||||
|
["1"] = 0x02,
|
||||||
|
["2"] = 0x03,
|
||||||
|
["3"] = 0x04,
|
||||||
|
["4"] = 0x05,
|
||||||
|
["5"] = 0x06,
|
||||||
|
["6"] = 0x07,
|
||||||
|
["7"] = 0x08,
|
||||||
|
["8"] = 0x09,
|
||||||
|
["9"] = 0x0A,
|
||||||
|
["0"] = 0x0B,
|
||||||
|
a = 0x1E,
|
||||||
|
b = 0x30,
|
||||||
|
c = 0x2E,
|
||||||
|
d = 0x20,
|
||||||
|
e = 0x12,
|
||||||
|
f = 0x21,
|
||||||
|
g = 0x22,
|
||||||
|
h = 0x23,
|
||||||
|
i = 0x17,
|
||||||
|
j = 0x24,
|
||||||
|
k = 0x25,
|
||||||
|
l = 0x26,
|
||||||
|
m = 0x32,
|
||||||
|
n = 0x31,
|
||||||
|
o = 0x18,
|
||||||
|
p = 0x19,
|
||||||
|
q = 0x10,
|
||||||
|
r = 0x13,
|
||||||
|
s = 0x1F,
|
||||||
|
t = 0x14,
|
||||||
|
u = 0x16,
|
||||||
|
v = 0x2F,
|
||||||
|
w = 0x11,
|
||||||
|
x = 0x2D,
|
||||||
|
y = 0x15,
|
||||||
|
z = 0x2C,
|
||||||
|
|
||||||
|
apostrophe = 0x28,
|
||||||
|
at = 0x91,
|
||||||
|
back = 0x0E, -- backspace
|
||||||
|
backslash = 0x2B,
|
||||||
|
colon = 0x92,
|
||||||
|
comma = 0x33,
|
||||||
|
enter = 0x1C,
|
||||||
|
equals = 0x0D,
|
||||||
|
grave = 0x29, -- accent grave
|
||||||
|
lbracket = 0x1A,
|
||||||
|
lcontrol = 0x1D,
|
||||||
|
lmenu = 0x38, -- left Alt
|
||||||
|
lshift = 0x2A,
|
||||||
|
minus = 0x0C,
|
||||||
|
numlock = 0x45,
|
||||||
|
pause = 0xC5,
|
||||||
|
period = 0x34,
|
||||||
|
rbracket = 0x1B,
|
||||||
|
rcontrol = 0x9D,
|
||||||
|
rmenu = 0xB8, -- right Alt
|
||||||
|
rshift = 0x36,
|
||||||
|
scroll = 0x46, -- Scroll Lock
|
||||||
|
semicolon = 0x27,
|
||||||
|
slash = 0x35, -- / on main keyboard
|
||||||
|
space = 0x39,
|
||||||
|
stop = 0x95,
|
||||||
|
tab = 0x0F,
|
||||||
|
underline = 0x93,
|
||||||
|
|
||||||
|
-- Keypad (and numpad with numlock off)
|
||||||
|
up = 0xC8,
|
||||||
|
down = 0xD0,
|
||||||
|
left = 0xCB,
|
||||||
|
right = 0xCD,
|
||||||
|
home = 0xC7,
|
||||||
|
["end"] = 0xCF,
|
||||||
|
pageUp = 0xC9,
|
||||||
|
pageDown = 0xD1,
|
||||||
|
insert = 0xD2,
|
||||||
|
delete = 0xD3,
|
||||||
|
|
||||||
|
-- Function keys
|
||||||
|
f1 = 0x3B,
|
||||||
|
f2 = 0x3C,
|
||||||
|
f3 = 0x3D,
|
||||||
|
f4 = 0x3E,
|
||||||
|
f5 = 0x3F,
|
||||||
|
f6 = 0x40,
|
||||||
|
f7 = 0x41,
|
||||||
|
f8 = 0x42,
|
||||||
|
f9 = 0x43,
|
||||||
|
f10 = 0x44,
|
||||||
|
f11 = 0x57,
|
||||||
|
f12 = 0x58,
|
||||||
|
f13 = 0x64,
|
||||||
|
f14 = 0x65,
|
||||||
|
f15 = 0x66,
|
||||||
|
f16 = 0x67,
|
||||||
|
f17 = 0x68,
|
||||||
|
f18 = 0x69,
|
||||||
|
f19 = 0x71,
|
||||||
|
|
||||||
|
-- Japanese keyboards
|
||||||
|
kana = 0x70,
|
||||||
|
kanji = 0x94,
|
||||||
|
convert = 0x79,
|
||||||
|
noconvert = 0x7B,
|
||||||
|
yen = 0x7D,
|
||||||
|
circumflex = 0x90,
|
||||||
|
ax = 0x96,
|
||||||
|
|
||||||
|
-- Numpad
|
||||||
|
numpad0 = 0x52,
|
||||||
|
numpad1 = 0x4F,
|
||||||
|
numpad2 = 0x50,
|
||||||
|
numpad3 = 0x51,
|
||||||
|
numpad4 = 0x4B,
|
||||||
|
numpad5 = 0x4C,
|
||||||
|
numpad6 = 0x4D,
|
||||||
|
numpad7 = 0x47,
|
||||||
|
numpad8 = 0x48,
|
||||||
|
numpad9 = 0x49,
|
||||||
|
numpadmul = 0x37,
|
||||||
|
numpaddiv = 0xB5,
|
||||||
|
numpadsub = 0x4A,
|
||||||
|
numpadadd = 0x4E,
|
||||||
|
numpaddecimal = 0x53,
|
||||||
|
numpadcomma = 0xB3,
|
||||||
|
numpadenter = 0x9C,
|
||||||
|
numpadequals = 0x8D,
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Create inverse mapping for name lookup.
|
||||||
|
for k, v in pairs(keyboard.keys) do
|
||||||
|
keyboard.keys[v] = k
|
||||||
|
end
|
||||||
|
|
||||||
|
function keyboard.isControl(char)
|
||||||
|
return type(char) == "number" and (char < 0x20 or (char >= 0x7F and char <= 0x9F))
|
||||||
|
end
|
||||||
|
|
||||||
function keyboard.isKeyDown(charOrCode)
|
function keyboard.isKeyDown(charOrCode)
|
||||||
checkArg(1, charOrCode, "string", "number")
|
checkArg(1, charOrCode, "string", "number")
|
||||||
if type(charOrCode) == "string" then
|
if type(charOrCode) == "string" then
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
network = {}
|
|
||||||
net = network
|
|
||||||
|
|
||||||
function network.open(port)
|
|
||||||
return driver.network.open(component.primary("network"), port)
|
|
||||||
end
|
|
||||||
|
|
||||||
function network.close(port)
|
|
||||||
return driver.network.close(component.primary("network"), port)
|
|
||||||
end
|
|
||||||
|
|
||||||
function network.send(target, port, ...)
|
|
||||||
return driver.network.send(component.primary("network"), target, port, ...)
|
|
||||||
end
|
|
||||||
|
|
||||||
function network.broadcast(port, ...)
|
|
||||||
return driver.network.broadcast(component.primary("network"), port, ...)
|
|
||||||
end
|
|
@ -21,19 +21,19 @@ os.execute = function(command)
|
|||||||
return shell.execute(head, table.unpack(args))
|
return shell.execute(head, table.unpack(args))
|
||||||
end
|
end
|
||||||
|
|
||||||
os.remove = driver.filesystem.remove
|
os.remove = fs.remove
|
||||||
|
|
||||||
os.rename = driver.filesystem.rename
|
os.rename = fs.rename
|
||||||
|
|
||||||
function os.sleep(timeout)
|
function os.sleep(timeout)
|
||||||
event.wait(nil, timeout)
|
event.wait(nil, timeout)
|
||||||
end
|
end
|
||||||
|
|
||||||
function os.tmpname()
|
function os.tmpname()
|
||||||
if driver.filesystem.exists("tmp") then
|
if fs.exists("tmp") then
|
||||||
for i = 1, 10 do
|
for i = 1, 10 do
|
||||||
local name = "tmp/" .. math.random(1, 0x7FFFFFFF)
|
local name = "tmp/" .. math.random(1, 0x7FFFFFFF)
|
||||||
if not driver.filesystem.exists(name) then
|
if not fs.exists(name) then
|
||||||
return name
|
return name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -10,24 +10,6 @@ end
|
|||||||
redstone = {}
|
redstone = {}
|
||||||
rs = redstone
|
rs = redstone
|
||||||
|
|
||||||
function rs.analogInput(side)
|
|
||||||
return driver.redstone.analogInput(component.primary("redstone"), stringToSide(side))
|
|
||||||
end
|
|
||||||
|
|
||||||
function rs.analogOutput(side, value)
|
|
||||||
return driver.redstone.analogOutput(component.primary("redstone"), stringToSide(side), value)
|
|
||||||
end
|
|
||||||
|
|
||||||
function rs.input(side)
|
|
||||||
return driver.redstone.input(component.primary("redstone"), stringToSide(side))
|
|
||||||
end
|
|
||||||
|
|
||||||
function rs.output(side, value)
|
|
||||||
return driver.redstone.output(component.primary("redstone"), stringToSide(side), value)
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
rs.sides = {
|
rs.sides = {
|
||||||
[0] = "bottom",
|
[0] = "bottom",
|
||||||
[1] = "top",
|
[1] = "top",
|
||||||
@ -41,3 +23,25 @@ for k, v in pairs(rs.sides) do
|
|||||||
end
|
end
|
||||||
rs.sides.up = rs.sides.top
|
rs.sides.up = rs.sides.top
|
||||||
rs.sides.down = rs.sides.bottom
|
rs.sides.down = rs.sides.bottom
|
||||||
|
|
||||||
|
function rs.proxy(address)
|
||||||
|
local proxy = component.proxy(address)
|
||||||
|
local analogInput = proxy.analogInput
|
||||||
|
local analogOutput = proxy.analogOutput
|
||||||
|
function proxy.analogInput(side)
|
||||||
|
return analogInput(stringToSide(side))
|
||||||
|
end
|
||||||
|
function proxy.analogOutput(side, value)
|
||||||
|
return analogOutput(stringToSide(side), value)
|
||||||
|
end
|
||||||
|
function proxy.input(side)
|
||||||
|
return proxy.analogInput(side) > 0
|
||||||
|
end
|
||||||
|
function proxy.output(side, value)
|
||||||
|
if value then
|
||||||
|
return proxy.analogOutput(side, value and 15 or 0)
|
||||||
|
else
|
||||||
|
return proxy.analogOutput(side) > 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
local cursorX, cursorY = 1, 1
|
local cursorX, cursorY = 1, 1
|
||||||
local cursorBlink = nil
|
local cursorBlink = nil
|
||||||
|
|
||||||
|
local function gpu() return component.primary("gpu") end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
term = {}
|
term = {}
|
||||||
@ -11,23 +13,23 @@ end
|
|||||||
|
|
||||||
function term.clear()
|
function term.clear()
|
||||||
if term.isAvailable() then
|
if term.isAvailable() then
|
||||||
local w, h = gpu.resolution()
|
local w, h = gpu().getResolution()
|
||||||
gpu.fill(1, 1, w, h, " ")
|
gpu().fill(1, 1, w, h, " ")
|
||||||
end
|
end
|
||||||
cursorX, cursorY = 1, 1
|
cursorX, cursorY = 1, 1
|
||||||
end
|
end
|
||||||
|
|
||||||
function term.clearLine()
|
function term.clearLine()
|
||||||
if term.isAvailable() then
|
if term.isAvailable() then
|
||||||
local w = gpu.resolution()
|
local w = gpu().getResolution()
|
||||||
gpu.fill(1, cursorY, w, 1, " ")
|
gpu().fill(1, cursorY, w, 1, " ")
|
||||||
end
|
end
|
||||||
cursorX = 1
|
cursorX = 1
|
||||||
end
|
end
|
||||||
|
|
||||||
function term.cursor(col, row)
|
function term.cursor(col, row)
|
||||||
if col and row then
|
if col and row then
|
||||||
local w, h = gpu.resolution()
|
local w, h = gpu().getResolution()
|
||||||
cursorX = math.min(math.max(col, 1), w)
|
cursorX = math.min(math.max(col, 1), w)
|
||||||
cursorY = math.min(math.max(row, 1), h)
|
cursorY = math.min(math.max(row, 1), h)
|
||||||
end
|
end
|
||||||
@ -39,7 +41,7 @@ function term.cursorBlink(enabled)
|
|||||||
cursorBlink.state = not cursorBlink.state
|
cursorBlink.state = not cursorBlink.state
|
||||||
if term.isAvailable() then
|
if term.isAvailable() then
|
||||||
local char = cursorBlink.state and cursorBlink.solid or cursorBlink.alt
|
local char = cursorBlink.state and cursorBlink.solid or cursorBlink.alt
|
||||||
gpu.set(cursorX, cursorY, char)
|
gpu().set(cursorX, cursorY, char)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local function start(alt)
|
local function start(alt)
|
||||||
@ -93,24 +95,24 @@ function term.read(history)
|
|||||||
local function remove()
|
local function remove()
|
||||||
local x = start - 1 + cursor - scroll
|
local x = start - 1 + cursor - scroll
|
||||||
local _, y = term.cursor()
|
local _, y = term.cursor()
|
||||||
local w = gpu.resolution()
|
local w = gpu().getResolution()
|
||||||
gpu.copy(x + 1, y, w - x, 1, -1, 0)
|
gpu().copy(x + 1, y, w - x, 1, -1, 0)
|
||||||
local cursor = cursor + (w - x)
|
local cursor = cursor + (w - x)
|
||||||
local char = history[current]:usub(cursor, cursor)
|
local char = history[current]:usub(cursor, cursor)
|
||||||
if char:ulen() == 0 then
|
if char:ulen() == 0 then
|
||||||
char = " "
|
char = " "
|
||||||
end
|
end
|
||||||
gpu.set(w, y, char)
|
gpu().set(w, y, char)
|
||||||
end
|
end
|
||||||
local function render()
|
local function render()
|
||||||
local _, y = term.cursor()
|
local _, y = term.cursor()
|
||||||
local w = gpu.resolution()
|
local w = gpu().getResolution()
|
||||||
local str = history[current]:usub(1 + scroll, 1 + scroll + w - (start - 1))
|
local str = history[current]:usub(1 + scroll, 1 + scroll + w - (start - 1))
|
||||||
str = str .. string.rep(" ", (w - (start - 1)) - str:ulen())
|
str = str .. string.rep(" ", (w - (start - 1)) - str:ulen())
|
||||||
gpu.set(start, y, str)
|
gpu().set(start, y, str)
|
||||||
end
|
end
|
||||||
local function scrollEnd()
|
local function scrollEnd()
|
||||||
local w = gpu.resolution()
|
local w = gpu().getResolution()
|
||||||
cursor = history[current]:ulen() + 1
|
cursor = history[current]:ulen() + 1
|
||||||
scroll = math.max(0, cursor - (w - (start - 1)))
|
scroll = math.max(0, cursor - (w - (start - 1)))
|
||||||
render()
|
render()
|
||||||
@ -118,36 +120,36 @@ function term.read(history)
|
|||||||
local function scrollLeft()
|
local function scrollLeft()
|
||||||
scroll = scroll - 1
|
scroll = scroll - 1
|
||||||
local _, y = term.cursor()
|
local _, y = term.cursor()
|
||||||
local w = gpu.resolution()
|
local w = gpu().getResolution()
|
||||||
gpu.copy(start, y, w - start - 1, 1, 1, 0)
|
gpu().copy(start, y, w - start - 1, 1, 1, 0)
|
||||||
local cursor = w - (start - 1) + scroll
|
local cursor = w - (start - 1) + scroll
|
||||||
local char = history[current]:usub(cursor, cursor)
|
local char = history[current]:usub(cursor, cursor)
|
||||||
if char:ulen() == 0 then
|
if char:ulen() == 0 then
|
||||||
char = " "
|
char = " "
|
||||||
end
|
end
|
||||||
gpu.set(1, y, char)
|
gpu().set(1, y, char)
|
||||||
end
|
end
|
||||||
local function scrollRight()
|
local function scrollRight()
|
||||||
scroll = scroll + 1
|
scroll = scroll + 1
|
||||||
local _, y = term.cursor()
|
local _, y = term.cursor()
|
||||||
local w = gpu.resolution()
|
local w = gpu().getResolution()
|
||||||
gpu.copy(start + 1, y, w - start, 1, -1, 0)
|
gpu().copy(start + 1, y, w - start, 1, -1, 0)
|
||||||
local cursor = w - (start - 1) + scroll
|
local cursor = w - (start - 1) + scroll
|
||||||
local char = history[current]:usub(cursor, cursor)
|
local char = history[current]:usub(cursor, cursor)
|
||||||
if char:ulen() == 0 then
|
if char:ulen() == 0 then
|
||||||
char = " "
|
char = " "
|
||||||
end
|
end
|
||||||
gpu.set(w, y, char)
|
gpu().set(w, y, char)
|
||||||
end
|
end
|
||||||
local function update()
|
local function update()
|
||||||
local _, y = term.cursor()
|
local _, y = term.cursor()
|
||||||
local w = gpu.resolution()
|
local w = gpu().getResolution()
|
||||||
local cursor = cursor - 1
|
local cursor = cursor - 1
|
||||||
local x = start - 1 + cursor - scroll
|
local x = start - 1 + cursor - scroll
|
||||||
if cursor < history[current]:ulen() then
|
if cursor < history[current]:ulen() then
|
||||||
gpu.copy(x, y, w - x, 1, 1, 0)
|
gpu().copy(x, y, w - x, 1, 1, 0)
|
||||||
end
|
end
|
||||||
gpu.set(x, y, history[current]:usub(cursor, cursor))
|
gpu().set(x, y, history[current]:usub(cursor, cursor))
|
||||||
end
|
end
|
||||||
local function copyIfNecessary()
|
local function copyIfNecessary()
|
||||||
if current ~= #history then
|
if current ~= #history then
|
||||||
@ -166,7 +168,7 @@ function term.read(history)
|
|||||||
end
|
end
|
||||||
local function handleKeyPress(char, code)
|
local function handleKeyPress(char, code)
|
||||||
if not term.isAvailable() then return end
|
if not term.isAvailable() then return end
|
||||||
local w, h = gpu.resolution()
|
local w, h = gpu().getResolution()
|
||||||
local cancel, blink = false, false
|
local cancel, blink = false, false
|
||||||
term.cursorBlink(false)
|
term.cursorBlink(false)
|
||||||
if code == keyboard.keys.back then
|
if code == keyboard.keys.back then
|
||||||
@ -334,15 +336,15 @@ function term.write(value, wrap)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
value = value:gsub("\t", " ")
|
value = value:gsub("\t", " ")
|
||||||
local w, h = gpu.resolution()
|
local w, h = gpu().getResolution()
|
||||||
local function checkCursor()
|
local function checkCursor()
|
||||||
if cursorX > w then
|
if cursorX > w then
|
||||||
cursorX = 1
|
cursorX = 1
|
||||||
cursorY = cursorY + 1
|
cursorY = cursorY + 1
|
||||||
end
|
end
|
||||||
if cursorY > h then
|
if cursorY > h then
|
||||||
gpu.copy(1, 1, w, h, 0, -1)
|
gpu().copy(1, 1, w, h, 0, -1)
|
||||||
gpu.fill(1, h, w, 1, " ")
|
gpu().fill(1, h, w, 1, " ")
|
||||||
cursorY = h
|
cursorY = h
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -350,12 +352,12 @@ function term.write(value, wrap)
|
|||||||
while wrap and line:ulen() > w - cursorX + 1 do
|
while wrap and line:ulen() > w - cursorX + 1 do
|
||||||
local partial = line:usub(1, w - cursorX + 1)
|
local partial = line:usub(1, w - cursorX + 1)
|
||||||
line = line:usub(partial:ulen() + 1)
|
line = line:usub(partial:ulen() + 1)
|
||||||
gpu.set(cursorX, cursorY, partial)
|
gpu().set(cursorX, cursorY, partial)
|
||||||
cursorX = cursorX + partial:ulen()
|
cursorX = cursorX + partial:ulen()
|
||||||
checkCursor()
|
checkCursor()
|
||||||
end
|
end
|
||||||
if line:ulen() > 0 then
|
if line:ulen() > 0 then
|
||||||
gpu.set(cursorX, cursorY, line)
|
gpu().set(cursorX, cursorY, line)
|
||||||
cursorX = cursorX + line:ulen()
|
cursorX = cursorX + line:ulen()
|
||||||
end
|
end
|
||||||
if nl:ulen() == 1 then
|
if nl:ulen() == 1 then
|
||||||
|
@ -6,7 +6,6 @@ object Config {
|
|||||||
val resourceDomain = "opencomputers"
|
val resourceDomain = "opencomputers"
|
||||||
val savePath = "opencomputers/"
|
val savePath = "opencomputers/"
|
||||||
val scriptPath = "/assets/" + resourceDomain + "/lua/"
|
val scriptPath = "/assets/" + resourceDomain + "/lua/"
|
||||||
val driverPath = "/assets/" + resourceDomain + "/lua/drivers/"
|
|
||||||
|
|
||||||
val screenResolutionsByTier = Array((50, 16), (80, 25), (160, 50))
|
val screenResolutionsByTier = Array((50, 16), (80, 25), (160, 50))
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ class Delegator[Child <: Delegate](id: Int) extends Block(id, Material.iron) {
|
|||||||
subBlock(metadata) match {
|
subBlock(metadata) match {
|
||||||
case Some(subBlock) => {
|
case Some(subBlock) => {
|
||||||
world.getBlockTileEntity(x, y, z) match {
|
world.getBlockTileEntity(x, y, z) match {
|
||||||
case environment: Environment =>
|
case environment: Environment if environment.node != null && environment.node.network != null =>
|
||||||
environment.node.network.remove(environment.node)
|
environment.node.network.remove(environment.node)
|
||||||
case _ => // Nothing special to do.
|
case _ => // Nothing special to do.
|
||||||
}
|
}
|
||||||
@ -293,7 +293,7 @@ class Delegator[Child <: Delegate](id: Int) extends Block(id, Material.iron) {
|
|||||||
subBlock(world, x, y, z) match {
|
subBlock(world, x, y, z) match {
|
||||||
case Some(subBlock) => {
|
case Some(subBlock) => {
|
||||||
world.getBlockTileEntity(x, y, z) match {
|
world.getBlockTileEntity(x, y, z) match {
|
||||||
case _: Node => Network.joinOrCreateNetwork(world, x, y, z)
|
case _: Environment => Network.joinOrCreateNetwork(world, x, y, z)
|
||||||
case _ => // Nothing special to do.
|
case _ => // Nothing special to do.
|
||||||
}
|
}
|
||||||
subBlock.onBlockAdded(world, x, y, z)
|
subBlock.onBlockAdded(world, x, y, z)
|
||||||
|
@ -59,12 +59,6 @@ class Computer(isClient: Boolean) extends Rotatable with component.Computer.Envi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
//
|
|
||||||
// override def save(nbt: NBTTagCompound) {}
|
|
||||||
//
|
|
||||||
// override def load(nbt: NBTTagCompound) {}
|
|
||||||
|
|
||||||
override def onMessage(message: Message) = null
|
|
||||||
|
|
||||||
override def updateEntity() = if (!worldObj.isRemote) {
|
override def updateEntity() = if (!worldObj.isRemote) {
|
||||||
instance.update()
|
instance.update()
|
||||||
|
@ -16,7 +16,7 @@ class CommandBlock(entity: TileEntityCommandBlock) extends ManagedComponent {
|
|||||||
|
|
||||||
@LuaCallback("setValue")
|
@LuaCallback("setValue")
|
||||||
def setValue(message: Message): Array[Object] = {
|
def setValue(message: Message): Array[Object] = {
|
||||||
val value = message.checkString(0)
|
val value = message.checkString(1)
|
||||||
entity.setCommand(value)
|
entity.setCommand(value)
|
||||||
entity.worldObj.markBlockForUpdate(entity.xCoord, entity.yCoord, entity.zCoord)
|
entity.worldObj.markBlockForUpdate(entity.xCoord, entity.yCoord, entity.zCoord)
|
||||||
result(true)
|
result(true)
|
||||||
|
@ -94,10 +94,10 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
def recomputeMemory() = if (lua != null) {
|
def recomputeMemory() = stateMonitor.synchronized(if (lua != null) {
|
||||||
lua.gc(LuaState.GcAction.COLLECT, 0)
|
lua.gc(LuaState.GcAction.COLLECT, 0)
|
||||||
lua.setTotalMemory(kernelMemory + owner.installedMemory)
|
lua.setTotalMemory(kernelMemory + owner.installedMemory)
|
||||||
}
|
})
|
||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
@ -113,6 +113,9 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
|||||||
// Mark state change in owner, to send it to clients.
|
// Mark state change in owner, to send it to clients.
|
||||||
owner.markAsChanged()
|
owner.markAsChanged()
|
||||||
|
|
||||||
|
// Push a dummy signal to get the computer going.
|
||||||
|
signal("dummy")
|
||||||
|
|
||||||
// All green, computer started successfully.
|
// All green, computer started successfully.
|
||||||
owner.node.network.sendToVisible(owner.node, "computer.started")
|
owner.node.network.sendToVisible(owner.node, "computer.started")
|
||||||
true
|
true
|
||||||
@ -156,17 +159,17 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
|||||||
true
|
true
|
||||||
})
|
})
|
||||||
|
|
||||||
def addComponent(address: String, name: String) {
|
def addComponent(component: Component) {
|
||||||
if (!components.contains(address)) {
|
if (!components.contains(component.address)) {
|
||||||
components += address -> name
|
components += component.address -> component.name
|
||||||
signal("component_added", address)
|
signal("component_added", component.address, component.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def removeComponent(address: String) {
|
def removeComponent(component: Component) {
|
||||||
if (components.contains(address)) {
|
if (components.contains(component.address)) {
|
||||||
components -= address
|
components -= component.address
|
||||||
signal("component_removed", address)
|
signal("component_removed", component.address, component.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -483,6 +486,12 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
|||||||
case Some(value) => lua = value
|
case Some(value) => lua = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Connect the ROM and `/tmp` node to our owner.
|
||||||
|
if (owner.node.network != null) {
|
||||||
|
rom.foreach(rom => owner.node.network.connect(owner.node, rom.node))
|
||||||
|
tmp.foreach(tmp => owner.node.network.connect(owner.node, tmp.node))
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Push a couple of functions that override original Lua API functions or
|
// Push a couple of functions that override original Lua API functions or
|
||||||
// that add new functionality to it.
|
// that add new functionality to it.
|
||||||
@ -570,17 +579,16 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
|||||||
|
|
||||||
// Until we get to ingame screens we log to Java's stdout.
|
// Until we get to ingame screens we log to Java's stdout.
|
||||||
lua.pushScalaFunction(lua => {
|
lua.pushScalaFunction(lua => {
|
||||||
for (i <- 1 to lua.getTop) lua.`type`(i) match {
|
println((1 to lua.getTop).map(i => lua.`type`(i) match {
|
||||||
case LuaType.NIL => print("nil")
|
case LuaType.NIL => "nil"
|
||||||
case LuaType.BOOLEAN => print(lua.toBoolean(i))
|
case LuaType.BOOLEAN => lua.toBoolean(i)
|
||||||
case LuaType.NUMBER => print(lua.toNumber(i))
|
case LuaType.NUMBER => lua.toNumber(i)
|
||||||
case LuaType.STRING => print(lua.toString(i))
|
case LuaType.STRING => lua.toString(i)
|
||||||
case LuaType.TABLE => print("table")
|
case LuaType.TABLE => "table"
|
||||||
case LuaType.FUNCTION => print("function")
|
case LuaType.FUNCTION => "function"
|
||||||
case LuaType.THREAD => print("thread")
|
case LuaType.THREAD => "thread"
|
||||||
case LuaType.LIGHTUSERDATA | LuaType.USERDATA => print("userdata")
|
case LuaType.LIGHTUSERDATA | LuaType.USERDATA => "userdata"
|
||||||
}
|
}).mkString(" "))
|
||||||
println()
|
|
||||||
0
|
0
|
||||||
})
|
})
|
||||||
lua.setGlobal("print")
|
lua.setGlobal("print")
|
||||||
@ -596,7 +604,7 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
|||||||
if (filter.isEmpty || name.matches(filter.get)) {
|
if (filter.isEmpty || name.matches(filter.get)) {
|
||||||
lua.pushString(address)
|
lua.pushString(address)
|
||||||
lua.pushString(name)
|
lua.pushString(name)
|
||||||
lua.rawSet(-2)
|
lua.rawSet(-3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
1
|
1
|
||||||
@ -616,6 +624,22 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
|||||||
})
|
})
|
||||||
lua.setGlobal("componentType")
|
lua.setGlobal("componentType")
|
||||||
|
|
||||||
|
lua.pushScalaFunction(lua => {
|
||||||
|
owner.node.network.sendToAddress(owner.node, lua.checkString(1), "component.methods") match {
|
||||||
|
case Array(names: Array[String]) =>
|
||||||
|
lua.newTable()
|
||||||
|
for ((name, index) <- names.zipWithIndex) {
|
||||||
|
lua.pushString(name)
|
||||||
|
lua.rawSet(-2, index + 1)
|
||||||
|
}
|
||||||
|
1
|
||||||
|
case _ =>
|
||||||
|
lua.pushNil()
|
||||||
|
lua.pushString("no such component")
|
||||||
|
2
|
||||||
|
}
|
||||||
|
})
|
||||||
|
lua.setGlobal("componentMethods")
|
||||||
|
|
||||||
// Set up functions used to send component.invoke network messages.
|
// Set up functions used to send component.invoke network messages.
|
||||||
def parseArgument(lua: LuaState, index: Int): AnyRef = lua.`type`(index) match {
|
def parseArgument(lua: LuaState, index: Int): AnyRef = lua.`type`(index) match {
|
||||||
@ -643,6 +667,7 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
|||||||
}
|
}
|
||||||
|
|
||||||
def pushResult(lua: LuaState, value: Any): Unit = value match {
|
def pushResult(lua: LuaState, value: Any): Unit = value match {
|
||||||
|
case null | Unit => lua.pushNil()
|
||||||
case value: Boolean => lua.pushBoolean(value)
|
case value: Boolean => lua.pushBoolean(value)
|
||||||
case value: Byte => lua.pushNumber(value)
|
case value: Byte => lua.pushNumber(value)
|
||||||
case value: Short => lua.pushNumber(value)
|
case value: Short => lua.pushNumber(value)
|
||||||
@ -680,45 +705,43 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
|||||||
case _ => throw new Exception("no such component")
|
case _ => throw new Exception("no such component")
|
||||||
}) match {
|
}) match {
|
||||||
case Array(results@_*) =>
|
case Array(results@_*) =>
|
||||||
lua.pushBoolean(true)
|
|
||||||
results.foreach(pushResult(lua, _))
|
results.foreach(pushResult(lua, _))
|
||||||
1 + results.length
|
results.length
|
||||||
case _ =>
|
case _ =>
|
||||||
lua.pushBoolean(true)
|
0
|
||||||
1
|
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
case e: Throwable if e.getMessage != null && !e.getMessage.isEmpty =>
|
case e: Throwable if e.getMessage != null && !e.getMessage.isEmpty =>
|
||||||
lua.pushBoolean(false)
|
lua.pushNil()
|
||||||
lua.pushString(e.getMessage)
|
lua.pushString(e.getMessage)
|
||||||
2
|
2
|
||||||
case _: FileNotFoundException =>
|
case _: FileNotFoundException =>
|
||||||
lua.pushBoolean(false)
|
lua.pushNil()
|
||||||
lua.pushString("file not found")
|
lua.pushString("file not found")
|
||||||
2
|
2
|
||||||
case _: SecurityException =>
|
case _: SecurityException =>
|
||||||
lua.pushBoolean(false)
|
lua.pushNil()
|
||||||
lua.pushString("access denied")
|
lua.pushString("access denied")
|
||||||
2
|
2
|
||||||
case _: IOException =>
|
case _: IOException =>
|
||||||
lua.pushBoolean(false)
|
lua.pushNil()
|
||||||
lua.pushString("i/o error")
|
lua.pushString("i/o error")
|
||||||
2
|
2
|
||||||
case _: NoSuchMethodException =>
|
case _: NoSuchMethodException =>
|
||||||
lua.pushBoolean(false)
|
lua.pushNil()
|
||||||
lua.pushString("no such method")
|
lua.pushString("no such method")
|
||||||
2
|
2
|
||||||
case _: IllegalArgumentException =>
|
case _: IllegalArgumentException =>
|
||||||
lua.pushBoolean(false)
|
lua.pushNil()
|
||||||
lua.pushString("bad argument")
|
lua.pushString("bad argument")
|
||||||
2
|
2
|
||||||
case _: Throwable =>
|
case _: Throwable =>
|
||||||
lua.pushBoolean(false)
|
lua.pushNil()
|
||||||
lua.pushString("unknown error")
|
lua.pushString("unknown error")
|
||||||
2
|
2
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
lua.setGlobal("componentCall")
|
lua.setGlobal("componentInvoke")
|
||||||
|
|
||||||
// List of installed GPUs - this is used during boot to allow giving some
|
// List of installed GPUs - this is used during boot to allow giving some
|
||||||
// feedback on the process, since booting can take some time. It feels a
|
// feedback on the process, since booting can take some time. It feels a
|
||||||
@ -750,7 +773,11 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
|||||||
lua.newThread() // Left as the first value on the stack.
|
lua.newThread() // Left as the first value on the stack.
|
||||||
// Run to the first yield in kernel, to get a good idea of how much
|
// Run to the first yield in kernel, to get a good idea of how much
|
||||||
// memory all the basic functionality we provide needs.
|
// memory all the basic functionality we provide needs.
|
||||||
lua.pop(lua.resume(1, 0))
|
val results = lua.resume(1, 0)
|
||||||
|
if (lua.status(1) != LuaState.YIELD)
|
||||||
|
if (!lua.toBoolean(-2)) throw new Exception(lua.toString(-1))
|
||||||
|
else throw new Exception("kernel return unexpectedly")
|
||||||
|
lua.pop(results)
|
||||||
|
|
||||||
// Run the garbage collector to get rid of stuff left behind after the
|
// 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
|
// initialization phase to get a good estimate of the base memory usage
|
||||||
@ -765,12 +792,6 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
|||||||
// Clear any left-over signals from a previous run.
|
// Clear any left-over signals from a previous run.
|
||||||
signals.clear()
|
signals.clear()
|
||||||
|
|
||||||
// Connect the ROM and `/tmp` node to our owner.
|
|
||||||
if (owner.node.network != null) {
|
|
||||||
rom.foreach(rom => owner.node.network.connect(owner.node, rom.node))
|
|
||||||
tmp.foreach(tmp => owner.node.network.connect(owner.node, tmp.node))
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
@ -997,17 +1018,16 @@ object Computer {
|
|||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
override def onMessage(message: Message) = {
|
override def onMessage(message: Message) = {
|
||||||
|
message.source match {
|
||||||
|
case component: Component if component.canBeSeenBy(node) =>
|
||||||
|
message.name match {
|
||||||
|
case "system.connect" => instance.addComponent(component)
|
||||||
|
case "system.disconnect" => instance.removeComponent(component)
|
||||||
|
case _ =>
|
||||||
|
}
|
||||||
|
case _ =>
|
||||||
|
}
|
||||||
if (instance.isRunning) {
|
if (instance.isRunning) {
|
||||||
message.source match {
|
|
||||||
case node: Component if node.canBeSeenBy(node) =>
|
|
||||||
message.name match {
|
|
||||||
case "system.connect" =>
|
|
||||||
instance.addComponent(message.source.address, message.source.name)
|
|
||||||
case "system.disconnect" =>
|
|
||||||
instance.removeComponent(message.source.address)
|
|
||||||
}
|
|
||||||
case _ =>
|
|
||||||
}
|
|
||||||
message.data match {
|
message.data match {
|
||||||
// Arbitrary signals, usually from other components.
|
// Arbitrary signals, usually from other components.
|
||||||
case Array(name: String, args@_*) if message.name == "computer.signal" =>
|
case Array(name: String, args@_*) if message.name == "computer.signal" =>
|
||||||
|
@ -22,7 +22,7 @@ class FileSystem(val fileSystem: api.fs.FileSystem) extends ManagedComponent {
|
|||||||
|
|
||||||
@LuaCallback("setLabel")
|
@LuaCallback("setLabel")
|
||||||
def setLabel(message: Message): Array[Object] = {
|
def setLabel(message: Message): Array[Object] = {
|
||||||
label = message.checkString(0)
|
label = message.checkString(1)
|
||||||
if (label.length > 16)
|
if (label.length > 16)
|
||||||
label = label.substring(0, 16)
|
label = label.substring(0, 16)
|
||||||
result(true)
|
result(true)
|
||||||
@ -43,24 +43,24 @@ class FileSystem(val fileSystem: api.fs.FileSystem) extends ManagedComponent {
|
|||||||
|
|
||||||
@LuaCallback("exists")
|
@LuaCallback("exists")
|
||||||
def exists(message: Message): Array[Object] =
|
def exists(message: Message): Array[Object] =
|
||||||
result(fileSystem.exists(clean(message.checkString(0))))
|
result(fileSystem.exists(clean(message.checkString(1))))
|
||||||
|
|
||||||
@LuaCallback("size")
|
@LuaCallback("size")
|
||||||
def size(message: Message): Array[Object] =
|
def size(message: Message): Array[Object] =
|
||||||
result(fileSystem.size(clean(message.checkString(0))))
|
result(fileSystem.size(clean(message.checkString(1))))
|
||||||
|
|
||||||
@LuaCallback("isDirectory")
|
@LuaCallback("isDirectory")
|
||||||
def isDirectory(message: Message): Array[Object] =
|
def isDirectory(message: Message): Array[Object] =
|
||||||
result(fileSystem.isDirectory(clean(message.checkString(0))))
|
result(fileSystem.isDirectory(clean(message.checkString(1))))
|
||||||
|
|
||||||
@LuaCallback("lastModified")
|
@LuaCallback("lastModified")
|
||||||
def lastModified(message: Message): Array[Object] =
|
def lastModified(message: Message): Array[Object] =
|
||||||
result(fileSystem.lastModified(clean(message.checkString(0))))
|
result(fileSystem.lastModified(clean(message.checkString(1))))
|
||||||
|
|
||||||
@LuaCallback("list")
|
@LuaCallback("list")
|
||||||
def list(message: Message): Array[Object] =
|
def list(message: Message): Array[Object] =
|
||||||
Option(fileSystem.list(clean(message.checkString(0)))) match {
|
Option(fileSystem.list(clean(message.checkString(1)))) match {
|
||||||
case Some(list) => list.map(_.asInstanceOf[AnyRef])
|
case Some(list) => Array(list)
|
||||||
case _ => null
|
case _ => null
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,23 +68,23 @@ class FileSystem(val fileSystem: api.fs.FileSystem) extends ManagedComponent {
|
|||||||
def makeDirectory(message: Message): Array[Object] = {
|
def makeDirectory(message: Message): Array[Object] = {
|
||||||
def recurse(path: String): Boolean = !fileSystem.exists(path) && (fileSystem.makeDirectory(path) ||
|
def recurse(path: String): Boolean = !fileSystem.exists(path) && (fileSystem.makeDirectory(path) ||
|
||||||
(recurse(path.split("/").dropRight(1).mkString("/")) && fileSystem.makeDirectory(path)))
|
(recurse(path.split("/").dropRight(1).mkString("/")) && fileSystem.makeDirectory(path)))
|
||||||
result(recurse(clean(message.checkString(0))))
|
result(recurse(clean(message.checkString(1))))
|
||||||
}
|
}
|
||||||
|
|
||||||
@LuaCallback("remove")
|
@LuaCallback("remove")
|
||||||
def remove(message: Message): Array[Object] = {
|
def remove(message: Message): Array[Object] = {
|
||||||
def recurse(parent: String): Boolean = (!fileSystem.isDirectory(parent) ||
|
def recurse(parent: String): Boolean = (!fileSystem.isDirectory(parent) ||
|
||||||
fileSystem.list(parent).forall(child => recurse(parent + "/" + child))) && fileSystem.delete(parent)
|
fileSystem.list(parent).forall(child => recurse(parent + "/" + child))) && fileSystem.delete(parent)
|
||||||
result(recurse(clean(message.checkString(0))))
|
result(recurse(clean(message.checkString(1))))
|
||||||
}
|
}
|
||||||
|
|
||||||
@LuaCallback("rename")
|
@LuaCallback("rename")
|
||||||
def rename(message: Message): Array[Object] =
|
def rename(message: Message): Array[Object] =
|
||||||
result(fileSystem.rename(clean(message.checkString(0)), clean(message.checkString(1))))
|
result(fileSystem.rename(clean(message.checkString(1)), clean(message.checkString(2))))
|
||||||
|
|
||||||
@LuaCallback("close")
|
@LuaCallback("close")
|
||||||
def close(message: Message): Array[Object] = {
|
def close(message: Message): Array[Object] = {
|
||||||
val handle = message.checkInteger(0)
|
val handle = message.checkInteger(1)
|
||||||
Option(fileSystem.file(handle)) match {
|
Option(fileSystem.file(handle)) match {
|
||||||
case Some(file) =>
|
case Some(file) =>
|
||||||
owners.get(message.source.address) match {
|
owners.get(message.source.address) match {
|
||||||
@ -101,8 +101,8 @@ class FileSystem(val fileSystem: api.fs.FileSystem) extends ManagedComponent {
|
|||||||
if (owners.get(message.source.address).fold(false)(_.size >= Config.maxHandles))
|
if (owners.get(message.source.address).fold(false)(_.size >= Config.maxHandles))
|
||||||
result(Unit, "too many open handles")
|
result(Unit, "too many open handles")
|
||||||
else {
|
else {
|
||||||
val path = message.checkString(0)
|
val path = message.checkString(1)
|
||||||
val mode = message.checkString(1)
|
val mode = if (message.data.length > 2) message.checkString(2) else "r"
|
||||||
val handle = fileSystem.open(clean(path), Mode.parse(mode))
|
val handle = fileSystem.open(clean(path), Mode.parse(mode))
|
||||||
if (handle > 0) {
|
if (handle > 0) {
|
||||||
owners.getOrElseUpdate(message.source.address, mutable.Set.empty[Int]) += handle
|
owners.getOrElseUpdate(message.source.address, mutable.Set.empty[Int]) += handle
|
||||||
@ -112,8 +112,8 @@ class FileSystem(val fileSystem: api.fs.FileSystem) extends ManagedComponent {
|
|||||||
|
|
||||||
@LuaCallback("read")
|
@LuaCallback("read")
|
||||||
def read(message: Message): Array[Object] = {
|
def read(message: Message): Array[Object] = {
|
||||||
val handle = message.checkInteger(0)
|
val handle = message.checkInteger(1)
|
||||||
val n = message.checkInteger(1)
|
val n = message.checkInteger(2)
|
||||||
Option(fileSystem.file(handle)) match {
|
Option(fileSystem.file(handle)) match {
|
||||||
case None => throw new IOException("bad file descriptor")
|
case None => throw new IOException("bad file descriptor")
|
||||||
case Some(file) =>
|
case Some(file) =>
|
||||||
@ -139,9 +139,9 @@ class FileSystem(val fileSystem: api.fs.FileSystem) extends ManagedComponent {
|
|||||||
|
|
||||||
@LuaCallback("seek")
|
@LuaCallback("seek")
|
||||||
def seek(message: Message): Array[Object] = {
|
def seek(message: Message): Array[Object] = {
|
||||||
val handle = message.checkInteger(0)
|
val handle = message.checkInteger(1)
|
||||||
val whence = message.checkString(1)
|
val whence = message.checkString(2)
|
||||||
val offset = message.checkInteger(2)
|
val offset = message.checkInteger(3)
|
||||||
Option(fileSystem.file(handle)) match {
|
Option(fileSystem.file(handle)) match {
|
||||||
case Some(file) =>
|
case Some(file) =>
|
||||||
whence match {
|
whence match {
|
||||||
@ -157,8 +157,8 @@ class FileSystem(val fileSystem: api.fs.FileSystem) extends ManagedComponent {
|
|||||||
|
|
||||||
@LuaCallback("write")
|
@LuaCallback("write")
|
||||||
def write(message: Message): Array[Object] = {
|
def write(message: Message): Array[Object] = {
|
||||||
val handle = message.checkInteger(0)
|
val handle = message.checkInteger(1)
|
||||||
val value = message.checkByteArray(1)
|
val value = message.checkByteArray(2)
|
||||||
Option(fileSystem.file(handle)) match {
|
Option(fileSystem.file(handle)) match {
|
||||||
case Some(file) => file.write(value); result(true)
|
case Some(file) => file.write(value); result(true)
|
||||||
case _ => throw new IOException("bad file descriptor")
|
case _ => throw new IOException("bad file descriptor")
|
||||||
|
@ -15,7 +15,7 @@ class GraphicsCard(val maxResolution: (Int, Int)) extends ManagedComponent {
|
|||||||
|
|
||||||
@LuaCallback("bind")
|
@LuaCallback("bind")
|
||||||
def bind(message: Message): Array[Object] = {
|
def bind(message: Message): Array[Object] = {
|
||||||
val address = message.checkString(0)
|
val address = message.checkString(1)
|
||||||
node.network.node(address) match {
|
node.network.node(address) match {
|
||||||
case null => Array(Unit, "invalid address")
|
case null => Array(Unit, "invalid address")
|
||||||
case value if value.name() == "screen" =>
|
case value if value.name() == "screen" =>
|
||||||
@ -30,8 +30,8 @@ class GraphicsCard(val maxResolution: (Int, Int)) extends ManagedComponent {
|
|||||||
|
|
||||||
@LuaCallback("setResolution")
|
@LuaCallback("setResolution")
|
||||||
def setResolution(message: Message): Array[Object] = {
|
def setResolution(message: Message): Array[Object] = {
|
||||||
val w = message.checkInteger(0)
|
val w = message.checkInteger(1)
|
||||||
val h = message.checkInteger(1)
|
val h = message.checkInteger(2)
|
||||||
val (mw, mh) = maxResolution
|
val (mw, mh) = maxResolution
|
||||||
if (w <= mw && h <= mh)
|
if (w <= mw && h <= mh)
|
||||||
trySend("screen.resolution=", w, h)
|
trySend("screen.resolution=", w, h)
|
||||||
@ -50,18 +50,18 @@ class GraphicsCard(val maxResolution: (Int, Int)) extends ManagedComponent {
|
|||||||
|
|
||||||
@LuaCallback("set")
|
@LuaCallback("set")
|
||||||
def set(message: Message): Array[Object] = {
|
def set(message: Message): Array[Object] = {
|
||||||
val x = message.checkInteger(0)
|
val x = message.checkInteger(1)
|
||||||
val y = message.checkInteger(1)
|
val y = message.checkInteger(2)
|
||||||
val value = message.checkString(2)
|
val value = message.checkString(3)
|
||||||
trySend("screen.set", x - 1, y - 1, value)
|
trySend("screen.set", x - 1, y - 1, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@LuaCallback("fill")
|
@LuaCallback("fill")
|
||||||
def fill(message: Message): Array[Object] = {
|
def fill(message: Message): Array[Object] = {
|
||||||
val x = message.checkInteger(0)
|
val x = message.checkInteger(1)
|
||||||
val y = message.checkInteger(1)
|
val y = message.checkInteger(2)
|
||||||
val w = message.checkInteger(2)
|
val w = message.checkInteger(3)
|
||||||
val h = message.checkInteger(3)
|
val h = message.checkInteger(4)
|
||||||
val value = message.checkString(4)
|
val value = message.checkString(4)
|
||||||
if (value.length == 1)
|
if (value.length == 1)
|
||||||
trySend("screen.fill", x - 1, y - 1, w, h, value.charAt(0))
|
trySend("screen.fill", x - 1, y - 1, w, h, value.charAt(0))
|
||||||
@ -71,12 +71,12 @@ class GraphicsCard(val maxResolution: (Int, Int)) extends ManagedComponent {
|
|||||||
|
|
||||||
@LuaCallback("copy")
|
@LuaCallback("copy")
|
||||||
def copy(message: Message): Array[Object] = {
|
def copy(message: Message): Array[Object] = {
|
||||||
val x = message.checkInteger(0)
|
val x = message.checkInteger(1)
|
||||||
val y = message.checkInteger(1)
|
val y = message.checkInteger(2)
|
||||||
val w = message.checkInteger(2)
|
val w = message.checkInteger(3)
|
||||||
val h = message.checkInteger(3)
|
val h = message.checkInteger(4)
|
||||||
val tx = message.checkInteger(4)
|
val tx = message.checkInteger(5)
|
||||||
val ty = message.checkInteger(5)
|
val ty = message.checkInteger(6)
|
||||||
trySend("screen.copy", x - 1, y - 1, w, h, tx, ty)
|
trySend("screen.copy", x - 1, y - 1, w, h, tx, ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,40 +17,40 @@ class NetworkCard extends ManagedComponent {
|
|||||||
|
|
||||||
@LuaCallback("open")
|
@LuaCallback("open")
|
||||||
def open(message: Message): Array[Object] = {
|
def open(message: Message): Array[Object] = {
|
||||||
val port = checkPort(message.checkInteger(0))
|
val port = checkPort(message.checkInteger(1))
|
||||||
result(openPorts.add(port))
|
result(openPorts.add(port))
|
||||||
}
|
}
|
||||||
|
|
||||||
@LuaCallback("close")
|
@LuaCallback("close")
|
||||||
def close(message: Message): Array[Object] = {
|
def close(message: Message): Array[Object] = {
|
||||||
if (message.data.length == 0) {
|
if (message.data.length < 2) {
|
||||||
openPorts.clear()
|
openPorts.clear()
|
||||||
result(true)
|
result(true)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val port = checkPort(message.checkInteger(0))
|
val port = checkPort(message.checkInteger(1))
|
||||||
result(openPorts.remove(port))
|
result(openPorts.remove(port))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@LuaCallback("isOpen")
|
@LuaCallback("isOpen")
|
||||||
def isOpen(message: Message): Array[Object] = {
|
def isOpen(message: Message): Array[Object] = {
|
||||||
val port = checkPort(message.checkInteger(0))
|
val port = checkPort(message.checkInteger(1))
|
||||||
result(openPorts.contains(port))
|
result(openPorts.contains(port))
|
||||||
}
|
}
|
||||||
|
|
||||||
@LuaCallback("send")
|
@LuaCallback("send")
|
||||||
def send(message: Message): Array[Object] = {
|
def send(message: Message): Array[Object] = {
|
||||||
val address = message.checkString(0)
|
val address = message.checkString(1)
|
||||||
val port = checkPort(message.checkInteger(1))
|
val port = checkPort(message.checkInteger(2))
|
||||||
node.network.sendToAddress(node, address, "network.message", Seq(Int.box(port)) ++ message.data.drop(2): _*)
|
node.network.sendToAddress(node, address, "network.message", Seq(Int.box(port)) ++ message.data.drop(3): _*)
|
||||||
result(true)
|
result(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@LuaCallback("broadcast")
|
@LuaCallback("broadcast")
|
||||||
def broadcast(message: Message): Array[Object] = {
|
def broadcast(message: Message): Array[Object] = {
|
||||||
val port = checkPort(message.checkInteger(0))
|
val port = checkPort(message.checkInteger(1))
|
||||||
node.network.sendToVisible(node, "network.message", Seq(Int.box(port)) ++ message.data.drop(1): _*)
|
node.network.sendToVisible(node, "network.message", Seq(Int.box(port)) ++ message.data.drop(2): _*)
|
||||||
result(true)
|
result(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,22 +10,22 @@ class RedstoneCard extends ManagedComponent {
|
|||||||
|
|
||||||
@LuaCallback("getInput")
|
@LuaCallback("getInput")
|
||||||
def getInput(message: Message): Array[Object] = {
|
def getInput(message: Message): Array[Object] = {
|
||||||
val side = message.checkInteger(0)
|
val side = message.checkInteger(1)
|
||||||
node.network.sendToAddress(node, message.source.address,
|
node.network.sendToAddress(node, message.source.address,
|
||||||
"redstone.input", ForgeDirection.getOrientation(side))
|
"redstone.input", ForgeDirection.getOrientation(side))
|
||||||
}
|
}
|
||||||
|
|
||||||
@LuaCallback("getOutput")
|
@LuaCallback("getOutput")
|
||||||
def getOutput(message: Message): Array[Object] = {
|
def getOutput(message: Message): Array[Object] = {
|
||||||
val side = message.checkInteger(0)
|
val side = message.checkInteger(1)
|
||||||
node.network.sendToAddress(node, message.source.address,
|
node.network.sendToAddress(node, message.source.address,
|
||||||
"redstone.output", ForgeDirection.getOrientation(side))
|
"redstone.output", ForgeDirection.getOrientation(side))
|
||||||
}
|
}
|
||||||
|
|
||||||
@LuaCallback("setOutput")
|
@LuaCallback("setOutput")
|
||||||
def setOutput(message: Message): Array[Object] = {
|
def setOutput(message: Message): Array[Object] = {
|
||||||
val side = message.checkInteger(0)
|
val side = message.checkInteger(1)
|
||||||
val value = message.checkInteger(1)
|
val value = message.checkInteger(2)
|
||||||
node.network.sendToAddress(node, message.source.address,
|
node.network.sendToAddress(node, message.source.address,
|
||||||
"redstone.output=", ForgeDirection.getOrientation(side.toInt), Int.box(value))
|
"redstone.output=", ForgeDirection.getOrientation(side.toInt), Int.box(value))
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package li.cil.oc.server.network
|
|||||||
|
|
||||||
import cpw.mods.fml.common.FMLCommonHandler
|
import cpw.mods.fml.common.FMLCommonHandler
|
||||||
import cpw.mods.fml.relauncher.Side
|
import cpw.mods.fml.relauncher.Side
|
||||||
|
import java.lang.reflect.InvocationTargetException
|
||||||
import li.cil.oc.api
|
import li.cil.oc.api
|
||||||
import li.cil.oc.api.network.environment.{Environment, LuaCallback}
|
import li.cil.oc.api.network.environment.{Environment, LuaCallback}
|
||||||
import li.cil.oc.api.network.{Message, Visibility}
|
import li.cil.oc.api.network.{Message, Visibility}
|
||||||
@ -60,18 +61,16 @@ class Component(host: Environment, name: String, reachability: Visibility) exten
|
|||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
override def receive(message: Message) = {
|
override def receive(message: Message) = {
|
||||||
if (message.name == "computer.started" && canBeSeenBy(message.source))
|
|
||||||
network.sendToAddress(this, message.source.address, "computer.signal", "component_added")
|
|
||||||
|
|
||||||
if (message.name == "component.methods")
|
if (message.name == "component.methods")
|
||||||
if (canBeSeenBy(message.source))
|
if (canBeSeenBy(message.source))
|
||||||
Array(luaCallbacks.keys.toSeq: _*)
|
Array(Array(luaCallbacks.keys.toSeq: _*))
|
||||||
else null
|
else null
|
||||||
else if (message.name == "component.call")
|
else if (message.name == "component.invoke") {
|
||||||
luaCallbacks.get(message.name) match {
|
luaCallbacks.get(message.data()(0).asInstanceOf[String]) match {
|
||||||
case Some(callback) => callback(host, message)
|
case Some(callback) => callback(host, message)
|
||||||
case _ => throw new NoSuchMethodException()
|
case _ => throw new NoSuchMethodException()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else super.receive(message)
|
else super.receive(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +112,11 @@ object Component {
|
|||||||
throw new IllegalArgumentException("Invalid use of LuaCallback annotation (name must not be null or empty).")
|
throw new IllegalArgumentException("Invalid use of LuaCallback annotation (name must not be null or empty).")
|
||||||
}
|
}
|
||||||
else if (!callbacks.contains(a.value)) {
|
else if (!callbacks.contains(a.value)) {
|
||||||
callbacks += a.value -> ((o, e) => m.invoke(o, e).asInstanceOf[Array[Object]])
|
callbacks += a.value -> ((o, e) => try {
|
||||||
|
m.invoke(o, e).asInstanceOf[Array[Object]]
|
||||||
|
} catch {
|
||||||
|
case e: InvocationTargetException => throw e.getCause
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
package li.cil.oc.server.network
|
package li.cil.oc.server.network
|
||||||
|
|
||||||
import java.util.logging.Level
|
import li.cil.oc.api
|
||||||
import li.cil.oc.api.network.Visibility
|
import li.cil.oc.api.network.Visibility
|
||||||
import li.cil.oc.api.network.environment.Environment
|
import li.cil.oc.api.network.environment.Environment
|
||||||
import li.cil.oc.server.network
|
import li.cil.oc.server.network
|
||||||
import li.cil.oc.{api, OpenComputers}
|
|
||||||
import net.minecraft.block.Block
|
import net.minecraft.block.Block
|
||||||
import net.minecraft.tileentity.TileEntity
|
import net.minecraft.tileentity.TileEntity
|
||||||
import net.minecraft.world.{IBlockAccess, World}
|
import net.minecraft.world.{IBlockAccess, World}
|
||||||
@ -292,12 +291,13 @@ class Network private(private val addressedNodes: mutable.Map[String, Network.No
|
|||||||
}
|
}
|
||||||
|
|
||||||
private def send(message: Network.Message, targets: Iterable[api.network.Node]) = {
|
private def send(message: Network.Message, targets: Iterable[api.network.Node]) = {
|
||||||
|
var error: Option[Throwable] = None
|
||||||
def protectedSend(target: api.network.Node) = try {
|
def protectedSend(target: api.network.Node) = try {
|
||||||
//println("receive(" + message.name + "(" + message.data.mkString(", ") + "): " + message.source.address.get + ":" + message.source.name + " -> " + target.address.get + ":" + target.name + ")")
|
//println("receive(" + message.name + "(" + message.data.mkString(", ") + "): " + message.source.address.get + ":" + message.source.name + " -> " + target.address.get + ":" + target.name + ")")
|
||||||
target.receive(message)
|
target.receive(message)
|
||||||
} catch {
|
} catch {
|
||||||
case e: Throwable =>
|
case e: Throwable =>
|
||||||
OpenComputers.log.log(Level.WARNING, "Error in message handler", e)
|
if (error.isEmpty) error = Some(e)
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,7 +313,10 @@ class Network private(private val addressedNodes: mutable.Map[String, Network.No
|
|||||||
case null => // Ignore.
|
case null => // Ignore.
|
||||||
case r => result = r
|
case r => result = r
|
||||||
}
|
}
|
||||||
result
|
error match {
|
||||||
|
case Some(e) => throw e
|
||||||
|
case _ => result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -477,7 +480,7 @@ object Network extends api.detail.NetworkAPI {
|
|||||||
|
|
||||||
private def typeError(index: Int, have: AnyRef, want: String) =
|
private def typeError(index: Int, have: AnyRef, want: String) =
|
||||||
new IllegalArgumentException(
|
new IllegalArgumentException(
|
||||||
"bad argument #%d (%s expected, got %have)".
|
"bad argument #%d (%s expected, got %s)".
|
||||||
format(index + 1, want, typeName(have)))
|
format(index + 1, want, typeName(have)))
|
||||||
|
|
||||||
private def typeName(value: AnyRef): String = value match {
|
private def typeName(value: AnyRef): String = value match {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user