mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-19 04:06:43 -04:00
metric ton of fixes. somehow the number of touches files feels too low... anyway, also added option to LuaCallback annotation to mark callbacks as asynchronous (meaning they will be called directly from the executor thread) and synchronized the sh*t out of the network manager in the hopes that not all will crumble to pieces due to sending messages around while someone meddles with the network (program calls sth while player places/breaks block e.g.); oh, and we now take the memory baseline after loading the libs, meaning 32k is enough to run a computer again. also, loading libs is done asynchronously now, since it's loaded from the rom and nobody else should be able to access that anyway, meaning fast boot-times; command line program to manipulate redstone output and read input
This commit is contained in:
parent
a7ec5c0ec0
commit
78a426c4fe
@ -52,48 +52,4 @@ local function flattenAndStore(k, v)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Mark everything that's globally reachable at this point as permanent.
|
-- Mark everything that's globally reachable at this point as permanent.
|
||||||
flattenAndStore("_ENV", _ENV)
|
flattenAndStore("_ENV", _ENV)
|
||||||
|
|
||||||
--[[ Wrap message sending callbacks to synchronize them to the server thread.
|
|
||||||
|
|
||||||
We generate a wrapper that will yield a closure that will perform the
|
|
||||||
actual call. The host will switch to messaging state and perform the
|
|
||||||
actual in the server thread (by running the yielded function), meaning
|
|
||||||
we don't have to worry about mutlithreading interaction with other
|
|
||||||
components of Minecraft.
|
|
||||||
--]]
|
|
||||||
-- OK, I admit this is a little crazy... here goes:
|
|
||||||
local function wrap(f)
|
|
||||||
assert(f)
|
|
||||||
-- This is the function that replaces the original API function. It is
|
|
||||||
-- called from userland when it wants something from a driver.
|
|
||||||
return function(...)
|
|
||||||
local args = table.pack(...)
|
|
||||||
-- What it does, is that it yields a function. That function is called
|
|
||||||
-- from the server thread, to ensure synchronicity with the world.
|
|
||||||
local result = coroutine.yield(function()
|
|
||||||
-- It runs the actual API function protected mode. We return this as
|
|
||||||
-- a table because a) we need it like this on the outside anyway and
|
|
||||||
-- b) only the first item in the global stack is persisted.
|
|
||||||
local result = table.pack(pcall(f, table.unpack(args, 1, args.n)))
|
|
||||||
if not result[1] then
|
|
||||||
-- We apply tostring to error messages immediately because JNLua
|
|
||||||
-- pushes the original Java exceptions which cannot be persisted.
|
|
||||||
result[2] = tostring(result[2])
|
|
||||||
end
|
|
||||||
return result
|
|
||||||
end)
|
|
||||||
-- The next time our executor runs it pushes that result and calls
|
|
||||||
-- resume, so we get it via the yield. Thus: result = pcall(f, ...)
|
|
||||||
if result[1] then
|
|
||||||
-- API call was successful, return the results.
|
|
||||||
return table.unpack(result, 2, result.n)
|
|
||||||
else
|
|
||||||
-- API call failed, re-throw the error.
|
|
||||||
error(result[2], 2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
componentMethodsSynchronized = wrap(componentMethods)
|
|
||||||
componentInvokeSynchronized = wrap(componentInvoke)
|
|
@ -236,36 +236,107 @@ end
|
|||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local component = {}
|
||||||
|
|
||||||
|
component.list = componentList
|
||||||
|
component.type = componentType
|
||||||
|
|
||||||
|
-- We use a custom "protocol" where we return a boolean first, indicating
|
||||||
|
-- whether we had an error that should be re-thrown.
|
||||||
|
function component.invokeAsync(...)
|
||||||
|
local result = table.pack(componentInvoke(...))
|
||||||
|
if not result[1] then
|
||||||
|
-- We caught an error but performed some custom tostring logic.
|
||||||
|
error(result[2], 2)
|
||||||
|
else
|
||||||
|
-- We should return the results as they are.
|
||||||
|
return table.unpack(result, 2, result.n)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--[[ Wrap message sending callbacks to synchronize them to the server thread.
|
||||||
|
|
||||||
|
We generate a wrapper that will yield a closure that will perform the
|
||||||
|
actual call. The host will switch to messaging state and perform the
|
||||||
|
actual in the server thread (by running the yielded function), meaning
|
||||||
|
we don't have to worry about mutlithreading interaction with other
|
||||||
|
components of Minecraft.
|
||||||
|
--]]
|
||||||
|
-- OK, I admit this is a little crazy... here goes:
|
||||||
|
local function synchronized(f)
|
||||||
|
-- This is the function that replaces the original API function. It is
|
||||||
|
-- called from userland when it wants something from a driver.
|
||||||
|
return function(...)
|
||||||
|
local args = table.pack(...)
|
||||||
|
-- What it does, is that it yields a function. That function is called
|
||||||
|
-- from the server thread, to ensure synchronicity with the world.
|
||||||
|
local result = coroutine.yield(function()
|
||||||
|
-- It runs the actual API function protected mode. We return this as
|
||||||
|
-- a table because a) we need it like this on the outside anyway and
|
||||||
|
-- b) only the first item in the global stack is persisted.
|
||||||
|
local result = table.pack(pcall(f, table.unpack(args, 1, args.n)))
|
||||||
|
if not result[1] then
|
||||||
|
-- We apply tostring to error messages immediately because JNLua
|
||||||
|
-- pushes the original Java exceptions which cannot be persisted.
|
||||||
|
result[2] = tostring(result[2])
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end)
|
||||||
|
-- The next time our executor runs it pushes that result and calls
|
||||||
|
-- resume, so we get it via the yield. Thus: result = pcall(f, ...)
|
||||||
|
if result[1] then
|
||||||
|
-- API call was successful, return the results.
|
||||||
|
return table.unpack(result, 2, result.n)
|
||||||
|
else
|
||||||
|
-- API call failed, re-throw the error.
|
||||||
|
error(result[2], 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
component.methods = synchronized(componentMethods)
|
||||||
|
component.invoke = synchronized(component.invokeAsync)
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
sandbox.component = {}
|
sandbox.component = {}
|
||||||
|
|
||||||
function sandbox.component.list(filter)
|
function sandbox.component.list(filter)
|
||||||
checkArg(1, filter, "string", "nil")
|
checkArg(1, filter, "string", "nil")
|
||||||
return pairs(componentList(filter))
|
return pairs(component.list(filter))
|
||||||
end
|
end
|
||||||
|
|
||||||
function sandbox.component.type(address)
|
function sandbox.component.type(address)
|
||||||
checkArg(1, address, "string")
|
checkArg(1, address, "string")
|
||||||
return componentType(address)
|
return component.type(address)
|
||||||
end
|
end
|
||||||
|
|
||||||
function sandbox.component.invoke(address, method, ...)
|
function sandbox.component.invoke(address, method, ...)
|
||||||
checkArg(1, address, "string")
|
checkArg(1, address, "string")
|
||||||
checkArg(2, method, "string")
|
checkArg(2, method, "string")
|
||||||
return componentInvokeSynchronized(address, method, ...)
|
return component.invoke(address, method, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
function sandbox.component.proxy(address)
|
function sandbox.component.proxy(address)
|
||||||
checkArg(1, address, "string")
|
checkArg(1, address, "string")
|
||||||
local type, reason = componentType(address)
|
local type, reason = component.type(address)
|
||||||
if not type then
|
if not type then
|
||||||
return nil, reason
|
return nil, reason
|
||||||
end
|
end
|
||||||
local proxy = {address = address, type = type}
|
local proxy = {address = address, type = type}
|
||||||
local methods = componentMethodsSynchronized(address)
|
local methods = component.methods(address)
|
||||||
if methods then
|
if methods then
|
||||||
for _, method in ipairs(methods) do
|
for method, asynchronous in pairs(methods) do
|
||||||
|
local invoke
|
||||||
|
if asynchronous then
|
||||||
|
invoke = component.invokeAsync
|
||||||
|
else
|
||||||
|
invoke = component.invoke
|
||||||
|
end
|
||||||
proxy[method] = function(...)
|
proxy[method] = function(...)
|
||||||
return componentInvokeSynchronized(address, method, ...)
|
return invoke(address, method, ...)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -275,40 +346,14 @@ end
|
|||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
local function main()
|
local function main()
|
||||||
|
local args
|
||||||
local function bootstrap()
|
local function bootstrap()
|
||||||
-- Prepare low-level print logic to give boot progress feedback.
|
|
||||||
-- local gpus = gpus()
|
|
||||||
-- for i = 1, #gpus do
|
|
||||||
-- local gpu = gpus[i]
|
|
||||||
-- gpus[i] = nil
|
|
||||||
-- local w, h = sandbox.driver.gpu.resolution(gpu)
|
|
||||||
-- if w then
|
|
||||||
-- if sandbox.driver.gpu.fill(gpu, 1, 1, w, h, " ") then
|
|
||||||
-- gpus[i] = {gpu, w, h}
|
|
||||||
-- end
|
|
||||||
-- end
|
|
||||||
-- end
|
|
||||||
-- local l = 0
|
|
||||||
-- local function print(s)
|
|
||||||
-- l = l + 1
|
|
||||||
-- for _, gpu in pairs(gpus) do
|
|
||||||
-- if l > gpu[3] then
|
|
||||||
-- 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, " ")
|
|
||||||
-- end
|
|
||||||
-- sandbox.driver.gpu.set(gpu[1], 1, math.min(l, gpu[3]), s)
|
|
||||||
-- end
|
|
||||||
-- end
|
|
||||||
print("Booting...")
|
|
||||||
|
|
||||||
print("Mounting ROM and temporary file system.")
|
|
||||||
local function rom(method, ...)
|
local function rom(method, ...)
|
||||||
return componentInvoke(os.romAddress(), method, ...)
|
return component.invokeAsync(os.romAddress(), method, ...)
|
||||||
end
|
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)
|
|
||||||
local handle, reason = rom("open", file)
|
local handle, reason = rom("open", file)
|
||||||
if not handle then
|
if not handle then
|
||||||
error(reason)
|
error(reason)
|
||||||
@ -337,7 +382,6 @@ local function main()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
print("Loading libraries.")
|
|
||||||
local init = {}
|
local init = {}
|
||||||
for _, api in ipairs(rom("list", "lib")) do
|
for _, api in ipairs(rom("list", "lib")) do
|
||||||
local path = "lib/" .. api
|
local path = "lib/" .. api
|
||||||
@ -349,32 +393,29 @@ local function main()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
print("Initializing libraries.")
|
|
||||||
for _, install in ipairs(init) do
|
for _, install in ipairs(init) do
|
||||||
install()
|
install()
|
||||||
end
|
end
|
||||||
init = nil
|
init = nil
|
||||||
|
|
||||||
print("Starting shell.")
|
-- Yield once to allow initializing up to here to get a memory baseline.
|
||||||
|
args = table.pack(coroutine.yield(0))
|
||||||
|
|
||||||
return coroutine.create(load([[
|
return coroutine.create(load([[
|
||||||
fs.mount(os.romAddress(), "/")
|
fs.mount(os.romAddress(), "/")
|
||||||
fs.mount(os.tmpAddress(), "/tmp")
|
fs.mount(os.tmpAddress(), "/tmp")
|
||||||
for c, t in component.list() do
|
for c, t in component.list() do
|
||||||
event.fire("component_added", c, t)
|
event.fire("component_added", c, t)
|
||||||
end
|
end
|
||||||
|
term.clear()
|
||||||
while true do
|
while true do
|
||||||
local result, reason = os.execute("/bin/sh")
|
local result, reason = os.execute("/bin/sh -v")
|
||||||
if not result then
|
if not result then
|
||||||
error(reason)
|
error(reason)
|
||||||
end
|
end
|
||||||
end]], "=init", "t", sandbox))
|
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
|
||||||
|
26
assets/opencomputers/lua/rom/bin/redstone.lua
Normal file
26
assets/opencomputers/lua/rom/bin/redstone.lua
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
local args = shell.parse(...)
|
||||||
|
if #args < 1 then
|
||||||
|
print("Usage: redstone <side> [<value>]")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local side = rs.sides[args[1]]
|
||||||
|
if not side then
|
||||||
|
print("Invalid side.")
|
||||||
|
end
|
||||||
|
if type(side) == "string" then
|
||||||
|
side = rs.sides[side]
|
||||||
|
end
|
||||||
|
|
||||||
|
if #args > 1 then
|
||||||
|
local value = args[2]
|
||||||
|
if tonumber(value) then
|
||||||
|
value = tonumber(value)
|
||||||
|
else
|
||||||
|
value = ({["true"]=true,["on"]=true,["yes"]=true})[value] and 15 or 0
|
||||||
|
end
|
||||||
|
component.primary("redstone").setOutput(side, value)
|
||||||
|
else
|
||||||
|
print("in: " .. component.primary("redstone").getInput(side))
|
||||||
|
print("out: " .. component.primary("redstone").getOutput(side))
|
||||||
|
end
|
@ -1,11 +1,19 @@
|
|||||||
|
local args, options = shell.parse(...)
|
||||||
local history = {}
|
local history = {}
|
||||||
|
|
||||||
|
if options.v then
|
||||||
|
print("OpenOS v1.0 (" .. math.floor(os.totalMemory() / 1024) .. "k RAM)")
|
||||||
|
end
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
if not term.isAvailable() then -- don't clear when opened by another shell
|
if not term.isAvailable() then -- don't clear unless we lost the term
|
||||||
while not term.isAvailable() do
|
while not term.isAvailable() do
|
||||||
os.sleep()
|
os.sleep()
|
||||||
end
|
end
|
||||||
term.clear()
|
term.clear()
|
||||||
print("OpenOS v1.0 (" .. math.floor(os.totalMemory() / 1024) .. "k RAM)")
|
if options.v then
|
||||||
|
print("OpenOS v1.0 (" .. math.floor(os.totalMemory() / 1024) .. "k RAM)")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
while term.isAvailable() do
|
while term.isAvailable() do
|
||||||
term.write("# ")
|
term.write("# ")
|
||||||
|
@ -2,9 +2,10 @@ 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
|
||||||
component.primary("gpu").bind(component.primary("screen").address)
|
local gpu = component.primary("gpu")
|
||||||
|
gpu.bind(component.primary("screen").address)
|
||||||
local maxX, maxY = gpu.maxResolution()
|
local maxX, maxY = gpu.maxResolution()
|
||||||
component.primary("gpu").setResolution(maxX, maxY)
|
gpu.setResolution(maxX, maxY)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
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",
|
||||||
md="mkdir", cls="clear", more="less"}
|
md="mkdir", cls="clear", more="less", rs="redstone"}
|
||||||
|
|
||||||
local function findFile(name, path, ext)
|
local function findFile(name, path, ext)
|
||||||
checkArg(1, name, "string")
|
checkArg(1, name, "string")
|
||||||
|
@ -21,4 +21,15 @@ public @interface LuaCallback {
|
|||||||
* @return the name of the function.
|
* @return the name of the function.
|
||||||
*/
|
*/
|
||||||
String value() default "";
|
String value() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this function may be called asynchronously, i.e. directly from
|
||||||
|
* the computer's executor thread.
|
||||||
|
* <p/>
|
||||||
|
* You will have to ensure anything your callback does is thread safe when
|
||||||
|
* setting this to <tt>true</tt>. Use this for minor lookups, for example.
|
||||||
|
* This is mainly intended to allow functions to perform faster than when
|
||||||
|
* called synchronously (where the call takes at least one server tick).
|
||||||
|
*/
|
||||||
|
boolean asynchronous() default false;
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,7 @@ trait ComponentInventory extends Inventory with Environment with Persistable {
|
|||||||
case Some(driver) =>
|
case Some(driver) =>
|
||||||
Option(driver.createEnvironment(stack)) match {
|
Option(driver.createEnvironment(stack)) match {
|
||||||
case Some(environment) =>
|
case Some(environment) =>
|
||||||
|
environment.node.load(driver.nbt(stack))
|
||||||
environment.load(driver.nbt(stack))
|
environment.load(driver.nbt(stack))
|
||||||
Some(environment)
|
Some(environment)
|
||||||
case _ => None
|
case _ => None
|
||||||
@ -66,7 +67,9 @@ trait ComponentInventory extends Inventory with Environment with Persistable {
|
|||||||
case (stack, slot) => components(slot) match {
|
case (stack, slot) => components(slot) match {
|
||||||
case Some(environment) =>
|
case Some(environment) =>
|
||||||
// We're guaranteed to have a driver for entries.
|
// We're guaranteed to have a driver for entries.
|
||||||
environment.save(Registry.driverFor(stack).get.nbt(stack))
|
val nbt = Registry.driverFor(stack).get.nbt(stack)
|
||||||
|
environment.save(nbt)
|
||||||
|
environment.node.save(nbt)
|
||||||
case _ => // Nothing special to save.
|
case _ => // Nothing special to save.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -82,6 +85,7 @@ trait ComponentInventory extends Inventory with Environment with Persistable {
|
|||||||
case Some(driver) => Option(driver.createEnvironment(item)) match {
|
case Some(driver) => Option(driver.createEnvironment(item)) match {
|
||||||
case Some(environment) =>
|
case Some(environment) =>
|
||||||
components(slot) = Some(environment)
|
components(slot) = Some(environment)
|
||||||
|
environment.node.load(driver.nbt(item))
|
||||||
environment.load(driver.nbt(item))
|
environment.load(driver.nbt(item))
|
||||||
node.network.connect(node, environment.node)
|
node.network.connect(node, environment.node)
|
||||||
case _ => // No environment (e.g. RAM).
|
case _ => // No environment (e.g. RAM).
|
||||||
@ -100,7 +104,11 @@ trait ComponentInventory extends Inventory with Environment with Persistable {
|
|||||||
// being installed into a different computer, even!)
|
// being installed into a different computer, even!)
|
||||||
components(slot) = None
|
components(slot) = None
|
||||||
environment.node.network.remove(environment.node)
|
environment.node.network.remove(environment.node)
|
||||||
Registry.driverFor(item).foreach(driver => environment.save(driver.nbt(item)))
|
Registry.driverFor(item).foreach(driver => {
|
||||||
|
val nbt = driver.nbt(item)
|
||||||
|
environment.save(nbt)
|
||||||
|
environment.node.save(nbt)
|
||||||
|
})
|
||||||
case _ => // Nothing to do.
|
case _ => // Nothing to do.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -329,8 +329,16 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
|||||||
}.toArray)
|
}.toArray)
|
||||||
})
|
})
|
||||||
|
|
||||||
rom.foreach(_.load(computerNbt.getCompoundTag("rom")))
|
rom.foreach(rom => {
|
||||||
tmp.foreach(_.load(computerNbt.getCompoundTag("tmp")))
|
val romNbt = computerNbt.getCompoundTag("rom")
|
||||||
|
rom.node.load(romNbt)
|
||||||
|
rom.load(romNbt)
|
||||||
|
})
|
||||||
|
tmp.foreach(tmp => {
|
||||||
|
val tmpNbt = computerNbt.getCompoundTag("tmp")
|
||||||
|
tmp.node.load(tmpNbt)
|
||||||
|
tmp.load(tmpNbt)
|
||||||
|
})
|
||||||
kernelMemory = computerNbt.getInteger("kernelMemory")
|
kernelMemory = computerNbt.getInteger("kernelMemory")
|
||||||
timeStarted = computerNbt.getLong("timeStarted")
|
timeStarted = computerNbt.getLong("timeStarted")
|
||||||
cpuTime = computerNbt.getLong("cpuTime")
|
cpuTime = computerNbt.getLong("cpuTime")
|
||||||
@ -417,11 +425,17 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
|||||||
computerNbt.setTag("signals", signalsNbt)
|
computerNbt.setTag("signals", signalsNbt)
|
||||||
|
|
||||||
val romNbt = new NBTTagCompound()
|
val romNbt = new NBTTagCompound()
|
||||||
rom.foreach(_.save(romNbt))
|
rom.foreach(rom => {
|
||||||
|
rom.save(romNbt)
|
||||||
|
rom.node.save(romNbt)
|
||||||
|
})
|
||||||
computerNbt.setCompoundTag("rom", romNbt)
|
computerNbt.setCompoundTag("rom", romNbt)
|
||||||
|
|
||||||
val tmpNbt = new NBTTagCompound()
|
val tmpNbt = new NBTTagCompound()
|
||||||
tmp.foreach(_.save(tmpNbt))
|
tmp.foreach(tmp => {
|
||||||
|
tmp.save(tmpNbt)
|
||||||
|
tmp.node.save(tmpNbt)
|
||||||
|
})
|
||||||
computerNbt.setCompoundTag("tmp", tmpNbt)
|
computerNbt.setCompoundTag("tmp", tmpNbt)
|
||||||
|
|
||||||
computerNbt.setInteger("kernelMemory", kernelMemory)
|
computerNbt.setInteger("kernelMemory", kernelMemory)
|
||||||
@ -626,11 +640,16 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
|||||||
|
|
||||||
lua.pushScalaFunction(lua => {
|
lua.pushScalaFunction(lua => {
|
||||||
owner.node.network.sendToAddress(owner.node, lua.checkString(1), "component.methods") match {
|
owner.node.network.sendToAddress(owner.node, lua.checkString(1), "component.methods") match {
|
||||||
case Array(names: Array[String]) =>
|
case Array(methods: Array[_]) =>
|
||||||
lua.newTable()
|
lua.newTable()
|
||||||
for ((name, index) <- names.zipWithIndex) {
|
for ((method, index) <- methods.zipWithIndex) {
|
||||||
lua.pushString(name)
|
method match {
|
||||||
lua.rawSet(-2, index + 1)
|
case (name: String, asynchronous: Boolean) =>
|
||||||
|
lua.pushString(name)
|
||||||
|
lua.pushBoolean(asynchronous)
|
||||||
|
lua.rawSet(-3)
|
||||||
|
case _ =>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
1
|
1
|
||||||
case _ =>
|
case _ =>
|
||||||
@ -704,41 +723,52 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
|||||||
owner.node.network.sendToAddress(owner.node, address, "component.invoke", Seq(method) ++ args: _*)
|
owner.node.network.sendToAddress(owner.node, address, "component.invoke", Seq(method) ++ args: _*)
|
||||||
case _ => throw new Exception("no such component")
|
case _ => throw new Exception("no such component")
|
||||||
}) match {
|
}) match {
|
||||||
case Array(results@_*) =>
|
case results: Array[_] =>
|
||||||
|
lua.pushBoolean(true)
|
||||||
results.foreach(pushResult(lua, _))
|
results.foreach(pushResult(lua, _))
|
||||||
results.length
|
1 + results.length
|
||||||
case _ =>
|
case _ =>
|
||||||
0
|
lua.pushBoolean(true)
|
||||||
|
1
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
case e: Throwable if e.getMessage != null && !e.getMessage.isEmpty =>
|
|
||||||
lua.pushNil()
|
|
||||||
lua.pushString(e.getMessage)
|
|
||||||
2
|
|
||||||
case _: FileNotFoundException =>
|
|
||||||
lua.pushNil()
|
|
||||||
lua.pushString("file not found")
|
|
||||||
2
|
|
||||||
case _: SecurityException =>
|
|
||||||
lua.pushNil()
|
|
||||||
lua.pushString("access denied")
|
|
||||||
2
|
|
||||||
case _: IOException =>
|
|
||||||
lua.pushNil()
|
|
||||||
lua.pushString("i/o error")
|
|
||||||
2
|
|
||||||
case _: NoSuchMethodException =>
|
case _: NoSuchMethodException =>
|
||||||
lua.pushNil()
|
lua.pushBoolean(false)
|
||||||
lua.pushString("no such method")
|
lua.pushString("no such method")
|
||||||
2
|
2
|
||||||
|
case e: IllegalArgumentException if e.getMessage != null =>
|
||||||
|
lua.pushBoolean(false)
|
||||||
|
lua.pushString(e.getMessage)
|
||||||
|
2
|
||||||
case _: IllegalArgumentException =>
|
case _: IllegalArgumentException =>
|
||||||
lua.pushNil()
|
lua.pushBoolean(false)
|
||||||
lua.pushString("bad argument")
|
lua.pushString("bad argument")
|
||||||
2
|
2
|
||||||
|
case e: Throwable if e.getMessage != null =>
|
||||||
|
lua.pushBoolean(true)
|
||||||
|
lua.pushNil()
|
||||||
|
lua.pushString(e.getMessage)
|
||||||
|
3
|
||||||
|
case _: FileNotFoundException =>
|
||||||
|
lua.pushBoolean(true)
|
||||||
|
lua.pushNil()
|
||||||
|
lua.pushString("file not found")
|
||||||
|
3
|
||||||
|
case _: SecurityException =>
|
||||||
|
lua.pushBoolean(true)
|
||||||
|
lua.pushNil()
|
||||||
|
lua.pushString("access denied")
|
||||||
|
3
|
||||||
|
case _: IOException =>
|
||||||
|
lua.pushBoolean(true)
|
||||||
|
lua.pushNil()
|
||||||
|
lua.pushString("i/o error")
|
||||||
|
3
|
||||||
case _: Throwable =>
|
case _: Throwable =>
|
||||||
|
lua.pushBoolean(true)
|
||||||
lua.pushNil()
|
lua.pushNil()
|
||||||
lua.pushString("unknown error")
|
lua.pushString("unknown error")
|
||||||
2
|
3
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
lua.setGlobal("componentInvoke")
|
lua.setGlobal("componentInvoke")
|
||||||
@ -771,23 +801,13 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
|||||||
// timeouts.
|
// timeouts.
|
||||||
lua.load(classOf[Computer].getResourceAsStream(Config.scriptPath + "kernel.lua"), "=kernel", "t")
|
lua.load(classOf[Computer].getResourceAsStream(Config.scriptPath + "kernel.lua"), "=kernel", "t")
|
||||||
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.
|
||||||
val results = lua.resume(1, 0)
|
// val results = lua.resume(1, 0)
|
||||||
if (lua.status(1) != LuaState.YIELD)
|
// if (lua.status(1) != LuaState.YIELD)
|
||||||
if (!lua.toBoolean(-2)) throw new Exception(lua.toString(-1))
|
// if (!lua.toBoolean(-2)) throw new Exception(lua.toString(-1))
|
||||||
else throw new Exception("kernel return unexpectedly")
|
// else throw new Exception("kernel return unexpectedly")
|
||||||
lua.pop(results)
|
// lua.pop(results)
|
||||||
|
|
||||||
// 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
|
|
||||||
// the kernel has. We remember that size to grant user-space programs a
|
|
||||||
// fixed base amount of memory, regardless of the memory need of the
|
|
||||||
// underlying system (which may change across releases). Add some buffer
|
|
||||||
// to avoid the init script eating up all the rest immediately.
|
|
||||||
lua.gc(LuaState.GcAction.COLLECT, 0)
|
|
||||||
kernelMemory = (lua.getTotalMemory - lua.getFreeMemory) + Config.baseMemory
|
|
||||||
recomputeMemory()
|
|
||||||
|
|
||||||
// Clear any left-over signals from a previous run.
|
// Clear any left-over signals from a previous run.
|
||||||
signals.clear()
|
signals.clear()
|
||||||
@ -862,7 +882,7 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
|||||||
try {
|
try {
|
||||||
// Help out the GC a little. The emergency GC has a few limitations that
|
// Help out the GC a little. The emergency GC has a few limitations that
|
||||||
// will make it free less memory than doing a full step manually.
|
// will make it free less memory than doing a full step manually.
|
||||||
lua.gc(LuaState.GcAction.COLLECT, 0)
|
// lua.gc(LuaState.GcAction.COLLECT, 0)
|
||||||
// Resume the Lua state and remember the number of results we get.
|
// Resume the Lua state and remember the number of results we get.
|
||||||
cpuStart = System.nanoTime()
|
cpuStart = System.nanoTime()
|
||||||
val results = if (callReturn) {
|
val results = if (callReturn) {
|
||||||
@ -887,6 +907,19 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
|
|||||||
}
|
}
|
||||||
cpuTime += System.nanoTime() - cpuStart
|
cpuTime += System.nanoTime() - cpuStart
|
||||||
|
|
||||||
|
// Check if this was the first run, meaning the one used for initializing
|
||||||
|
// the kernel (loading the libs, establishing a memory baseline).
|
||||||
|
if (kernelMemory == 0) {
|
||||||
|
// 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
|
||||||
|
// the kernel has. We remember that size to grant user-space programs a
|
||||||
|
// fixed base amount of memory, regardless of the memory need of the
|
||||||
|
// underlying system (which may change across releases).
|
||||||
|
lua.gc(LuaState.GcAction.COLLECT, 0)
|
||||||
|
kernelMemory = (lua.getTotalMemory - lua.getFreeMemory) + Config.baseMemory
|
||||||
|
recomputeMemory()
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the kernel is still alive.
|
// Check if the kernel is still alive.
|
||||||
stateMonitor.synchronized(if (lua.status(1) == LuaState.YIELD) {
|
stateMonitor.synchronized(if (lua.status(1) == LuaState.YIELD) {
|
||||||
// Intermediate state in some cases. Satisfies the assert in execute().
|
// Intermediate state in some cases. Satisfies the assert in execute().
|
||||||
|
@ -25,7 +25,7 @@ class GraphicsCard(val maxResolution: (Int, Int)) extends ManagedComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@LuaCallback("getResolution")
|
@LuaCallback(value = "getResolution", asynchronous = true)
|
||||||
def getResolution(message: Message): Array[Object] = trySend("screen.resolution")
|
def getResolution(message: Message): Array[Object] = trySend("screen.resolution")
|
||||||
|
|
||||||
@LuaCallback("setResolution")
|
@LuaCallback("setResolution")
|
||||||
@ -39,7 +39,7 @@ class GraphicsCard(val maxResolution: (Int, Int)) extends ManagedComponent {
|
|||||||
Array(Unit, "unsupported resolution")
|
Array(Unit, "unsupported resolution")
|
||||||
}
|
}
|
||||||
|
|
||||||
@LuaCallback("maxResolution")
|
@LuaCallback(value = "maxResolution", asynchronous = true)
|
||||||
def maxResolution(message: Message): Array[Object] =
|
def maxResolution(message: Message): Array[Object] =
|
||||||
trySend("screen.resolution") match {
|
trySend("screen.resolution") match {
|
||||||
case Array(w: Integer, h: Integer) =>
|
case Array(w: Integer, h: Integer) =>
|
||||||
@ -62,7 +62,7 @@ class GraphicsCard(val maxResolution: (Int, Int)) extends ManagedComponent {
|
|||||||
val y = message.checkInteger(2)
|
val y = message.checkInteger(2)
|
||||||
val w = message.checkInteger(3)
|
val w = message.checkInteger(3)
|
||||||
val h = message.checkInteger(4)
|
val h = message.checkInteger(4)
|
||||||
val value = message.checkString(4)
|
val value = message.checkString(5)
|
||||||
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))
|
||||||
else
|
else
|
||||||
@ -96,13 +96,11 @@ class GraphicsCard(val maxResolution: (Int, Int)) extends ManagedComponent {
|
|||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
override def load(nbt: NBTTagCompound) {
|
override def load(nbt: NBTTagCompound) {
|
||||||
super.load(nbt)
|
|
||||||
if (nbt.hasKey("oc.gpu.screen"))
|
if (nbt.hasKey("oc.gpu.screen"))
|
||||||
screen = Some(nbt.getString("oc.gpu.screen"))
|
screen = Some(nbt.getString("oc.gpu.screen"))
|
||||||
}
|
}
|
||||||
|
|
||||||
override def save(nbt: NBTTagCompound) {
|
override def save(nbt: NBTTagCompound) {
|
||||||
super.save(nbt)
|
|
||||||
if (screen.isDefined)
|
if (screen.isDefined)
|
||||||
nbt.setString("oc.gpu.screen", screen.get)
|
nbt.setString("oc.gpu.screen", screen.get)
|
||||||
}
|
}
|
||||||
|
@ -8,14 +8,14 @@ import net.minecraftforge.common.ForgeDirection
|
|||||||
class RedstoneCard extends ManagedComponent {
|
class RedstoneCard extends ManagedComponent {
|
||||||
val node = api.Network.createComponent(api.Network.createNode(this, "redstone", Visibility.Neighbors))
|
val node = api.Network.createComponent(api.Network.createNode(this, "redstone", Visibility.Neighbors))
|
||||||
|
|
||||||
@LuaCallback("getInput")
|
@LuaCallback(value = "getInput", asynchronous = true)
|
||||||
def getInput(message: Message): Array[Object] = {
|
def getInput(message: Message): Array[Object] = {
|
||||||
val side = message.checkInteger(1)
|
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(value = "getOutput", asynchronous = true)
|
||||||
def getOutput(message: Message): Array[Object] = {
|
def getOutput(message: Message): Array[Object] = {
|
||||||
val side = message.checkInteger(1)
|
val side = message.checkInteger(1)
|
||||||
node.network.sendToAddress(node, message.source.address,
|
node.network.sendToAddress(node, message.source.address,
|
||||||
|
@ -63,11 +63,13 @@ class Component(host: Environment, name: String, reachability: Visibility) exten
|
|||||||
override def receive(message: Message) = {
|
override def receive(message: Message) = {
|
||||||
if (message.name == "component.methods")
|
if (message.name == "component.methods")
|
||||||
if (canBeSeenBy(message.source))
|
if (canBeSeenBy(message.source))
|
||||||
Array(Array(luaCallbacks.keys.toSeq: _*))
|
Array(luaCallbacks.map {
|
||||||
|
case (method, (asynchronous, _)) => (method, asynchronous)
|
||||||
|
}.toArray)
|
||||||
else null
|
else null
|
||||||
else if (message.name == "component.invoke") {
|
else if (message.name == "component.invoke") {
|
||||||
luaCallbacks.get(message.data()(0).asInstanceOf[String]) 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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,25 +78,25 @@ class Component(host: Environment, name: String, reachability: Visibility) exten
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
override def save(nbt: NBTTagCompound) {
|
override def load(nbt: NBTTagCompound) {
|
||||||
super.load(nbt)
|
super.load(nbt)
|
||||||
if (nbt.hasKey("visibility"))
|
if (nbt.hasKey("visibility"))
|
||||||
visibility_ = Visibility.values()(nbt.getInteger("visibility"))
|
visibility_ = Visibility.values()(nbt.getInteger("visibility"))
|
||||||
}
|
}
|
||||||
|
|
||||||
override def load(nbt: NBTTagCompound) {
|
override def save(nbt: NBTTagCompound) {
|
||||||
super.save(nbt)
|
super.save(nbt)
|
||||||
nbt.setInteger("visibility", visibility_.ordinal())
|
nbt.setInteger("visibility", visibility_.ordinal())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object Component {
|
object Component {
|
||||||
private val cache = mutable.Map.empty[Class[_], immutable.Map[String, (Object, api.network.Message) => Array[Object]]]
|
private val cache = mutable.Map.empty[Class[_], immutable.Map[String, (Boolean, (Object, api.network.Message) => Array[Object])]]
|
||||||
|
|
||||||
def callbacks(clazz: Class[_]) = cache.getOrElseUpdate(clazz, analyze(clazz))
|
def callbacks(clazz: Class[_]) = cache.getOrElseUpdate(clazz, analyze(clazz))
|
||||||
|
|
||||||
private def analyze(clazz: Class[_]) = {
|
private def analyze(clazz: Class[_]) = {
|
||||||
val callbacks = mutable.Map.empty[String, (Object, api.network.Message) => Array[Object]]
|
val callbacks = mutable.Map.empty[String, (Boolean, (Object, api.network.Message) => Array[Object])]
|
||||||
var c = clazz
|
var c = clazz
|
||||||
while (c != classOf[Object]) {
|
while (c != classOf[Object]) {
|
||||||
val ms = c.getDeclaredMethods
|
val ms = c.getDeclaredMethods
|
||||||
@ -112,7 +114,7 @@ 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) => try {
|
callbacks += a.value ->(a.asynchronous(), (o: Object, e: Message) => try {
|
||||||
m.invoke(o, e).asInstanceOf[Array[Object]]
|
m.invoke(o, e).asInstanceOf[Array[Object]]
|
||||||
} catch {
|
} catch {
|
||||||
case e: InvocationTargetException => throw e.getCause
|
case e: InvocationTargetException => throw e.getCause
|
||||||
|
@ -18,7 +18,7 @@ import scala.collection.mutable.ArrayBuffer
|
|||||||
|
|
||||||
class Network private(private val addressedNodes: mutable.Map[String, Network.Node] = mutable.Map.empty,
|
class Network private(private val addressedNodes: mutable.Map[String, Network.Node] = mutable.Map.empty,
|
||||||
private val unaddressedNodes: mutable.ArrayBuffer[Network.Node] = mutable.ArrayBuffer.empty) extends api.network.Network {
|
private val unaddressedNodes: mutable.ArrayBuffer[Network.Node] = mutable.ArrayBuffer.empty) extends api.network.Network {
|
||||||
def this(node: Node) = {
|
def this(node: network.Node) = {
|
||||||
this()
|
this()
|
||||||
addNew(node)
|
addNew(node)
|
||||||
if (node.address != null)
|
if (node.address != null)
|
||||||
@ -31,14 +31,18 @@ class Network private(private val addressedNodes: mutable.Map[String, Network.No
|
|||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
def connect(nodeA: api.network.Node, nodeB: api.network.Node) = {
|
def connect(nodeA: api.network.Node, nodeB: api.network.Node) = {
|
||||||
if (nodeA == nodeB) throw new IllegalArgumentException(
|
|
||||||
"Cannot connect a node to itself.")
|
|
||||||
|
|
||||||
if (!nodeA.isInstanceOf[network.Node]) throw new IllegalArgumentException(
|
if (!nodeA.isInstanceOf[network.Node]) throw new IllegalArgumentException(
|
||||||
"Unsupported node implementation. Don't implement the interface yourself!")
|
"Unsupported node implementation. Don't implement the interface yourself!")
|
||||||
if (!nodeB.isInstanceOf[network.Node]) throw new IllegalArgumentException(
|
if (!nodeB.isInstanceOf[network.Node]) throw new IllegalArgumentException(
|
||||||
"Unsupported node implementation. Don't implement the interface yourself!")
|
"Unsupported node implementation. Don't implement the interface yourself!")
|
||||||
|
|
||||||
|
connect0(nodeA.asInstanceOf[network.Node], nodeB.asInstanceOf[network.Node])
|
||||||
|
}
|
||||||
|
|
||||||
|
private def connect0(nodeA: network.Node, nodeB: network.Node) = this.synchronized {
|
||||||
|
if (nodeA == nodeB) throw new IllegalArgumentException(
|
||||||
|
"Cannot connect a node to itself.")
|
||||||
|
|
||||||
val containsA = contains(nodeA)
|
val containsA = contains(nodeA)
|
||||||
val containsB = contains(nodeB)
|
val containsB = contains(nodeB)
|
||||||
|
|
||||||
@ -65,19 +69,23 @@ class Network private(private val addressedNodes: mutable.Map[String, Network.No
|
|||||||
}
|
}
|
||||||
else false // That connection already exists.
|
else false // That connection already exists.
|
||||||
}
|
}
|
||||||
else if (containsA) add(oldNodeA, nodeB.asInstanceOf[network.Node])
|
else if (containsA) add(oldNodeA, nodeB)
|
||||||
else add(oldNodeB, nodeA.asInstanceOf[network.Node])
|
else add(oldNodeB, nodeA)
|
||||||
}
|
}
|
||||||
|
|
||||||
def disconnect(nodeA: api.network.Node, nodeB: api.network.Node) = {
|
def disconnect(nodeA: api.network.Node, nodeB: api.network.Node) = {
|
||||||
if (nodeA == nodeB) throw new IllegalArgumentException(
|
|
||||||
"Cannot disconnect a node from itself.")
|
|
||||||
|
|
||||||
if (!nodeA.isInstanceOf[network.Node]) throw new IllegalArgumentException(
|
if (!nodeA.isInstanceOf[network.Node]) throw new IllegalArgumentException(
|
||||||
"Unsupported node implementation. Don't implement the interface yourself!")
|
"Unsupported node implementation. Don't implement the interface yourself!")
|
||||||
if (!nodeB.isInstanceOf[network.Node]) throw new IllegalArgumentException(
|
if (!nodeB.isInstanceOf[network.Node]) throw new IllegalArgumentException(
|
||||||
"Unsupported node implementation. Don't implement the interface yourself!")
|
"Unsupported node implementation. Don't implement the interface yourself!")
|
||||||
|
|
||||||
|
disconnect0(nodeA.asInstanceOf[network.Node], nodeB.asInstanceOf[network.Node])
|
||||||
|
}
|
||||||
|
|
||||||
|
private def disconnect0(nodeA: network.Node, nodeB: network.Node) = this.synchronized {
|
||||||
|
if (nodeA == nodeB) throw new IllegalArgumentException(
|
||||||
|
"Cannot disconnect a node from itself.")
|
||||||
|
|
||||||
val containsA = contains(nodeA)
|
val containsA = contains(nodeA)
|
||||||
val containsB = contains(nodeB)
|
val containsB = contains(nodeB)
|
||||||
|
|
||||||
@ -100,61 +108,95 @@ class Network private(private val addressedNodes: mutable.Map[String, Network.No
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def remove(node: api.network.Node) = (Option(node.address) match {
|
def remove(node: api.network.Node) = {
|
||||||
case Some(address) => addressedNodes.remove(address)
|
if (!node.isInstanceOf[network.Node]) throw new IllegalArgumentException(
|
||||||
case _ => unaddressedNodes.indexWhere(_.data == node) match {
|
"Unsupported node implementation. Don't implement the interface yourself!")
|
||||||
case -1 => None
|
|
||||||
case index => Some(unaddressedNodes.remove(index))
|
remove0(node.asInstanceOf[network.Node])
|
||||||
|
}
|
||||||
|
|
||||||
|
private def remove0(node: network.Node) = this.synchronized {
|
||||||
|
(Option(node.address) match {
|
||||||
|
case Some(address) => addressedNodes.remove(address)
|
||||||
|
case _ => unaddressedNodes.indexWhere(_.data == node) match {
|
||||||
|
case -1 => None
|
||||||
|
case index => Some(unaddressedNodes.remove(index))
|
||||||
|
}
|
||||||
|
}) match {
|
||||||
|
case Some(entry) => {
|
||||||
|
node.asInstanceOf[Node].network = null
|
||||||
|
val subGraphs = entry.remove()
|
||||||
|
val targets = Iterable(node) ++ (entry.data.reachability match {
|
||||||
|
case Visibility.None => Iterable.empty[api.network.Node]
|
||||||
|
case Visibility.Neighbors => entry.edges.map(_.other(entry).data)
|
||||||
|
case Visibility.Network => subGraphs.map {
|
||||||
|
case (addressed, _) => addressed.values.map(_.data)
|
||||||
|
}.flatten
|
||||||
|
})
|
||||||
|
handleSplit(subGraphs)
|
||||||
|
send(Network.DisconnectMessage(node), targets)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
case _ => false
|
||||||
}
|
}
|
||||||
}) match {
|
|
||||||
case Some(entry) => {
|
|
||||||
node.asInstanceOf[Node].network = null
|
|
||||||
val subGraphs = entry.remove()
|
|
||||||
val targets = Iterable(node) ++ (entry.data.reachability match {
|
|
||||||
case Visibility.None => Iterable.empty[api.network.Node]
|
|
||||||
case Visibility.Neighbors => entry.edges.map(_.other(entry).data)
|
|
||||||
case Visibility.Network => subGraphs.map {
|
|
||||||
case (addressed, _) => addressed.values.map(_.data)
|
|
||||||
}.flatten
|
|
||||||
})
|
|
||||||
handleSplit(subGraphs)
|
|
||||||
send(Network.DisconnectMessage(node), targets)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
case _ => false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
def node(address: String) = addressedNodes.get(address) match {
|
def node(address: String) = this.synchronized {
|
||||||
case Some(node) => node.data
|
addressedNodes.get(address) match {
|
||||||
case _ => null
|
case Some(node) => node.data
|
||||||
|
case _ => null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def nodes = addressedNodes.values.map(_.data.asInstanceOf[api.network.Node]).asJava
|
def nodes = this.synchronized(addressedNodes.values.map(_.data.asInstanceOf[api.network.Node]).asJava)
|
||||||
|
|
||||||
def nodes(reference: api.network.Node) = {
|
def nodes(reference: api.network.Node) = {
|
||||||
|
if (!reference.isInstanceOf[network.Node]) throw new IllegalArgumentException(
|
||||||
|
"Unsupported node implementation. Don't implement the interface yourself!")
|
||||||
|
|
||||||
|
nodes0(reference.asInstanceOf[network.Node])
|
||||||
|
}
|
||||||
|
|
||||||
|
private def nodes0(reference: api.network.Node) = {
|
||||||
val referenceNeighbors = neighbors(reference).toSet.asJava
|
val referenceNeighbors = neighbors(reference).toSet.asJava
|
||||||
nodes.filter(node => node != reference && (node.reachability == Visibility.Network ||
|
nodes.filter(node => node != reference && (node.reachability == Visibility.Network ||
|
||||||
(node.reachability == Visibility.Neighbors && referenceNeighbors.contains(node)))).asJava
|
(node.reachability == Visibility.Neighbors && referenceNeighbors.contains(node)))).asJava
|
||||||
}
|
}
|
||||||
|
|
||||||
def neighbors(node: api.network.Node) = Option(node.address) match {
|
def neighbors(node: api.network.Node) = {
|
||||||
case Some(address) =>
|
if (!node.isInstanceOf[network.Node]) throw new IllegalArgumentException(
|
||||||
addressedNodes.get(address) match {
|
"Unsupported node implementation. Don't implement the interface yourself!")
|
||||||
case Some(n) => n.edges.map(_.other(n).data)
|
|
||||||
case _ => throw new IllegalArgumentException("Node must be in this network.")
|
neighbors0(node.asInstanceOf[network.Node])
|
||||||
}
|
}
|
||||||
case None =>
|
|
||||||
unaddressedNodes.find(_.data == node) match {
|
private def neighbors0(node: network.Node) = this.synchronized {
|
||||||
case Some(n) => n.edges.map(_.other(n).data)
|
Option(node.address) match {
|
||||||
case _ => throw new IllegalArgumentException("Node must be in this network.")
|
case Some(address) =>
|
||||||
}
|
addressedNodes.get(address) match {
|
||||||
|
case Some(n) => n.edges.map(_.other(n).data)
|
||||||
|
case _ => throw new IllegalArgumentException("Node must be in this network.")
|
||||||
|
}
|
||||||
|
case None =>
|
||||||
|
unaddressedNodes.find(_.data == node) match {
|
||||||
|
case Some(n) => n.edges.map(_.other(n).data)
|
||||||
|
case _ => throw new IllegalArgumentException("Node must be in this network.")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
def sendToAddress(source: api.network.Node, target: String, name: String, data: AnyRef*) = {
|
def sendToAddress(source: api.network.Node, target: String, name: String, data: AnyRef*): Array[Object] = {
|
||||||
|
if (!source.isInstanceOf[network.Node]) throw new IllegalArgumentException(
|
||||||
|
"Unsupported node implementation. Don't implement the interface yourself!")
|
||||||
|
|
||||||
|
sendToAddress0(source.asInstanceOf[network.Node], target, name, data: _*)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def sendToAddress0(source: network.Node, target: String, name: String, data: AnyRef*) = this.synchronized {
|
||||||
if (source.network == null || source.network != this)
|
if (source.network == null || source.network != this)
|
||||||
throw new IllegalArgumentException("Source node must be in this network.")
|
throw new IllegalArgumentException("Source node must be in this network.")
|
||||||
if (source.address != null) addressedNodes.get(target) match {
|
if (source.address != null) addressedNodes.get(target) match {
|
||||||
@ -165,7 +207,14 @@ class Network private(private val addressedNodes: mutable.Map[String, Network.No
|
|||||||
} else null
|
} else null
|
||||||
}
|
}
|
||||||
|
|
||||||
def sendToNeighbors(source: api.network.Node, name: String, data: AnyRef*) = {
|
def sendToNeighbors(source: api.network.Node, name: String, data: AnyRef*): Array[Object] = {
|
||||||
|
if (!source.isInstanceOf[network.Node]) throw new IllegalArgumentException(
|
||||||
|
"Unsupported node implementation. Don't implement the interface yourself!")
|
||||||
|
|
||||||
|
sendToNeighbors0(source.asInstanceOf[network.Node], name, data: _*)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def sendToNeighbors0(source: network.Node, name: String, data: AnyRef*) = this.synchronized {
|
||||||
if (source.network == null || source.network != this)
|
if (source.network == null || source.network != this)
|
||||||
throw new IllegalArgumentException("Source node must be in this network.")
|
throw new IllegalArgumentException("Source node must be in this network.")
|
||||||
if (source.address != null)
|
if (source.address != null)
|
||||||
@ -173,7 +222,14 @@ class Network private(private val addressedNodes: mutable.Map[String, Network.No
|
|||||||
else null
|
else null
|
||||||
}
|
}
|
||||||
|
|
||||||
def sendToVisible(source: api.network.Node, name: String, data: AnyRef*) = {
|
def sendToVisible(source: api.network.Node, name: String, data: AnyRef*): Array[Object] = {
|
||||||
|
if (!source.isInstanceOf[network.Node]) throw new IllegalArgumentException(
|
||||||
|
"Unsupported node implementation. Don't implement the interface yourself!")
|
||||||
|
|
||||||
|
sendToVisible0(source.asInstanceOf[network.Node], name, data: _*)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def sendToVisible0(source: network.Node, name: String, data: AnyRef*) = this.synchronized {
|
||||||
if (source.network == null || source.network != this)
|
if (source.network == null || source.network != this)
|
||||||
throw new IllegalArgumentException("Source node must be in this network.")
|
throw new IllegalArgumentException("Source node must be in this network.")
|
||||||
if (source.address != null)
|
if (source.address != null)
|
||||||
@ -476,12 +532,12 @@ object Network extends api.detail.NetworkAPI {
|
|||||||
private def checkCount(count: Int, name: String) =
|
private def checkCount(count: Int, name: String) =
|
||||||
if (data.length <= count) throw new IllegalArgumentException(
|
if (data.length <= count) throw new IllegalArgumentException(
|
||||||
"bad arguments #%d (%s expected, got no value)".
|
"bad arguments #%d (%s expected, got no value)".
|
||||||
format(count + 1, name))
|
format(count, name))
|
||||||
|
|
||||||
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 %s)".
|
"bad argument #%d (%s expected, got %s)".
|
||||||
format(index + 1, want, typeName(have)))
|
format(index, want, typeName(have)))
|
||||||
|
|
||||||
private def typeName(value: AnyRef): String = value match {
|
private def typeName(value: AnyRef): String = value match {
|
||||||
case null => "nil"
|
case null => "nil"
|
||||||
|
@ -27,18 +27,21 @@ class TextBuffer(var width: Int, var height: Int) {
|
|||||||
* buffer valid as possible if the size decreases, i.e. only data outside the
|
* buffer valid as possible if the size decreases, i.e. only data outside the
|
||||||
* new buffer size will be truncated, all data still inside will be copied.
|
* new buffer size will be truncated, all data still inside will be copied.
|
||||||
*/
|
*/
|
||||||
def size_=(value: (Int, Int)): Boolean = if (size != value) {
|
def size_=(value: (Int, Int)): Boolean = {
|
||||||
val (w, h) = value
|
val (iw, ih) = value
|
||||||
val newBuffer = Array.fill(h, w)(' ')
|
val (w, h) = (iw max 1, ih max 1)
|
||||||
(0 until (h min height)) foreach {
|
if (width != w || height != h) {
|
||||||
y => Array.copy(buffer(y), 0, newBuffer(y), 0, w min width)
|
val newBuffer = Array.fill(h, w)(' ')
|
||||||
|
(0 until (h min height)) foreach {
|
||||||
|
y => Array.copy(buffer(y), 0, newBuffer(y), 0, w min width)
|
||||||
|
}
|
||||||
|
buffer = newBuffer
|
||||||
|
width = w
|
||||||
|
height = h
|
||||||
|
true
|
||||||
}
|
}
|
||||||
buffer = newBuffer
|
else false
|
||||||
width = w
|
|
||||||
height = h
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
else false
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* String based fill starting at a specified location.
|
* String based fill starting at a specified location.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user