mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-17 03:05:30 -04:00
forbid access to string metatable for sandbox; minor restructuring of libraries (mostly sorting by name); saving managed environments attached to an adapter. this is not very reliable, sadly, since there are way too many ways neighbors can change outside our control (chunks only partially loaded, types removed across games due to other mod changes, mods that move blocks such as RIM, ...); making some effort to re-attach peripherals using their previous address; removed connection code on chunk load in network manager, since this is also handled by the check in the tile entities' update function
This commit is contained in:
parent
fcdb75e4bc
commit
ab785eb3ac
@ -57,7 +57,10 @@ sandbox = {
|
|||||||
dofile = nil, -- in lib/base.lua
|
dofile = nil, -- in lib/base.lua
|
||||||
error = error,
|
error = error,
|
||||||
_G = nil, -- see below
|
_G = nil, -- see below
|
||||||
getmetatable = getmetatable,
|
getmetatable = function(t)
|
||||||
|
if type(t) == "string" then return nil end
|
||||||
|
return getmetatable(t)
|
||||||
|
end,
|
||||||
ipairs = ipairs,
|
ipairs = ipairs,
|
||||||
load = function(ld, source, mode, env)
|
load = function(ld, source, mode, env)
|
||||||
assert((mode or "t") == "t", "unsupported mode")
|
assert((mode or "t") == "t", "unsupported mode")
|
||||||
@ -143,8 +146,8 @@ sandbox = {
|
|||||||
uchar = string.uchar,
|
uchar = string.uchar,
|
||||||
|
|
||||||
trim = function(s) -- from http://lua-users.org/wiki/StringTrim
|
trim = function(s) -- from http://lua-users.org/wiki/StringTrim
|
||||||
local from = s:match("^%s*()")
|
local from = string.match(s, "^%s*()")
|
||||||
return from > #s and "" or s:match(".*%S", from)
|
return from > #s and "" or string.match(s, ".*%S", from)
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -22,9 +22,9 @@ for path, proxy in pairs(mounts) do
|
|||||||
local used, total = proxy.spaceUsed(), proxy.spaceTotal()
|
local used, total = proxy.spaceUsed(), proxy.spaceTotal()
|
||||||
local available, percent
|
local available, percent
|
||||||
if total == "unlimited" then
|
if total == "unlimited" then
|
||||||
used = "N/A"
|
used = used or "N/A"
|
||||||
available = "unlimited"
|
available = "unlimited"
|
||||||
percent = 0
|
percent = "0%"
|
||||||
else
|
else
|
||||||
available = total - used
|
available = total - used
|
||||||
percent = used / total
|
percent = used / total
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
local listeners, timers = {}, {}
|
local event, listeners, timers = {}, {}, {}
|
||||||
|
|
||||||
local function matches(signal, name, filter)
|
local function matches(signal, name, filter)
|
||||||
if name and not (type(signal[1]) == "string" and signal[1]:match(name))
|
if name and not (type(signal[1]) == "string" and signal[1]:match(name))
|
||||||
@ -58,7 +58,14 @@ end
|
|||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
event = {}
|
function event.cancel(timerId)
|
||||||
|
checkArg(1, timerId, "number")
|
||||||
|
if timers[timerId] then
|
||||||
|
timers[timerId] = nil
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
--[[ Error handler for ALL event callbacks. If this throws an error or is not,
|
--[[ Error handler for ALL event callbacks. If this throws an error or is not,
|
||||||
set the computer will immediately shut down. ]]
|
set the computer will immediately shut down. ]]
|
||||||
@ -103,36 +110,6 @@ function event.listen(name, callback)
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function event.cancel(timerId)
|
|
||||||
checkArg(1, timerId, "number")
|
|
||||||
if timers[timerId] then
|
|
||||||
timers[timerId] = nil
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
function event.timer(interval, callback, times)
|
|
||||||
checkArg(1, interval, "number")
|
|
||||||
checkArg(2, callback, "function")
|
|
||||||
checkArg(3, times, "number", "nil")
|
|
||||||
local id
|
|
||||||
repeat
|
|
||||||
id = math.floor(math.random(1, 0x7FFFFFFF))
|
|
||||||
until not timers[id]
|
|
||||||
timers[id] = {
|
|
||||||
interval = interval,
|
|
||||||
after = os.uptime() + interval,
|
|
||||||
callback = callback,
|
|
||||||
times = times or 1
|
|
||||||
}
|
|
||||||
return id
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function event.pull(...)
|
function event.pull(...)
|
||||||
local args = table.pack(...)
|
local args = table.pack(...)
|
||||||
local seconds, name, filter
|
local seconds, name, filter
|
||||||
@ -182,3 +159,24 @@ function event.shouldInterrupt()
|
|||||||
keyboard.isAltDown() and
|
keyboard.isAltDown() and
|
||||||
keyboard.isKeyDown(keyboard.keys.c)
|
keyboard.isKeyDown(keyboard.keys.c)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function event.timer(interval, callback, times)
|
||||||
|
checkArg(1, interval, "number")
|
||||||
|
checkArg(2, callback, "function")
|
||||||
|
checkArg(3, times, "number", "nil")
|
||||||
|
local id
|
||||||
|
repeat
|
||||||
|
id = math.floor(math.random(1, 0x7FFFFFFF))
|
||||||
|
until not timers[id]
|
||||||
|
timers[id] = {
|
||||||
|
interval = interval,
|
||||||
|
after = os.uptime() + interval,
|
||||||
|
callback = callback,
|
||||||
|
times = times or 1
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
_G.event = event
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
local filesystem, fileStream = {}, {}
|
||||||
local isAutorunEnabled = true
|
local isAutorunEnabled = true
|
||||||
local mtab = {children={}}
|
local mtab = {children={}}
|
||||||
|
|
||||||
@ -58,8 +59,6 @@ end
|
|||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
filesystem = {}
|
|
||||||
|
|
||||||
function filesystem.autorun(enabled)
|
function filesystem.autorun(enabled)
|
||||||
if enabled ~= nil then
|
if enabled ~= nil then
|
||||||
checkArg(1, enabled, "boolean")
|
checkArg(1, enabled, "boolean")
|
||||||
@ -204,8 +203,6 @@ function filesystem.umount(fsOrPath)
|
|||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function filesystem.exists(path)
|
function filesystem.exists(path)
|
||||||
local node, rest = findNode(path)
|
local node, rest = findNode(path)
|
||||||
if not rest then -- virtual directory
|
if not rest then -- virtual directory
|
||||||
@ -269,8 +266,6 @@ function filesystem.list(path)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function filesystem.makeDirectory(path)
|
function filesystem.makeDirectory(path)
|
||||||
local node, rest = findNode(path)
|
local node, rest = findNode(path)
|
||||||
if node.fs and rest then
|
if node.fs and rest then
|
||||||
@ -336,10 +331,6 @@ function filesystem.copy(fromPath, toPath)
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
local fileStream = {}
|
|
||||||
|
|
||||||
function fileStream:close()
|
function fileStream:close()
|
||||||
self.fs.close(self.handle)
|
self.fs.close(self.handle)
|
||||||
self.handle = nil
|
self.handle = nil
|
||||||
@ -366,8 +357,6 @@ function fileStream:write(str)
|
|||||||
return self.fs.write(self.handle, str)
|
return self.fs.write(self.handle, str)
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function filesystem.open(path, mode)
|
function filesystem.open(path, mode)
|
||||||
checkArg(1, path, "string")
|
checkArg(1, path, "string")
|
||||||
mode = tostring(mode or "r")
|
mode = tostring(mode or "r")
|
||||||
@ -407,24 +396,20 @@ end
|
|||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
fs = filesystem
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
local function onComponentAdded(_, address, componentType)
|
local function onComponentAdded(_, address, componentType)
|
||||||
if componentType == "filesystem" then
|
if componentType == "filesystem" then
|
||||||
local proxy = component.proxy(address)
|
local proxy = component.proxy(address)
|
||||||
if proxy then
|
if proxy then
|
||||||
local name = address:sub(1, 3)
|
local name = address:sub(1, 3)
|
||||||
while fs.exists(fs.concat("/mnt", name)) and
|
while filesystem.exists(filesystem.concat("/mnt", name)) and
|
||||||
name:len() < address:len() -- just to be on the safe side
|
name:len() < address:len() -- just to be on the safe side
|
||||||
do
|
do
|
||||||
name = address:sub(1, name:len() + 1)
|
name = address:sub(1, name:len() + 1)
|
||||||
end
|
end
|
||||||
name = fs.concat("/mnt", name)
|
name = filesystem.concat("/mnt", name)
|
||||||
fs.mount(proxy, name)
|
filesystem.mount(proxy, name)
|
||||||
if isAutorunEnabled then
|
if isAutorunEnabled then
|
||||||
shell.execute(fs.concat(name, "autorun"), proxy)
|
shell.execute(filesystem.concat(name, "autorun"), proxy)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -432,10 +417,13 @@ end
|
|||||||
|
|
||||||
local function onComponentRemoved(_, address, componentType)
|
local function onComponentRemoved(_, address, componentType)
|
||||||
if componentType == "filesystem" then
|
if componentType == "filesystem" then
|
||||||
fs.umount(address)
|
filesystem.umount(address)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
_G.filesystem = filesystem
|
||||||
|
_G.fs = filesystem
|
||||||
|
|
||||||
return function()
|
return function()
|
||||||
event.listen("component_added", onComponentAdded)
|
event.listen("component_added", onComponentAdded)
|
||||||
event.listen("component_removed", onComponentRemoved)
|
event.listen("component_removed", onComponentRemoved)
|
||||||
|
@ -1,4 +1,19 @@
|
|||||||
local file = {}
|
local io, file = {}, {}
|
||||||
|
|
||||||
|
function file.new(mode, stream, nogc)
|
||||||
|
local result = {
|
||||||
|
mode = mode or "r",
|
||||||
|
stream = stream,
|
||||||
|
buffer = "",
|
||||||
|
bufferSize = math.max(128, math.min(8 * 1024, os.freeMemory() / 8)),
|
||||||
|
bufferMode = "full"
|
||||||
|
}
|
||||||
|
local metatable = {
|
||||||
|
__index = file,
|
||||||
|
__metatable = "file"
|
||||||
|
}
|
||||||
|
return setmetatable(result, metatable)
|
||||||
|
end
|
||||||
|
|
||||||
function file:close()
|
function file:close()
|
||||||
if self.mode ~= "r" and self.mode ~= "rb" then
|
if self.mode ~= "r" and self.mode ~= "rb" then
|
||||||
@ -252,23 +267,6 @@ end
|
|||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
function file.new(mode, stream, nogc)
|
|
||||||
local result = {
|
|
||||||
mode = mode or "r",
|
|
||||||
stream = stream,
|
|
||||||
buffer = "",
|
|
||||||
bufferSize = math.max(128, math.min(8 * 1024, os.freeMemory() / 8)),
|
|
||||||
bufferMode = "full"
|
|
||||||
}
|
|
||||||
local metatable = {
|
|
||||||
__index = file,
|
|
||||||
__metatable = "file"
|
|
||||||
}
|
|
||||||
return setmetatable(result, metatable)
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
local stdinStream = {handle="stdin"}
|
local stdinStream = {handle="stdin"}
|
||||||
local stdoutStream = {handle="stdout"}
|
local stdoutStream = {handle="stdout"}
|
||||||
local stdinHistory = {}
|
local stdinHistory = {}
|
||||||
@ -302,8 +300,6 @@ stdoutStream.seek = badFileDescriptor
|
|||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
io = {}
|
|
||||||
|
|
||||||
io.stdin = file.new("r", stdinStream, true)
|
io.stdin = file.new("r", stdinStream, true)
|
||||||
io.stdout = file.new("w", stdoutStream, true)
|
io.stdout = file.new("w", stdoutStream, true)
|
||||||
io.stderr = io.stdout
|
io.stderr = io.stdout
|
||||||
@ -420,3 +416,7 @@ end
|
|||||||
function io.write(...)
|
function io.write(...)
|
||||||
return io.output():write(...)
|
return io.output():write(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
_G.io = io
|
||||||
|
@ -1,9 +1,4 @@
|
|||||||
local pressedChars = {}
|
local keyboard, pressedChars, pressedCodes = {}, {}, {}
|
||||||
local pressedCodes = {}
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
keyboard = {}
|
|
||||||
|
|
||||||
keyboard.keys = {
|
keyboard.keys = {
|
||||||
["1"] = 0x02,
|
["1"] = 0x02,
|
||||||
@ -140,10 +135,20 @@ for k, v in pairs(keyboard.keys) do
|
|||||||
keyboard.keys[v] = k
|
keyboard.keys[v] = k
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function keyboard.isAltDown()
|
||||||
|
return (pressedCodes[keyboard.keys.lmenu] or pressedCodes[keyboard.keys.rmenu]) ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
function keyboard.isControl(char)
|
function keyboard.isControl(char)
|
||||||
return type(char) == "number" and (char < 0x20 or (char >= 0x7F and char <= 0x9F))
|
return type(char) == "number" and (char < 0x20 or (char >= 0x7F and char <= 0x9F))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function keyboard.isControlDown()
|
||||||
|
return (pressedCodes[keyboard.keys.lcontrol] or pressedCodes[keyboard.keys.rcontrol]) ~= nil
|
||||||
|
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
|
||||||
@ -153,14 +158,6 @@ function keyboard.isKeyDown(charOrCode)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function keyboard.isControlDown()
|
|
||||||
return (pressedCodes[keyboard.keys.lcontrol] or pressedCodes[keyboard.keys.rcontrol]) ~= nil
|
|
||||||
end
|
|
||||||
|
|
||||||
function keyboard.isAltDown()
|
|
||||||
return (pressedCodes[keyboard.keys.lmenu] or pressedCodes[keyboard.keys.rmenu]) ~= nil
|
|
||||||
end
|
|
||||||
|
|
||||||
function keyboard.isShiftDown()
|
function keyboard.isShiftDown()
|
||||||
return (pressedCodes[keyboard.keys.lshift] or pressedCodes[keyboard.keys.rshift]) ~= nil
|
return (pressedCodes[keyboard.keys.lshift] or pressedCodes[keyboard.keys.rshift]) ~= nil
|
||||||
end
|
end
|
||||||
@ -188,6 +185,8 @@ local function onComponentUnavailable(_, componentType)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
_G.keyboard = keyboard
|
||||||
|
|
||||||
return function()
|
return function()
|
||||||
event.listen("key_down", onKeyDown)
|
event.listen("key_down", onKeyDown)
|
||||||
event.listen("key_up", onKeyUp)
|
event.listen("key_up", onKeyUp)
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
local function stringToSide(side)
|
|
||||||
if type(side) == "string" and rs.sides[side] then
|
|
||||||
return rs.sides[side]
|
|
||||||
end
|
|
||||||
return side
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
redstone = {}
|
|
||||||
rs = redstone
|
|
||||||
|
|
||||||
rs.sides = {
|
|
||||||
[0] = "bottom",
|
|
||||||
[1] = "top",
|
|
||||||
[2] = "back",
|
|
||||||
[3] = "front",
|
|
||||||
[4] = "right",
|
|
||||||
[5] = "left"
|
|
||||||
}
|
|
||||||
for k, v in pairs(rs.sides) do
|
|
||||||
rs.sides[v] = k
|
|
||||||
end
|
|
||||||
rs.sides.up = rs.sides.top
|
|
||||||
rs.sides.down = rs.sides.bottom
|
|
@ -1,3 +1,4 @@
|
|||||||
|
local shell = {}
|
||||||
local cwd = "/"
|
local cwd = "/"
|
||||||
local path = {"/bin/", "/usr/bin/", "/home/bin/"}
|
local path = {"/bin/", "/usr/bin/", "/home/bin/"}
|
||||||
local aliases = {dir="ls", move="mv", rename="mv", copy="cp", del="rm",
|
local aliases = {dir="ls", move="mv", rename="mv", copy="cp", del="rm",
|
||||||
@ -48,8 +49,6 @@ end
|
|||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
shell = {}
|
|
||||||
|
|
||||||
function shell.alias(alias, ...)
|
function shell.alias(alias, ...)
|
||||||
checkArg(1, alias, "string")
|
checkArg(1, alias, "string")
|
||||||
local result = aliases[alias]
|
local result = aliases[alias]
|
||||||
@ -170,3 +169,7 @@ function shell.which(program)
|
|||||||
return nil, "program not found"
|
return nil, "program not found"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
_G.shell = shell
|
||||||
|
17
assets/opencomputers/lua/rom/lib/sides.lua
Normal file
17
assets/opencomputers/lua/rom/lib/sides.lua
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
local sides = {
|
||||||
|
[0] = "bottom",
|
||||||
|
[1] = "top",
|
||||||
|
[2] = "back",
|
||||||
|
[3] = "front",
|
||||||
|
[4] = "right",
|
||||||
|
[5] = "left"
|
||||||
|
}
|
||||||
|
for k, v in pairs(sides) do
|
||||||
|
sides[v] = k
|
||||||
|
end
|
||||||
|
sides.up = sides.top
|
||||||
|
sides.down = sides.bottom
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
_G.sides = sides
|
@ -1,3 +1,4 @@
|
|||||||
|
local term = {}
|
||||||
local gpuAvailable, screenAvailable = false, false
|
local gpuAvailable, screenAvailable = false, false
|
||||||
local cursorX, cursorY = 1, 1
|
local cursorX, cursorY = 1, 1
|
||||||
local cursorBlink = nil
|
local cursorBlink = nil
|
||||||
@ -17,12 +18,6 @@ end
|
|||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
term = {}
|
|
||||||
|
|
||||||
function term.isAvailable()
|
|
||||||
return gpuAvailable and screenAvailable
|
|
||||||
end
|
|
||||||
|
|
||||||
function term.clear()
|
function term.clear()
|
||||||
if term.isAvailable() then
|
if term.isAvailable() then
|
||||||
local w, h = gpu().getResolution()
|
local w, h = gpu().getResolution()
|
||||||
@ -75,7 +70,9 @@ function term.cursorBlink(enabled)
|
|||||||
return cursorBlink ~= nil
|
return cursorBlink ~= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
function term.isAvailable()
|
||||||
|
return gpuAvailable and screenAvailable
|
||||||
|
end
|
||||||
|
|
||||||
function term.read(history)
|
function term.read(history)
|
||||||
checkArg(1, history, "table", "nil")
|
checkArg(1, history, "table", "nil")
|
||||||
@ -379,6 +376,8 @@ local function onComponentUnavailable(_, componentType)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
_G.term = term
|
||||||
|
|
||||||
return function()
|
return function()
|
||||||
event.listen("component_available", onComponentAvailable)
|
event.listen("component_available", onComponentAvailable)
|
||||||
event.listen("component_unavailable", onComponentUnavailable)
|
event.listen("component_unavailable", onComponentUnavailable)
|
||||||
|
@ -21,14 +21,14 @@ class Proxy {
|
|||||||
Blocks.init()
|
Blocks.init()
|
||||||
Items.init()
|
Items.init()
|
||||||
|
|
||||||
|
api.Driver.add(driver.CommandBlock)
|
||||||
api.Driver.add(driver.FileSystem)
|
api.Driver.add(driver.FileSystem)
|
||||||
api.Driver.add(driver.GraphicsCard)
|
api.Driver.add(driver.GraphicsCard)
|
||||||
api.Driver.add(driver.Memory)
|
api.Driver.add(driver.Memory)
|
||||||
api.Driver.add(driver.NetworkCard)
|
api.Driver.add(driver.NetworkCard)
|
||||||
api.Driver.add(driver.RedstoneCard)
|
|
||||||
api.Driver.add(driver.CommandBlock)
|
|
||||||
api.Driver.add(driver.PowerSupply)
|
|
||||||
api.Driver.add(driver.Peripheral)
|
api.Driver.add(driver.Peripheral)
|
||||||
|
api.Driver.add(driver.PowerSupply)
|
||||||
|
api.Driver.add(driver.RedstoneCard)
|
||||||
|
|
||||||
MinecraftForge.EVENT_BUS.register(Computer)
|
MinecraftForge.EVENT_BUS.register(Computer)
|
||||||
MinecraftForge.EVENT_BUS.register(network.Network)
|
MinecraftForge.EVENT_BUS.register(network.Network)
|
||||||
|
@ -4,22 +4,19 @@ import dan200.computer.api.{ILuaContext, IComputerAccess, IPeripheral}
|
|||||||
import li.cil.oc.api
|
import li.cil.oc.api
|
||||||
import li.cil.oc.api.Network
|
import li.cil.oc.api.Network
|
||||||
import li.cil.oc.api.network._
|
import li.cil.oc.api.network._
|
||||||
import li.cil.oc.server
|
|
||||||
import li.cil.oc.server.driver
|
import li.cil.oc.server.driver
|
||||||
import net.minecraft.nbt.{NBTTagString, NBTTagList, NBTTagCompound}
|
import net.minecraft.nbt.{NBTTagList, NBTTagCompound}
|
||||||
import net.minecraftforge.common.ForgeDirection
|
import net.minecraftforge.common.ForgeDirection
|
||||||
import scala.Some
|
import scala.Some
|
||||||
import scala.collection.convert.WrapAsScala._
|
import scala.collection.convert.WrapAsScala._
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
|
||||||
// TODO persist managed environments of attached blocks somehow...
|
|
||||||
|
|
||||||
class Adapter extends Rotatable with Environment with IPeripheral {
|
class Adapter extends Rotatable with Environment with IPeripheral {
|
||||||
val node = api.Network.newNode(this, Visibility.Network).create()
|
val node = api.Network.newNode(this, Visibility.Network).create()
|
||||||
|
|
||||||
private val blocks = Array.fill[Option[(ManagedEnvironment, api.driver.Block)]](6)(None)
|
private val blocks = Array.fill[Option[(ManagedEnvironment, api.driver.Block)]](6)(None)
|
||||||
|
|
||||||
private val blocksAddresses = Array.fill[String](6)(java.util.UUID.randomUUID.toString)
|
private val blocksData = Array.fill[Option[BlockData]](6)(None)
|
||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
@ -39,27 +36,33 @@ class Adapter extends Rotatable with Environment with IPeripheral {
|
|||||||
val (x, y, z) = (xCoord + d.offsetX, yCoord + d.offsetY, zCoord + d.offsetZ)
|
val (x, y, z) = (xCoord + d.offsetX, yCoord + d.offsetY, zCoord + d.offsetZ)
|
||||||
driver.Registry.driverFor(worldObj, x, y, z) match {
|
driver.Registry.driverFor(worldObj, x, y, z) match {
|
||||||
case Some(newDriver) => blocks(d.ordinal()) match {
|
case Some(newDriver) => blocks(d.ordinal()) match {
|
||||||
case Some((environment, driver)) =>
|
case Some((oldEnvironment, driver)) =>
|
||||||
if (newDriver != driver) {
|
if (newDriver != driver) {
|
||||||
// This is... odd. Maybe moved by some other mod?
|
// This is... odd. Maybe moved by some other mod?
|
||||||
node.disconnect(environment.node)
|
node.disconnect(oldEnvironment.node)
|
||||||
val newEnvironment = newDriver.createEnvironment(worldObj, x, y, z)
|
val environment = newDriver.createEnvironment(worldObj, x, y, z)
|
||||||
newEnvironment.node.asInstanceOf[server.network.Node].address = blocksAddresses(d.ordinal())
|
blocks(d.ordinal()) = Some((environment, newDriver))
|
||||||
node.connect(newEnvironment.node)
|
blocksData(d.ordinal()) = Some(new BlockData(environment.getClass.getName, new NBTTagCompound()))
|
||||||
blocks(d.ordinal()) = Some((newEnvironment, newDriver))
|
node.connect(environment.node)
|
||||||
} // else: the more things change, the more they stay the same.
|
} // else: the more things change, the more they stay the same.
|
||||||
case _ =>
|
case _ =>
|
||||||
// A challenger appears.
|
// A challenger appears.
|
||||||
val environment = newDriver.createEnvironment(worldObj, x, y, z)
|
val environment = newDriver.createEnvironment(worldObj, x, y, z)
|
||||||
environment.node.asInstanceOf[server.network.Node].address = blocksAddresses(d.ordinal())
|
|
||||||
node.connect(environment.node)
|
|
||||||
blocks(d.ordinal()) = Some((environment, newDriver))
|
blocks(d.ordinal()) = Some((environment, newDriver))
|
||||||
|
blocksData(d.ordinal()) match {
|
||||||
|
case Some(data) if data.name == environment.getClass.getName =>
|
||||||
|
environment.load(data.data)
|
||||||
|
case _ =>
|
||||||
|
}
|
||||||
|
blocksData(d.ordinal()) = Some(new BlockData(environment.getClass.getName, new NBTTagCompound()))
|
||||||
|
node.connect(environment.node)
|
||||||
}
|
}
|
||||||
case _ => blocks(d.ordinal()) match {
|
case _ => blocks(d.ordinal()) match {
|
||||||
case Some((environment, driver)) =>
|
case Some((environment, driver)) =>
|
||||||
// We had something there, but it's gone now...
|
// We had something there, but it's gone now...
|
||||||
blocks(d.ordinal()) = None
|
|
||||||
node.disconnect(environment.node)
|
node.disconnect(environment.node)
|
||||||
|
environment.save(blocksData(d.ordinal()).get.data)
|
||||||
|
blocks(d.ordinal()) = None
|
||||||
case _ => // Nothing before, nothing now.
|
case _ => // Nothing before, nothing now.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,13 +111,16 @@ class Adapter extends Rotatable with Environment with IPeripheral {
|
|||||||
super.readFromNBT(nbt)
|
super.readFromNBT(nbt)
|
||||||
node.load(nbt)
|
node.load(nbt)
|
||||||
|
|
||||||
val addressesNbt = nbt.getTagList("oc.adapter.addresses")
|
val blocksNbt = nbt.getTagList("oc.adapter.blocks")
|
||||||
(0 until (addressesNbt.tagCount min blocksAddresses.length)).
|
(0 until (blocksNbt.tagCount min blocksData.length)).
|
||||||
map(addressesNbt.tagAt).
|
map(blocksNbt.tagAt).
|
||||||
map(_.asInstanceOf[NBTTagString].data).
|
map(_.asInstanceOf[NBTTagCompound]).
|
||||||
zipWithIndex.
|
zipWithIndex.
|
||||||
foreach {
|
foreach {
|
||||||
case (a, i) => blocksAddresses(i) = a
|
case (blockNbt, i) =>
|
||||||
|
if (blockNbt.hasKey("name") && blockNbt.hasKey("data")) {
|
||||||
|
blocksData(i) = Some(new BlockData(blockNbt.getString("name"), blockNbt.getCompoundTag("data")))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,11 +128,22 @@ class Adapter extends Rotatable with Environment with IPeripheral {
|
|||||||
super.writeToNBT(nbt)
|
super.writeToNBT(nbt)
|
||||||
node.save(nbt)
|
node.save(nbt)
|
||||||
|
|
||||||
val addressesNbt = new NBTTagList()
|
val blocksNbt = new NBTTagList()
|
||||||
for (i <- 0 until blocksAddresses.length) {
|
for (i <- 0 until blocks.length) {
|
||||||
addressesNbt.appendTag(new NBTTagString(null, blocksAddresses(i)))
|
val blockNbt = new NBTTagCompound()
|
||||||
|
blocksData(i) match {
|
||||||
|
case Some(data) =>
|
||||||
|
blocks(i) match {
|
||||||
|
case Some((environment, _)) => environment.save(data.data)
|
||||||
|
case _ =>
|
||||||
}
|
}
|
||||||
nbt.setTag("oc.adapter.addresses", addressesNbt)
|
blockNbt.setString("name", data.name)
|
||||||
|
blockNbt.setCompoundTag("data", data.data)
|
||||||
|
case _ =>
|
||||||
|
}
|
||||||
|
blocksNbt.appendTag(blockNbt)
|
||||||
|
}
|
||||||
|
nbt.setTag("oc.adapter.blocks", blocksNbt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
@ -183,4 +200,9 @@ class Adapter extends Rotatable with Environment with IPeripheral {
|
|||||||
throw new IllegalArgumentException("bad argument #%d (number in [1, 65535] expected)".format(index + 1))
|
throw new IllegalArgumentException("bad argument #%d (number in [1, 65535] expected)".format(index + 1))
|
||||||
port
|
port
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
|
private class BlockData(val name: String, val data: NBTTagCompound)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -187,12 +187,29 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
|||||||
for (component <- addedComponents) {
|
for (component <- addedComponents) {
|
||||||
if (component.canBeSeenFrom(owner.node)) {
|
if (component.canBeSeenFrom(owner.node)) {
|
||||||
components += component.address -> component.name
|
components += component.address -> component.name
|
||||||
|
// Skip the signal if we're not initialized yet, since we'd generate a
|
||||||
|
// duplicate in the startup script otherwise.
|
||||||
|
if (kernelMemory > 0)
|
||||||
signal("component_added", component.address, component.name)
|
signal("component_added", component.address, component.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addedComponents.clear()
|
addedComponents.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def verifyComponents() {
|
||||||
|
val invalid = mutable.Set.empty[String]
|
||||||
|
for ((address, name) <- components) {
|
||||||
|
if (owner.node.network.node(address) == null) {
|
||||||
|
OpenComputers.log.warning("A component of type " + name + " disappeared!")
|
||||||
|
signal("component_removed", address, name)
|
||||||
|
invalid += address
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (address <- invalid) {
|
||||||
|
components -= address
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def update() {
|
def update() {
|
||||||
// Add components that were added since the last update to the actual list
|
// Add components that were added since the last update to the actual list
|
||||||
// of components if we can see them. We use this delayed approach to avoid
|
// of components if we can see them. We use this delayed approach to avoid
|
||||||
@ -205,8 +222,11 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
|||||||
processAddedComponents()
|
processAddedComponents()
|
||||||
|
|
||||||
// Are we waiting for the world to settle?
|
// Are we waiting for the world to settle?
|
||||||
if (lastUpdate == 0 && System.currentTimeMillis() < sleepUntil)
|
if (lastUpdate == 0)
|
||||||
|
if (System.currentTimeMillis() < sleepUntil)
|
||||||
return
|
return
|
||||||
|
else
|
||||||
|
verifyComponents()
|
||||||
|
|
||||||
// Update last time run to let our executor thread know it doesn't have to
|
// Update last time run to let our executor thread know it doesn't have to
|
||||||
// pause.
|
// pause.
|
||||||
|
@ -3,6 +3,8 @@ package li.cil.oc.server.component
|
|||||||
import dan200.computer.api.{IMount, IWritableMount, IComputerAccess, IPeripheral}
|
import dan200.computer.api.{IMount, IWritableMount, IComputerAccess, IPeripheral}
|
||||||
import li.cil.oc.api
|
import li.cil.oc.api
|
||||||
import li.cil.oc.api.network._
|
import li.cil.oc.api.network._
|
||||||
|
import li.cil.oc.server.network.{Node => MutableNode}
|
||||||
|
import net.minecraft.nbt.{NBTTagString, NBTTagCompound}
|
||||||
import scala.collection.convert.WrapAsScala._
|
import scala.collection.convert.WrapAsScala._
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
|
||||||
@ -13,6 +15,9 @@ class Peripheral(peripheral: IPeripheral) extends ManagedComponent with ICompute
|
|||||||
|
|
||||||
private val mounts = mutable.Map.empty[String, ManagedEnvironment]
|
private val mounts = mutable.Map.empty[String, ManagedEnvironment]
|
||||||
|
|
||||||
|
// Used to restore mounts to their previous addresses after save/load.
|
||||||
|
private val mountAddresses = mutable.Map.empty[String, String]
|
||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
@LuaCallback(value = "getType", asynchronous = true)
|
@LuaCallback(value = "getType", asynchronous = true)
|
||||||
@ -53,6 +58,31 @@ class Peripheral(peripheral: IPeripheral) extends ManagedComponent with ICompute
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
|
override def load(nbt: NBTTagCompound) {
|
||||||
|
super.load(nbt)
|
||||||
|
|
||||||
|
if (nbt.hasKey("oc.peripheral.addresses")) {
|
||||||
|
val addressesNbt = nbt.getCompoundTag("oc.peripheral.addresses")
|
||||||
|
for (tag <- addressesNbt.getTags) tag match {
|
||||||
|
case addressNbt: NBTTagString =>
|
||||||
|
mountAddresses += addressNbt.getName -> addressNbt.data
|
||||||
|
case _ =>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override def save(nbt: NBTTagCompound) {
|
||||||
|
super.save(nbt)
|
||||||
|
|
||||||
|
val addressesNbt = new NBTTagCompound()
|
||||||
|
for ((location, env) <- mounts) {
|
||||||
|
addressesNbt.setString(location, env.node.address)
|
||||||
|
}
|
||||||
|
nbt.setCompoundTag("oc.peripheral.addresses", addressesNbt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
def getID = -1
|
def getID = -1
|
||||||
|
|
||||||
def getAttachmentName = node.address
|
def getAttachmentName = node.address
|
||||||
@ -71,6 +101,10 @@ class Peripheral(peripheral: IPeripheral) extends ManagedComponent with ICompute
|
|||||||
if (!mounts.contains(desiredLocation)) {
|
if (!mounts.contains(desiredLocation)) {
|
||||||
val env = api.FileSystem.asManagedEnvironment(fs, desiredLocation)
|
val env = api.FileSystem.asManagedEnvironment(fs, desiredLocation)
|
||||||
env.node.asInstanceOf[Component].setVisibility(Visibility.Network)
|
env.node.asInstanceOf[Component].setVisibility(Visibility.Network)
|
||||||
|
if (mountAddresses.contains(desiredLocation)) {
|
||||||
|
env.node.asInstanceOf[MutableNode].address = mountAddresses(desiredLocation)
|
||||||
|
mountAddresses -= desiredLocation
|
||||||
|
}
|
||||||
node.connect(env.node)
|
node.connect(env.node)
|
||||||
mounts += desiredLocation -> env
|
mounts += desiredLocation -> env
|
||||||
desiredLocation
|
desiredLocation
|
||||||
@ -79,7 +113,9 @@ class Peripheral(peripheral: IPeripheral) extends ManagedComponent with ICompute
|
|||||||
|
|
||||||
def unmount(location: String) {
|
def unmount(location: String) {
|
||||||
mounts.remove(location) match {
|
mounts.remove(location) match {
|
||||||
case Some(env) => env.node.remove()
|
case Some(env) =>
|
||||||
|
mountAddresses -= location
|
||||||
|
env.node.remove()
|
||||||
case _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,13 +84,13 @@ trait Component extends api.network.Component with Persistable {
|
|||||||
|
|
||||||
override def load(nbt: NBTTagCompound) {
|
override def load(nbt: NBTTagCompound) {
|
||||||
super.load(nbt)
|
super.load(nbt)
|
||||||
if (nbt.hasKey("visibility"))
|
if (nbt.hasKey("oc.component.visibility"))
|
||||||
visibility_ = Visibility.values()(nbt.getInteger("visibility"))
|
visibility_ = Visibility.values()(nbt.getInteger("oc.component.visibility"))
|
||||||
}
|
}
|
||||||
|
|
||||||
override def save(nbt: NBTTagCompound) {
|
override def save(nbt: NBTTagCompound) {
|
||||||
super.save(nbt)
|
super.save(nbt)
|
||||||
nbt.setInteger("visibility", visibility_.ordinal())
|
nbt.setInteger("oc.component.visibility", visibility_.ordinal())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,10 +332,6 @@ object Network extends api.detail.NetworkAPI {
|
|||||||
def onChunkUnload(e: ChunkEvent.Unload) =
|
def onChunkUnload(e: ChunkEvent.Unload) =
|
||||||
onUnload(e.world, e.getChunk.chunkTileEntityMap.values.asScala.map(_.asInstanceOf[TileEntity]))
|
onUnload(e.world, e.getChunk.chunkTileEntityMap.values.asScala.map(_.asInstanceOf[TileEntity]))
|
||||||
|
|
||||||
@ForgeSubscribe
|
|
||||||
def onChunkLoad(e: ChunkEvent.Load) =
|
|
||||||
onLoad(e.world, e.getChunk.chunkTileEntityMap.values.asScala.map(_.asInstanceOf[TileEntity]))
|
|
||||||
|
|
||||||
private def onUnload(w: World, tileEntities: Iterable[TileEntity]) = if (!w.isRemote) {
|
private def onUnload(w: World, tileEntities: Iterable[TileEntity]) = if (!w.isRemote) {
|
||||||
// TODO add a more efficient batch remove operation? something along the lines of if #remove > #nodes*factor remove all, re-add remaining?
|
// TODO add a more efficient batch remove operation? something along the lines of if #remove > #nodes*factor remove all, re-add remaining?
|
||||||
tileEntities.
|
tileEntities.
|
||||||
@ -344,10 +340,6 @@ object Network extends api.detail.NetworkAPI {
|
|||||||
foreach(t => t.node.remove())
|
foreach(t => t.node.remove())
|
||||||
}
|
}
|
||||||
|
|
||||||
private def onLoad(w: World, tileEntities: Iterable[TileEntity]) = if (!w.isRemote) {
|
|
||||||
tileEntities.foreach(t => joinOrCreateNetwork(w, t.xCoord, t.yCoord, t.zCoord))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
private class Vertex(val data: MutableNode) {
|
private class Vertex(val data: MutableNode) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user