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:
Florian Nücke 2013-11-04 20:53:31 +01:00
parent fcdb75e4bc
commit ab785eb3ac
16 changed files with 220 additions and 168 deletions

View File

@ -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
}, },

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View 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

View File

@ -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)

View File

@ -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)

View File

@ -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)
} }

View File

@ -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.

View File

@ -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 _ =>
} }
} }

View File

@ -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())
} }
} }

View File

@ -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) {