mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-17 11:15:12 -04:00
Merge branch 'master-MC1.11' into master-MC1.12
This commit is contained in:
commit
ac400a5485
@ -1,6 +1,7 @@
|
|||||||
local process = require("process")
|
local process = require("process")
|
||||||
local unicode = require("unicode")
|
local unicode = require("unicode")
|
||||||
local event = require("event")
|
local event = require("event")
|
||||||
|
local thread = require("thread")
|
||||||
local event_mt = getmetatable(event.handlers)
|
local event_mt = getmetatable(event.handlers)
|
||||||
|
|
||||||
-- WARNING this code does not use official kernel API and is likely to change
|
-- WARNING this code does not use official kernel API and is likely to change
|
||||||
@ -42,33 +43,38 @@ local cols =
|
|||||||
return count == 0 and "-" or tostring(count)
|
return count == 0 and "-" or tostring(count)
|
||||||
end},
|
end},
|
||||||
{"THREADS", function(_,p)
|
{"THREADS", function(_,p)
|
||||||
-- threads are handles with mt.close
|
-- threads are handles with mt.close == thread.waitForAll
|
||||||
local count = 0
|
local count = 0
|
||||||
for _,h in pairs(p.data.handles or {}) do
|
for h in pairs(p.data.handles) do
|
||||||
local mt = getmetatable(h)
|
local mt = getmetatable(h)
|
||||||
if mt and mt.__index and mt.__index.close then
|
if mt and mt.__status then
|
||||||
count = count + #h
|
count = count + 1
|
||||||
break -- there is only one thread handle manager
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return count == 0 and "-" or tostring(count)
|
return count == 0 and "-" or tostring(count)
|
||||||
end},
|
end},
|
||||||
{"PARENT", function(_,p)
|
{"PARENT", function(_,p)
|
||||||
for _,process_info in pairs(process.list) do
|
for _,process_info in pairs(process.list) do
|
||||||
for _,handle in pairs(process_info.data.handles or {}) do
|
for handle in pairs(process_info.data.handles) do
|
||||||
local mt = getmetatable(handle)
|
local mt = getmetatable(handle)
|
||||||
if mt and mt.__index and mt.__index.close then
|
if mt and mt.__status then
|
||||||
for _,ht in ipairs(handle) do
|
if mt.process == p then
|
||||||
local ht_mt = getmetatable(ht)
|
return thread_id(nil, process_info)
|
||||||
if ht_mt.process == p then
|
|
||||||
return thread_id(nil,process_info)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
break
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return thread_id(nil,p.parent)
|
return thread_id(nil, p.parent)
|
||||||
|
end},
|
||||||
|
{"HANDLES", function(_, p)
|
||||||
|
local count = 0
|
||||||
|
for stream,closure in pairs(p.data.handles) do
|
||||||
|
cprint(string.format("%s %s", stream, closure))
|
||||||
|
if closure then
|
||||||
|
count = count + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return count == 0 and "-" or tostring(count)
|
||||||
end},
|
end},
|
||||||
{"CMD", function(_,p) return p.command end},
|
{"CMD", function(_,p) return p.command end},
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
local process = require("process")
|
local process = require("process")
|
||||||
|
local fs = require("filesystem")
|
||||||
|
|
||||||
--Initialize coroutine library--
|
--Initialize coroutine library--
|
||||||
local _coroutine = coroutine -- real coroutine backend
|
local _coroutine = coroutine -- real coroutine backend
|
||||||
@ -62,8 +63,20 @@ process.list[init_thread] = {
|
|||||||
data =
|
data =
|
||||||
{
|
{
|
||||||
vars={},
|
vars={},
|
||||||
|
handles={},
|
||||||
io={}, --init will populate this
|
io={}, --init will populate this
|
||||||
coroutine_handler = _coroutine
|
coroutine_handler = _coroutine
|
||||||
},
|
},
|
||||||
instances = setmetatable({}, {__mode="v"})
|
instances = setmetatable({}, {__mode="v"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- intercept fs open
|
||||||
|
local fs_open = fs.open
|
||||||
|
fs.open = function(...)
|
||||||
|
local fs_open_result = table.pack(fs_open(...))
|
||||||
|
if fs_open_result[1] then
|
||||||
|
process.closeOnExit(fs_open_result[1])
|
||||||
|
end
|
||||||
|
return table.unpack(fs_open_result, 1, fs_open_result.n)
|
||||||
|
end
|
||||||
|
|
||||||
|
@ -17,21 +17,32 @@ function buffer.new(mode, stream)
|
|||||||
bufferWrite = "",
|
bufferWrite = "",
|
||||||
bufferSize = math.max(512, math.min(8 * 1024, computer.freeMemory() / 8)),
|
bufferSize = math.max(512, math.min(8 * 1024, computer.freeMemory() / 8)),
|
||||||
bufferMode = "full",
|
bufferMode = "full",
|
||||||
readTimeout = math.huge
|
readTimeout = math.huge,
|
||||||
}
|
}
|
||||||
mode = mode or "r"
|
mode = mode or "r"
|
||||||
for i = 1, unicode.len(mode) do
|
for i = 1, unicode.len(mode) do
|
||||||
result.mode[unicode.sub(mode, i, i)] = true
|
result.mode[unicode.sub(mode, i, i)] = true
|
||||||
end
|
end
|
||||||
|
-- when stream closes, result should close first
|
||||||
|
-- when result closes, stream should close after
|
||||||
|
-- when stream closes, it is removed from the proc
|
||||||
|
stream.close = setmetatable({close = stream.close,parent = result},{__call = buffer.close})
|
||||||
return setmetatable(result, metatable)
|
return setmetatable(result, metatable)
|
||||||
end
|
end
|
||||||
|
|
||||||
function buffer:close()
|
function buffer:close()
|
||||||
if self.mode.w or self.mode.a then
|
-- self is either the buffer, or the stream.close callable
|
||||||
self:flush()
|
local meta = getmetatable(self)
|
||||||
|
if meta == metatable.__metatable then
|
||||||
|
return self.stream:close()
|
||||||
end
|
end
|
||||||
self.closed = true
|
local parent = self.parent
|
||||||
return self.stream:close()
|
|
||||||
|
if parent.mode.w or parent.mode.a then
|
||||||
|
parent:flush()
|
||||||
|
end
|
||||||
|
parent.closed = true
|
||||||
|
return self.close(parent.stream)
|
||||||
end
|
end
|
||||||
|
|
||||||
function buffer:flush()
|
function buffer:flush()
|
||||||
|
@ -78,7 +78,7 @@ end
|
|||||||
-- redirects as built by buildCommentRedirects
|
-- redirects as built by buildCommentRedirects
|
||||||
function sh.internal.openCommandRedirects(redirects)
|
function sh.internal.openCommandRedirects(redirects)
|
||||||
local data = process.info().data
|
local data = process.info().data
|
||||||
local ios, handles = data.io, data.handles
|
local ios = data.io
|
||||||
|
|
||||||
for _,rjob in ipairs(redirects) do
|
for _,rjob in ipairs(redirects) do
|
||||||
local from_io, to_io, mode = table.unpack(rjob)
|
local from_io, to_io, mode = table.unpack(rjob)
|
||||||
@ -93,7 +93,6 @@ function sh.internal.openCommandRedirects(redirects)
|
|||||||
io.stderr:write("could not open '" .. to_io .. "': " .. reason .. "\n")
|
io.stderr:write("could not open '" .. to_io .. "': " .. reason .. "\n")
|
||||||
os.exit(1)
|
os.exit(1)
|
||||||
end
|
end
|
||||||
table.insert(handles, file)
|
|
||||||
ios[from_io] = file
|
ios[from_io] = file
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -134,16 +134,16 @@ function pipe.buildPipeChain(progs)
|
|||||||
-- B needs to be a stack in case any thread in B calls read
|
-- B needs to be a stack in case any thread in B calls read
|
||||||
pipe.createCoroutineStack(thread)
|
pipe.createCoroutineStack(thread)
|
||||||
chain[i] = thread
|
chain[i] = thread
|
||||||
local data = process.info(thread).data
|
local proc = process.info(thread)
|
||||||
local pio = data.io
|
local pio = proc.data.io
|
||||||
|
|
||||||
local piped_stream
|
local piped_stream
|
||||||
if i < #progs then
|
if i < #progs then
|
||||||
local handle = setmetatable({redirect = {rawget(pio, 1)},buffer = ""}, {__index = pipe_stream})
|
local handle = setmetatable({redirect = {rawget(pio, 1)},buffer = ""}, {__index = pipe_stream})
|
||||||
|
process.closeOnExit(handle, proc)
|
||||||
piped_stream = buffer.new("rw", handle)
|
piped_stream = buffer.new("rw", handle)
|
||||||
piped_stream:setvbuf("no", 1024)
|
piped_stream:setvbuf("no", 1024)
|
||||||
pio[1] = piped_stream
|
pio[1] = piped_stream
|
||||||
table.insert(data.handles, piped_stream)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if prev_piped_stream then
|
if prev_piped_stream then
|
||||||
@ -194,7 +194,7 @@ function pipe.popen(prog, mode, env)
|
|||||||
|
|
||||||
local chain = {}
|
local chain = {}
|
||||||
-- to simplify the code - shell.execute is run within a function to pass (prog, env)
|
-- to simplify the code - shell.execute is run within a function to pass (prog, env)
|
||||||
-- if cmd_proc where to come second (mode=="w") then the pipe_proc would have to pass
|
-- if cmd_proc were to come second (mode=="w") then the pipe_proc would have to pass
|
||||||
-- the starting args. which is possible, just more complicated
|
-- the starting args. which is possible, just more complicated
|
||||||
local cmd_proc = process.load(function() return shell.execute(prog, env) end, nil, nil, prog)
|
local cmd_proc = process.load(function() return shell.execute(prog, env) end, nil, nil, prog)
|
||||||
|
|
||||||
|
@ -125,7 +125,11 @@ function process.internal.close(thread, result)
|
|||||||
checkArg(1,thread,"thread")
|
checkArg(1,thread,"thread")
|
||||||
local pdata = process.info(thread).data
|
local pdata = process.info(thread).data
|
||||||
pdata.result = result
|
pdata.result = result
|
||||||
for _,v in pairs(pdata.handles) do
|
local handles = {}
|
||||||
|
for s,_ in pairs(pdata.handles) do
|
||||||
|
table.insert(handles, s)
|
||||||
|
end
|
||||||
|
for _,v in ipairs(handles) do
|
||||||
pcall(v.close, v)
|
pcall(v.close, v)
|
||||||
end
|
end
|
||||||
process.list[thread] = nil
|
process.list[thread] = nil
|
||||||
@ -146,6 +150,20 @@ function process.internal.continue(co, ...)
|
|||||||
return table.unpack(result, 2, result.n)
|
return table.unpack(result, 2, result.n)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function process.closeOnExit(stream, proc)
|
||||||
|
local handles = (proc or process.info()).data.handles
|
||||||
|
if not handles[stream] then
|
||||||
|
handles[stream] = stream.close
|
||||||
|
stream.close = function(...)
|
||||||
|
local close = handles[stream]
|
||||||
|
handles[stream] = nil
|
||||||
|
if close then
|
||||||
|
return close(...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function process.running(level) -- kept for backwards compat, prefer process.info
|
function process.running(level) -- kept for backwards compat, prefer process.info
|
||||||
local info = process.info(level)
|
local info = process.info(level)
|
||||||
if info then
|
if info then
|
||||||
|
@ -60,22 +60,6 @@ function thread.waitForAll(threads, timeout)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local box_thread = {}
|
local box_thread = {}
|
||||||
local box_thread_list = {close = thread.waitForAll}
|
|
||||||
|
|
||||||
local function get_process_threads(proc, bCreate)
|
|
||||||
local handles = proc.data.handles
|
|
||||||
for _,next_handle in ipairs(handles) do
|
|
||||||
local handle_mt = getmetatable(next_handle)
|
|
||||||
if handle_mt and handle_mt.__index == box_thread_list then
|
|
||||||
return next_handle
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if bCreate then
|
|
||||||
local btm = setmetatable({}, {__index = box_thread_list})
|
|
||||||
table.insert(handles, btm)
|
|
||||||
return btm
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function box_thread:resume()
|
function box_thread:resume()
|
||||||
local mt = getmetatable(self)
|
local mt = getmetatable(self)
|
||||||
@ -120,31 +104,25 @@ function box_thread:detach()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function box_thread:attach(parent)
|
function box_thread:attach(parent)
|
||||||
checkArg(1, parent, "thread", "number", "nil")
|
|
||||||
local mt = assert(getmetatable(self), "thread panic: no metadata")
|
|
||||||
local proc = process.info(parent)
|
local proc = process.info(parent)
|
||||||
|
local mt = assert(getmetatable(self), "thread panic: no metadata")
|
||||||
if not proc then return nil, "thread failed to attach, process not found" end
|
if not proc then return nil, "thread failed to attach, process not found" end
|
||||||
if mt.attached == proc then return self end -- already attached
|
if mt.attached == proc then return self end -- already attached
|
||||||
|
|
||||||
|
-- remove from old parent
|
||||||
|
local waiting_handler
|
||||||
if mt.attached then
|
if mt.attached then
|
||||||
local prev_threads = assert(get_process_threads(mt.attached), "thread panic: no thread handle")
|
-- registration happens on the attached proc, unregister before reparenting
|
||||||
for index,t_in_list in ipairs(prev_threads) do
|
waiting_handler = mt.unregister()
|
||||||
if t_in_list == self then
|
mt.attached.data.handles[self] = nil
|
||||||
table.remove(prev_threads, index)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- registration happens on the attached proc, unregister before reparenting
|
-- fix close
|
||||||
local waiting_handler = mt.unregister()
|
self.close = self.join
|
||||||
|
|
||||||
-- attach to parent or the current process
|
-- attach to parent or the current process
|
||||||
mt.attached = proc
|
mt.attached = proc
|
||||||
|
process.closeOnExit(self, proc)
|
||||||
-- this process may not have a box_thread list
|
|
||||||
local threads = get_process_threads(proc, true)
|
|
||||||
table.insert(threads, self)
|
|
||||||
|
|
||||||
-- register on the new parent
|
-- register on the new parent
|
||||||
if waiting_handler then -- event-waiting
|
if waiting_handler then -- event-waiting
|
||||||
@ -159,9 +137,9 @@ function thread.current()
|
|||||||
local thread_root
|
local thread_root
|
||||||
while proc do
|
while proc do
|
||||||
if thread_root then
|
if thread_root then
|
||||||
for _,bt in ipairs(get_process_threads(proc) or {}) do
|
for handle in pairs(proc.data.handles) do
|
||||||
if bt.pco.root == thread_root then
|
if handle.pco and handle.pco.root == thread_root then
|
||||||
return bt
|
return handle
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@ -174,9 +152,8 @@ end
|
|||||||
function thread.create(fp, ...)
|
function thread.create(fp, ...)
|
||||||
checkArg(1, fp, "function")
|
checkArg(1, fp, "function")
|
||||||
|
|
||||||
local t = {}
|
|
||||||
local mt = {__status="suspended",__index=box_thread}
|
local mt = {__status="suspended",__index=box_thread}
|
||||||
setmetatable(t, mt)
|
local t = setmetatable({}, mt)
|
||||||
t.pco = pipe.createCoroutineStack(function(...)
|
t.pco = pipe.createCoroutineStack(function(...)
|
||||||
mt.__status = "running"
|
mt.__status = "running"
|
||||||
local fp_co = t.pco.create(fp)
|
local fp_co = t.pco.create(fp)
|
||||||
@ -246,7 +223,7 @@ function thread.create(fp, ...)
|
|||||||
mt.id = nil
|
mt.id = nil
|
||||||
mt.reg = nil
|
mt.reg = nil
|
||||||
-- before just removing a handler, make sure it is still ours
|
-- before just removing a handler, make sure it is still ours
|
||||||
if id and mt.attached and mt.attached.data.handlers[id] == reg then
|
if id and mt.attached.data.handlers[id] == reg then
|
||||||
mt.attached.data.handlers[id] = nil
|
mt.attached.data.handlers[id] = nil
|
||||||
return reg
|
return reg
|
||||||
end
|
end
|
||||||
@ -285,18 +262,12 @@ function thread.create(fp, ...)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function mt.close()
|
function mt.close()
|
||||||
if t:status() == "dead" then
|
local old_status = t:status()
|
||||||
return
|
|
||||||
end
|
|
||||||
local threads = get_process_threads(mt.attached)
|
|
||||||
for index,t_in_list in ipairs(threads) do
|
|
||||||
if t_in_list == t then
|
|
||||||
table.remove(threads, index)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
mt.__status = "dead"
|
mt.__status = "dead"
|
||||||
event.push("thread_exit")
|
mt.attached.data.handles[t] = nil
|
||||||
|
if old_status ~= "dead" then
|
||||||
|
event.push("thread_exit")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
t:attach() -- the current process
|
t:attach() -- the current process
|
||||||
@ -321,8 +292,6 @@ do
|
|||||||
end
|
end
|
||||||
assert(init_thread, "thread library panic: no init thread")
|
assert(init_thread, "thread library panic: no init thread")
|
||||||
handlers_mt.threaded = true
|
handlers_mt.threaded = true
|
||||||
-- handles might be optimized out for memory
|
|
||||||
root_data.handles = root_data.handles or {}
|
|
||||||
-- if we don't separate root handlers from thread handlers we see double dispatch
|
-- if we don't separate root handlers from thread handlers we see double dispatch
|
||||||
-- because the thread calls dispatch on pull as well
|
-- because the thread calls dispatch on pull as well
|
||||||
root_data.handlers = {} -- root handlers
|
root_data.handlers = {} -- root handlers
|
||||||
|
@ -938,7 +938,14 @@ sandbox = {
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
traceback = debug.traceback
|
traceback = debug.traceback,
|
||||||
|
-- using () to wrap the return of debug methods because in Lua doing this
|
||||||
|
-- causes only the first return value to be selected
|
||||||
|
-- e.g. (1, 2) is only (1), the 2 is not returned
|
||||||
|
-- this is critically important here because the 2nd return value from these
|
||||||
|
-- debug methods is the value itself, which opens a door to exploit the sandbox
|
||||||
|
getlocal = function(...) return (debug.getlocal(...)) end,
|
||||||
|
getupvalue = function(...) return (debug.getupvalue(...)) end,
|
||||||
},
|
},
|
||||||
|
|
||||||
-- Lua 5.3.
|
-- Lua 5.3.
|
||||||
|
@ -51,54 +51,64 @@ abstract class Player(val playerInventory: InventoryPlayer, val otherInventory:
|
|||||||
ItemStack.EMPTY
|
ItemStack.EMPTY
|
||||||
}
|
}
|
||||||
|
|
||||||
protected def tryTransferStackInSlot(from: Slot, intoPlayerInventory: Boolean) {
|
// return true if all items have been moved or no more work to do
|
||||||
|
protected def tryMoveAllSlotToSlot(from: Slot, to: Slot): Boolean = {
|
||||||
|
if (to == null)
|
||||||
|
return false // nowhere to move it
|
||||||
|
|
||||||
|
if (from == null ||
|
||||||
|
!from.getHasStack ||
|
||||||
|
from.getStack.isEmpty)
|
||||||
|
return true // all moved because nothing to move
|
||||||
|
|
||||||
|
if (to.inventory == from.inventory)
|
||||||
|
return false // not intended for moving in the same inventory
|
||||||
|
|
||||||
|
// for ghost slots we don't care about stack size
|
||||||
val fromStack = from.getStack
|
val fromStack = from.getStack
|
||||||
var somethingChanged = false
|
val toStack = if (to.getHasStack) to.getStack else null
|
||||||
|
val toStackSize = if (!toStack.isEmpty) toStack.getCount else 0
|
||||||
|
|
||||||
val step = if (intoPlayerInventory) -1 else 1
|
val maxStackSize = math.min(fromStack.getMaxStackSize, to.getSlotStackLimit)
|
||||||
val (begin, end) =
|
val itemsMoved = math.min(maxStackSize - toStackSize, fromStack.getCount)
|
||||||
if (intoPlayerInventory) (inventorySlots.size - 1, 0)
|
|
||||||
else (0, inventorySlots.size - 1)
|
|
||||||
|
|
||||||
if (fromStack.getMaxStackSize > 1) for (i <- begin to end by step if i >= 0 && i < inventorySlots.size && from.getHasStack && from.getStack.getCount > 0) {
|
if (toStack != null) {
|
||||||
val intoSlot = inventorySlots.get(i)
|
if (toStackSize < maxStackSize &&
|
||||||
if (intoSlot.inventory != from.inventory && intoSlot.getHasStack) {
|
fromStack.isItemEqual(toStack) &&
|
||||||
val intoStack = intoSlot.getStack
|
ItemStack.areItemStackTagsEqual(fromStack, toStack) &&
|
||||||
val itemsAreEqual = fromStack.isItemEqual(intoStack) && ItemStack.areItemStackTagsEqual(fromStack, intoStack)
|
itemsMoved > 0) {
|
||||||
val maxStackSize = math.min(fromStack.getMaxStackSize, intoSlot.getSlotStackLimit)
|
toStack.grow(from.decrStackSize(itemsMoved).getCount)
|
||||||
val slotHasCapacity = intoStack.getCount < maxStackSize
|
} else return false
|
||||||
if (itemsAreEqual && slotHasCapacity) {
|
} else if (to.isItemValid(fromStack)) {
|
||||||
val itemsMoved = math.min(maxStackSize - intoStack.getCount, fromStack.getCount)
|
to.putStack(from.decrStackSize(itemsMoved))
|
||||||
if (itemsMoved > 0) {
|
if (maxStackSize == 0) {
|
||||||
intoStack.grow(from.decrStackSize(itemsMoved).getCount)
|
// Special case: we have an inventory with "phantom/ghost stacks", i.e.
|
||||||
intoSlot.onSlotChanged()
|
// zero size stacks, usually used for configuring machinery. In that
|
||||||
somethingChanged = true
|
// case we stop early if whatever we're shift clicking is already in a
|
||||||
}
|
// slot of the target inventory. This workaround can be problematic if
|
||||||
}
|
// an inventory has both real and phantom slots, but we don't have
|
||||||
|
// something like that, yet, so hey.
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
} else return false
|
||||||
|
|
||||||
for (i <- begin to end by step if i >= 0 && i < inventorySlots.size && from.getHasStack && from.getStack.getCount > 0) {
|
to.onSlotChanged()
|
||||||
val intoSlot = inventorySlots.get(i)
|
from.onSlotChanged()
|
||||||
if (intoSlot.inventory != from.inventory && !intoSlot.getHasStack && intoSlot.isItemValid(fromStack)) {
|
false
|
||||||
val maxStackSize = math.min(fromStack.getMaxStackSize, intoSlot.getSlotStackLimit)
|
}
|
||||||
val itemsMoved = math.min(maxStackSize, fromStack.getCount)
|
|
||||||
intoSlot.putStack(from.decrStackSize(itemsMoved))
|
|
||||||
if (maxStackSize == 0) {
|
|
||||||
// Special case: we have an inventory with "phantom/ghost stacks", i.e.
|
|
||||||
// zero size stacks, usually used for configuring machinery. In that
|
|
||||||
// case we stop early if whatever we're shift clicking is already in a
|
|
||||||
// slot of the target inventory. This workaround can be problematic if
|
|
||||||
// an inventory has both real and phantom slots, but we don't have
|
|
||||||
// something like that, yet, so hey.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
somethingChanged = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (somethingChanged) {
|
protected def fillOrder(backFill: Boolean): Seq[Int] = {
|
||||||
from.onSlotChanged()
|
(if (backFill) inventorySlots.indices.reverse else inventorySlots.indices).sortBy(i => inventorySlots(i) match {
|
||||||
|
case s: Slot if s.getHasStack => -1
|
||||||
|
case s: ComponentSlot => s.tier
|
||||||
|
case _ => 99
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
protected def tryTransferStackInSlot(from: Slot, intoPlayerInventory: Boolean) {
|
||||||
|
for (i <- fillOrder(intoPlayerInventory)) {
|
||||||
|
if (inventorySlots.get(i) match { case slot: Slot => tryMoveAllSlotToSlot(from, slot) case _ => false })
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,6 +249,7 @@ object Items extends ItemAPI {
|
|||||||
data.components = Array(
|
data.components = Array(
|
||||||
safeGetStack(Constants.BlockName.ScreenTier1),
|
safeGetStack(Constants.BlockName.ScreenTier1),
|
||||||
safeGetStack(Constants.BlockName.Keyboard),
|
safeGetStack(Constants.BlockName.Keyboard),
|
||||||
|
safeGetStack(Constants.BlockName.Geolyzer),
|
||||||
safeGetStack(Constants.ItemName.InventoryUpgrade),
|
safeGetStack(Constants.ItemName.InventoryUpgrade),
|
||||||
safeGetStack(Constants.ItemName.InventoryUpgrade),
|
safeGetStack(Constants.ItemName.InventoryUpgrade),
|
||||||
safeGetStack(Constants.ItemName.InventoryUpgrade),
|
safeGetStack(Constants.ItemName.InventoryUpgrade),
|
||||||
@ -257,6 +258,7 @@ object Items extends ItemAPI {
|
|||||||
safeGetStack(Constants.ItemName.TankUpgrade),
|
safeGetStack(Constants.ItemName.TankUpgrade),
|
||||||
safeGetStack(Constants.ItemName.TankControllerUpgrade),
|
safeGetStack(Constants.ItemName.TankControllerUpgrade),
|
||||||
safeGetStack(Constants.ItemName.CraftingUpgrade),
|
safeGetStack(Constants.ItemName.CraftingUpgrade),
|
||||||
|
safeGetStack(Constants.ItemName.HoverUpgradeTier2),
|
||||||
|
|
||||||
safeGetStack(Constants.ItemName.GraphicsCardTier3),
|
safeGetStack(Constants.ItemName.GraphicsCardTier3),
|
||||||
safeGetStack(Constants.ItemName.RedstoneCardTier2),
|
safeGetStack(Constants.ItemName.RedstoneCardTier2),
|
||||||
|
@ -130,7 +130,7 @@ trait ComponentInventory extends Inventory with network.Environment {
|
|||||||
|
|
||||||
override def getInventoryStackLimit = 1
|
override def getInventoryStackLimit = 1
|
||||||
|
|
||||||
override protected def onItemAdded(slot: Int, stack: ItemStack) = if (isComponentSlot(slot, stack)) {
|
override protected def onItemAdded(slot: Int, stack: ItemStack) = if (slot >= 0 && slot < components.length && isComponentSlot(slot, stack)) {
|
||||||
Option(Driver.driverFor(stack)).foreach(driver =>
|
Option(Driver.driverFor(stack)).foreach(driver =>
|
||||||
Option(driver.createEnvironment(stack, host)) match {
|
Option(driver.createEnvironment(stack, host)) match {
|
||||||
case Some(component) => this.synchronized {
|
case Some(component) => this.synchronized {
|
||||||
@ -154,7 +154,7 @@ trait ComponentInventory extends Inventory with network.Environment {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override protected def onItemRemoved(slot: Int, stack: ItemStack) {
|
override protected def onItemRemoved(slot: Int, stack: ItemStack): Unit = if (slot >= 0 && slot < components.length) {
|
||||||
// Uninstall component previously in that slot.
|
// Uninstall component previously in that slot.
|
||||||
components(slot) match {
|
components(slot) match {
|
||||||
case Some(component) => this.synchronized {
|
case Some(component) => this.synchronized {
|
||||||
|
@ -4,6 +4,7 @@ import li.cil.oc.Settings
|
|||||||
import li.cil.oc.api
|
import li.cil.oc.api
|
||||||
import li.cil.oc.api.network.Visibility
|
import li.cil.oc.api.network.Visibility
|
||||||
import li.cil.oc.common.EventHandler
|
import li.cil.oc.common.EventHandler
|
||||||
|
import li.cil.oc.common.tileentity.traits.RedstoneChangedEventArgs
|
||||||
import li.cil.oc.server.{PacketSender => ServerPacketSender}
|
import li.cil.oc.server.{PacketSender => ServerPacketSender}
|
||||||
import net.minecraft.init.SoundEvents
|
import net.minecraft.init.SoundEvents
|
||||||
import net.minecraft.nbt.NBTTagCompound
|
import net.minecraft.nbt.NBTTagCompound
|
||||||
@ -52,10 +53,10 @@ class NetSplitter extends traits.Environment with traits.OpenSides with traits.R
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
override protected def onRedstoneInputChanged(side: EnumFacing, oldMaxValue: Int, newMaxValue: Int): Unit = {
|
override protected def onRedstoneInputChanged(args: RedstoneChangedEventArgs): Unit = {
|
||||||
super.onRedstoneInputChanged(side, oldMaxValue, newMaxValue)
|
super.onRedstoneInputChanged(args)
|
||||||
val oldIsInverted = isInverted
|
val oldIsInverted = isInverted
|
||||||
isInverted = newMaxValue > 0
|
isInverted = args.newValue > 0
|
||||||
if (isInverted != oldIsInverted) {
|
if (isInverted != oldIsInverted) {
|
||||||
if (isServer) {
|
if (isServer) {
|
||||||
node.remove()
|
node.remove()
|
||||||
|
@ -7,6 +7,7 @@ import li.cil.oc.Constants
|
|||||||
import li.cil.oc.Settings
|
import li.cil.oc.Settings
|
||||||
import li.cil.oc.api
|
import li.cil.oc.api
|
||||||
import li.cil.oc.common.item.data.PrintData
|
import li.cil.oc.common.item.data.PrintData
|
||||||
|
import li.cil.oc.common.tileentity.traits.RedstoneChangedEventArgs
|
||||||
import li.cil.oc.util.ExtendedAABB
|
import li.cil.oc.util.ExtendedAABB
|
||||||
import li.cil.oc.util.ExtendedAABB._
|
import li.cil.oc.util.ExtendedAABB._
|
||||||
import li.cil.oc.util.ExtendedNBT._
|
import li.cil.oc.util.ExtendedNBT._
|
||||||
@ -145,9 +146,8 @@ class Print(val canToggle: Option[() => Boolean], val scheduleUpdate: Option[Int
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override protected def onRedstoneInputChanged(side: EnumFacing, oldMaxValue: Int, newMaxValue: Int): Unit = {
|
override protected def onRedstoneInputChanged(args: RedstoneChangedEventArgs): Unit = {
|
||||||
super.onRedstoneInputChanged(side, oldMaxValue, newMaxValue)
|
val newState = args.newValue > 0
|
||||||
val newState = newMaxValue > 0
|
|
||||||
if (!data.emitRedstone && data.hasActiveState && state != newState) {
|
if (!data.emitRedstone && data.hasActiveState && state != newState) {
|
||||||
toggleState()
|
toggleState()
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import li.cil.oc.api.network.Packet
|
|||||||
import li.cil.oc.api.network.Visibility
|
import li.cil.oc.api.network.Visibility
|
||||||
import li.cil.oc.api.util.StateAware
|
import li.cil.oc.api.util.StateAware
|
||||||
import li.cil.oc.common.Slot
|
import li.cil.oc.common.Slot
|
||||||
|
import li.cil.oc.common.tileentity.traits.RedstoneChangedEventArgs
|
||||||
import li.cil.oc.integration.opencomputers.DriverRedstoneCard
|
import li.cil.oc.integration.opencomputers.DriverRedstoneCard
|
||||||
import li.cil.oc.server.{PacketSender => ServerPacketSender}
|
import li.cil.oc.server.{PacketSender => ServerPacketSender}
|
||||||
import li.cil.oc.util.ExtendedInventory._
|
import li.cil.oc.util.ExtendedInventory._
|
||||||
@ -290,11 +291,11 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance
|
|||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
// RedstoneAware
|
// RedstoneAware
|
||||||
|
|
||||||
override protected def onRedstoneInputChanged(side: EnumFacing, oldMaxValue: Int, newMaxValue: Int) {
|
override protected def onRedstoneInputChanged(args: RedstoneChangedEventArgs) {
|
||||||
super.onRedstoneInputChanged(side, oldMaxValue, newMaxValue)
|
super.onRedstoneInputChanged(args)
|
||||||
components.collect {
|
components.collect {
|
||||||
case Some(mountable: RackMountable) if mountable.node != null =>
|
case Some(mountable: RackMountable) if mountable.node != null =>
|
||||||
mountable.node.sendToNeighbors("redstone.changed", toLocal(side), int2Integer(oldMaxValue), int2Integer(newMaxValue))
|
mountable.node.sendToNeighbors("redstone.changed", args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import li.cil.oc.api
|
|||||||
import li.cil.oc.api.network.Component
|
import li.cil.oc.api.network.Component
|
||||||
import li.cil.oc.api.network.Node
|
import li.cil.oc.api.network.Node
|
||||||
import li.cil.oc.api.network.Visibility
|
import li.cil.oc.api.network.Visibility
|
||||||
|
import li.cil.oc.common.tileentity.traits.RedstoneChangedEventArgs
|
||||||
import li.cil.oc.integration.util.BundledRedstone
|
import li.cil.oc.integration.util.BundledRedstone
|
||||||
import li.cil.oc.server.component
|
import li.cil.oc.server.component
|
||||||
import li.cil.oc.server.component.RedstoneVanilla
|
import li.cil.oc.server.component.RedstoneVanilla
|
||||||
@ -43,11 +44,11 @@ class Redstone extends traits.Environment with traits.BundledRedstoneAware with
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
override protected def onRedstoneInputChanged(side: EnumFacing, oldMaxValue: Int, newMaxValue: Int) {
|
override protected def onRedstoneInputChanged(args: RedstoneChangedEventArgs) {
|
||||||
super.onRedstoneInputChanged(side, oldMaxValue, newMaxValue)
|
super.onRedstoneInputChanged(args)
|
||||||
if (node != null && node.network != null) {
|
if (node != null && node.network != null) {
|
||||||
node.connect(dummyNode)
|
node.connect(dummyNode)
|
||||||
dummyNode.sendToNeighbors("redstone.changed", side, Int.box(oldMaxValue), Int.box(newMaxValue))
|
dummyNode.sendToNeighbors("redstone.changed", args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import li.cil.oc.api.network._
|
|||||||
import li.cil.oc.client.gui
|
import li.cil.oc.client.gui
|
||||||
import li.cil.oc.common.component.TextBuffer
|
import li.cil.oc.common.component.TextBuffer
|
||||||
import li.cil.oc.util.BlockPosition
|
import li.cil.oc.util.BlockPosition
|
||||||
|
import li.cil.oc.common.tileentity.traits.RedstoneChangedEventArgs
|
||||||
import li.cil.oc.util.Color
|
import li.cil.oc.util.Color
|
||||||
import li.cil.oc.util.ExtendedWorld._
|
import li.cil.oc.util.ExtendedWorld._
|
||||||
import net.minecraft.client.Minecraft
|
import net.minecraft.client.Minecraft
|
||||||
@ -344,8 +345,8 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with
|
|||||||
|
|
||||||
override def onAnalyze(player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = Array(origin.node)
|
override def onAnalyze(player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float) = Array(origin.node)
|
||||||
|
|
||||||
override protected def onRedstoneInputChanged(side: EnumFacing, oldMaxValue: Int, newMaxValue: Int) {
|
override protected def onRedstoneInputChanged(args: RedstoneChangedEventArgs) {
|
||||||
super.onRedstoneInputChanged(side, oldMaxValue, newMaxValue)
|
super.onRedstoneInputChanged(args)
|
||||||
val hasRedstoneInput = screens.map(_.maxInput).max > 0
|
val hasRedstoneInput = screens.map(_.maxInput).max > 0
|
||||||
if (hasRedstoneInput != hadRedstoneInput) {
|
if (hasRedstoneInput != hadRedstoneInput) {
|
||||||
hadRedstoneInput = hasRedstoneInput
|
hadRedstoneInput = hasRedstoneInput
|
||||||
|
@ -32,39 +32,31 @@ trait BundledRedstoneAware extends RedstoneAware {
|
|||||||
super.isOutputEnabled_=(value)
|
super.isOutputEnabled_=(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def bundledInput(side: EnumFacing, color: Int): Int =
|
||||||
|
math.max(_bundledInput(side.ordinal())(color), _rednetInput(side.ordinal())(color))
|
||||||
|
|
||||||
def bundledInput(side: EnumFacing): Array[Int] =
|
def bundledInput(side: EnumFacing): Array[Int] =
|
||||||
(_bundledInput(side.ordinal()), _rednetInput(side.ordinal())).zipped.map(math.max)
|
(_bundledInput(side.ordinal()), _rednetInput(side.ordinal())).zipped.map(math.max)
|
||||||
|
|
||||||
def bundledInput(side: EnumFacing, newBundledInput: Array[Int]): Unit = {
|
def bundledInput(side: EnumFacing, newBundledInput: Array[Int]): Unit = {
|
||||||
val ownBundledInput = _bundledInput(side.ordinal())
|
for (color <- 0 until 16) {
|
||||||
val oldMaxValue = ownBundledInput.max
|
updateInput(_bundledInput, side, color, if (newBundledInput == null) 0 else newBundledInput(color))
|
||||||
var changed = false
|
|
||||||
if (newBundledInput != null) for (color <- 0 until 16) {
|
|
||||||
changed = changed || (ownBundledInput(color) >= 0 && ownBundledInput(color) != newBundledInput(color))
|
|
||||||
ownBundledInput(color) = newBundledInput(color)
|
|
||||||
}
|
|
||||||
else for (color <- 0 until 16) {
|
|
||||||
changed = changed || ownBundledInput(color) > 0
|
|
||||||
ownBundledInput(color) = 0
|
|
||||||
}
|
|
||||||
if (changed) {
|
|
||||||
onRedstoneInputChanged(side, oldMaxValue, ownBundledInput.max)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def bundledInput(side: EnumFacing, color: Int): Int =
|
def rednetInput(side: EnumFacing, color: Int, value: Int): Unit = updateInput(_rednetInput, side, color, value)
|
||||||
math.max(_bundledInput(side.ordinal())(color), _rednetInput(side.ordinal())(color))
|
|
||||||
|
|
||||||
def rednetInput(side: EnumFacing, color: Int, value: Int): Unit = {
|
def updateInput(inputs: Array[Array[Int]], side: EnumFacing, color: Int, newValue: Int): Unit = {
|
||||||
val oldValue = _rednetInput(side.ordinal())(color)
|
val oldValue = inputs(side.ordinal())(color)
|
||||||
if (oldValue != value) {
|
if (oldValue != newValue) {
|
||||||
if (oldValue != -1) {
|
if (oldValue != -1) {
|
||||||
onRedstoneInputChanged(side, oldValue, value)
|
onRedstoneInputChanged(RedstoneChangedEventArgs(side, oldValue, newValue, color))
|
||||||
}
|
}
|
||||||
_rednetInput(side.ordinal())(color) = value
|
inputs(side.ordinal())(color) = newValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def bundledOutput(side: EnumFacing): Array[Int] = _bundledOutput(toLocal(side).ordinal())
|
def bundledOutput(side: EnumFacing): Array[Int] = _bundledOutput(toLocal(side).ordinal())
|
||||||
|
|
||||||
def bundledOutput(side: EnumFacing, color: Int): Int = bundledOutput(side)(color)
|
def bundledOutput(side: EnumFacing, color: Int): Int = bundledOutput(side)(color)
|
||||||
|
@ -203,9 +203,9 @@ trait Computer extends Environment with ComponentInventory with Rotatable with B
|
|||||||
checkRedstoneInputChanged()
|
checkRedstoneInputChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
override protected def onRedstoneInputChanged(side: EnumFacing, oldMaxValue: Int, newMaxValue: Int) {
|
override protected def onRedstoneInputChanged(args: RedstoneChangedEventArgs) {
|
||||||
super.onRedstoneInputChanged(side, oldMaxValue, newMaxValue)
|
super.onRedstoneInputChanged(args)
|
||||||
machine.node.sendToNeighbors("redstone.changed", toLocal(side), Int.box(oldMaxValue), Int.box(newMaxValue))
|
machine.node.sendToNeighbors("redstone.changed", args)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
@ -17,7 +17,11 @@ import scala.collection.mutable
|
|||||||
trait Hub extends traits.Environment with SidedEnvironment with Tickable {
|
trait Hub extends traits.Environment with SidedEnvironment with Tickable {
|
||||||
override def node: Node = null
|
override def node: Node = null
|
||||||
|
|
||||||
override protected def isConnected = plugs.exists(plug => plug.node.address != null && plug.node.network != null)
|
override protected def isConnected = plugs.exists(plug =>
|
||||||
|
plug != null &&
|
||||||
|
plug.node != null &&
|
||||||
|
plug.node.address != null &&
|
||||||
|
plug.node.network != null)
|
||||||
|
|
||||||
protected val plugs = EnumFacing.values.map(side => createPlug(side))
|
protected val plugs = EnumFacing.values.map(side => createPlug(side))
|
||||||
|
|
||||||
@ -127,7 +131,8 @@ trait Hub extends traits.Environment with SidedEnvironment with Tickable {
|
|||||||
if (isServer) {
|
if (isServer) {
|
||||||
nbt.setNewTagList(PlugsTag, plugs.map(plug => {
|
nbt.setNewTagList(PlugsTag, plugs.map(plug => {
|
||||||
val plugNbt = new NBTTagCompound()
|
val plugNbt = new NBTTagCompound()
|
||||||
plug.node.save(plugNbt)
|
if (plug.node != null)
|
||||||
|
plug.node.save(plugNbt)
|
||||||
plugNbt
|
plugNbt
|
||||||
}))
|
}))
|
||||||
nbt.setNewTagList(QueueTag, queue.map {
|
nbt.setNewTagList(QueueTag, queue.map {
|
||||||
|
@ -9,6 +9,8 @@ import net.minecraft.util.EnumFacing
|
|||||||
import net.minecraftforge.fml.relauncher.Side
|
import net.minecraftforge.fml.relauncher.Side
|
||||||
import net.minecraftforge.fml.relauncher.SideOnly
|
import net.minecraftforge.fml.relauncher.SideOnly
|
||||||
|
|
||||||
|
case class RedstoneChangedEventArgs (side: EnumFacing, oldValue: Int, newValue: Int, color: Int = -1)
|
||||||
|
|
||||||
trait RedstoneAware extends RotationAware {
|
trait RedstoneAware extends RotationAware {
|
||||||
protected[tileentity] val _input: Array[Int] = Array.fill(6)(-1)
|
protected[tileentity] val _input: Array[Int] = Array.fill(6)(-1)
|
||||||
|
|
||||||
@ -39,7 +41,7 @@ trait RedstoneAware extends RotationAware {
|
|||||||
val oldInput = _input(side.ordinal())
|
val oldInput = _input(side.ordinal())
|
||||||
_input(side.ordinal()) = newInput
|
_input(side.ordinal()) = newInput
|
||||||
if (oldInput >= 0 && newInput != oldInput) {
|
if (oldInput >= 0 && newInput != oldInput) {
|
||||||
onRedstoneInputChanged(side, oldInput, newInput)
|
onRedstoneInputChanged(RedstoneChangedEventArgs(side, oldInput, newInput))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,7 +119,7 @@ trait RedstoneAware extends RotationAware {
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
protected def onRedstoneInputChanged(side: EnumFacing, oldMaxValue: Int, newMaxValue: Int) {}
|
protected def onRedstoneInputChanged(args: RedstoneChangedEventArgs) {}
|
||||||
|
|
||||||
protected def onRedstoneOutputEnabledChanged() {
|
protected def onRedstoneOutputEnabledChanged() {
|
||||||
if (getWorld != null) {
|
if (getWorld != null) {
|
||||||
|
@ -155,6 +155,17 @@ trait Agent extends traits.WorldControl with traits.InventoryControl with traits
|
|||||||
reason = reason.orElse(Option(what))
|
reason = reason.orElse(Option(what))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// all side attempts failed - but there could be a partial block that is hard to "see"
|
||||||
|
val (hasBlock, _) = blockContent(facing)
|
||||||
|
if (hasBlock) {
|
||||||
|
val blockPos = position.offset(facing)
|
||||||
|
val player = rotatedPlayer(facing, facing)
|
||||||
|
player.setSneaking(sneaky)
|
||||||
|
val (ok, why) = click(player, blockPos.toBlockPos, facing)
|
||||||
|
player.setSneaking(false)
|
||||||
|
return result(ok, why)
|
||||||
|
}
|
||||||
|
|
||||||
result(false, reason.orNull)
|
result(false, reason.orNull)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,9 @@ import li.cil.oc.api.network.Message
|
|||||||
import li.cil.oc.api.network.Visibility
|
import li.cil.oc.api.network.Visibility
|
||||||
import li.cil.oc.api.prefab
|
import li.cil.oc.api.prefab
|
||||||
import li.cil.oc.api.prefab.AbstractManagedEnvironment
|
import li.cil.oc.api.prefab.AbstractManagedEnvironment
|
||||||
|
import li.cil.oc.common.tileentity.{Robot => EntityRobot, Microcontroller}
|
||||||
|
import li.cil.oc.common.entity.{Drone => EntityDrone}
|
||||||
|
import li.cil.oc.common.item.TabletWrapper
|
||||||
import li.cil.oc.util.BlockPosition
|
import li.cil.oc.util.BlockPosition
|
||||||
import li.cil.oc.util.DatabaseAccess
|
import li.cil.oc.util.DatabaseAccess
|
||||||
import li.cil.oc.util.ExtendedArguments._
|
import li.cil.oc.util.ExtendedArguments._
|
||||||
@ -34,7 +37,7 @@ import scala.collection.convert.WrapAsJava._
|
|||||||
import scala.collection.convert.WrapAsScala._
|
import scala.collection.convert.WrapAsScala._
|
||||||
import scala.language.existentials
|
import scala.language.existentials
|
||||||
|
|
||||||
class Geolyzer(val host: EnvironmentHost) extends AbstractManagedEnvironment with DeviceInfo {
|
class Geolyzer(val host: EnvironmentHost) extends AbstractManagedEnvironment with traits.WorldControl with DeviceInfo {
|
||||||
override val node = api.Network.newNode(this, Visibility.Network).
|
override val node = api.Network.newNode(this, Visibility.Network).
|
||||||
withComponent("geolyzer").
|
withComponent("geolyzer").
|
||||||
withConnector().
|
withConnector().
|
||||||
@ -52,6 +55,45 @@ class Geolyzer(val host: EnvironmentHost) extends AbstractManagedEnvironment wit
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
|
override protected def checkSideForAction(args: Arguments, n: Int): EnumFacing = {
|
||||||
|
val side = args.checkSideAny(n)
|
||||||
|
val is_uc = host.isInstanceOf[Microcontroller]
|
||||||
|
host match {
|
||||||
|
case robot: EntityRobot => robot.proxy.toGlobal(side)
|
||||||
|
case drone: EntityDrone => drone.toGlobal(side)
|
||||||
|
case uc: Microcontroller => uc.toLocal(side) // not really sure what it is reversed for microcontrollers
|
||||||
|
case tablet: TabletWrapper => tablet.toGlobal(side)
|
||||||
|
case _ => side
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override def position: BlockPosition = host match {
|
||||||
|
case robot: EntityRobot => robot.proxy.position
|
||||||
|
case drone: EntityDrone => BlockPosition(drone.getPosition, drone.getEntityWorld)
|
||||||
|
case uc: Microcontroller => uc.position
|
||||||
|
case tablet: TabletWrapper => BlockPosition(tablet.xPosition, tablet.yPosition, tablet.zPosition, tablet.world)
|
||||||
|
case _ => BlockPosition(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def canSeeSky: Boolean = {
|
||||||
|
val blockPos = position.offset(EnumFacing.UP)
|
||||||
|
!host.world.provider.isNether && host.world.canBlockSeeSky(blockPos.toBlockPos)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Callback(doc = """function():boolean -- Returns whether there is a clear line of sight to the sky directly above.""")
|
||||||
|
def canSeeSky(computer: Context, args: Arguments): Array[AnyRef] = {
|
||||||
|
result(canSeeSky)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Callback(doc = """function():boolean -- Return whether the sun is currently visible directly above.""")
|
||||||
|
def isSunVisible(computer: Context, args: Arguments): Array[AnyRef] = {
|
||||||
|
val blockPos = BlockPosition(host).offset(EnumFacing.UP)
|
||||||
|
result(
|
||||||
|
host.world.isDaytime &&
|
||||||
|
canSeeSky &&
|
||||||
|
!host.world.getBiome(blockPos.toBlockPos).canRain || (!host.world.isRaining && !host.world.isThundering))
|
||||||
|
}
|
||||||
|
|
||||||
@Callback(doc = """function(x:number, z:number[, y:number, w:number, d:number, h:number][, ignoreReplaceable:boolean|options:table]):table -- Analyzes the density of the column at the specified relative coordinates.""")
|
@Callback(doc = """function(x:number, z:number[, y:number, w:number, d:number, h:number][, ignoreReplaceable:boolean|options:table]):table -- Analyzes the density of the column at the specified relative coordinates.""")
|
||||||
def scan(computer: Context, args: Arguments): Array[AnyRef] = {
|
def scan(computer: Context, args: Arguments): Array[AnyRef] = {
|
||||||
val (minX, minY, minZ, maxX, maxY, maxZ, optIndex) = getScanArgs(args)
|
val (minX, minY, minZ, maxX, maxY, maxZ, optIndex) = getScanArgs(args)
|
||||||
|
@ -7,8 +7,11 @@ import li.cil.oc.api.machine.Context
|
|||||||
import li.cil.oc.api.network.Visibility
|
import li.cil.oc.api.network.Visibility
|
||||||
import li.cil.oc.api.prefab
|
import li.cil.oc.api.prefab
|
||||||
import li.cil.oc.api.prefab.AbstractManagedEnvironment
|
import li.cil.oc.api.prefab.AbstractManagedEnvironment
|
||||||
|
import li.cil.oc.common.tileentity.traits.RedstoneChangedEventArgs
|
||||||
import net.minecraft.nbt.NBTTagCompound
|
import net.minecraft.nbt.NBTTagCompound
|
||||||
|
|
||||||
|
import scala.collection.mutable.ArrayBuffer
|
||||||
|
|
||||||
trait RedstoneSignaller extends AbstractManagedEnvironment {
|
trait RedstoneSignaller extends AbstractManagedEnvironment {
|
||||||
override val node = Network.newNode(this, Visibility.Network).
|
override val node = Network.newNode(this, Visibility.Network).
|
||||||
withComponent("redstone", Visibility.Neighbors).
|
withComponent("redstone", Visibility.Neighbors).
|
||||||
@ -32,9 +35,13 @@ trait RedstoneSignaller extends AbstractManagedEnvironment {
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
def onRedstoneChanged(side: AnyRef, oldMaxValue: Int, newMaxValue: Int): Unit = {
|
def onRedstoneChanged(args: RedstoneChangedEventArgs): Unit = {
|
||||||
node.sendToReachable("computer.signal", "redstone_changed", side, Int.box(oldMaxValue), Int.box(newMaxValue))
|
val side: AnyRef = if (args.side == null) "wireless" else Int.box(args.side.ordinal)
|
||||||
if (oldMaxValue < wakeThreshold && newMaxValue >= wakeThreshold) {
|
val flatArgs = ArrayBuffer[Object]("redstone_changed", side, Int.box(args.oldValue), Int.box(args.newValue))
|
||||||
|
if (args.color >= 0)
|
||||||
|
flatArgs += Int.box(args.color)
|
||||||
|
node.sendToReachable("computer.signal", flatArgs: _*)
|
||||||
|
if (args.oldValue < wakeThreshold && args.newValue >= wakeThreshold) {
|
||||||
if (wakeNeighborsOnly)
|
if (wakeNeighborsOnly)
|
||||||
node.sendToNeighbors("computer.start")
|
node.sendToNeighbors("computer.start")
|
||||||
else
|
else
|
||||||
|
@ -12,7 +12,7 @@ import li.cil.oc.api.machine.Arguments
|
|||||||
import li.cil.oc.api.machine.Callback
|
import li.cil.oc.api.machine.Callback
|
||||||
import li.cil.oc.api.machine.Context
|
import li.cil.oc.api.machine.Context
|
||||||
import li.cil.oc.api.network._
|
import li.cil.oc.api.network._
|
||||||
import li.cil.oc.common.tileentity.traits.RedstoneAware
|
import li.cil.oc.common.tileentity.traits.{RedstoneAware, RedstoneChangedEventArgs}
|
||||||
import li.cil.oc.util.BlockPosition
|
import li.cil.oc.util.BlockPosition
|
||||||
import li.cil.oc.util.ExtendedBlock._
|
import li.cil.oc.util.ExtendedBlock._
|
||||||
import li.cil.oc.util.ExtendedWorld._
|
import li.cil.oc.util.ExtendedWorld._
|
||||||
@ -79,8 +79,8 @@ trait RedstoneVanilla extends RedstoneSignaller with DeviceInfo {
|
|||||||
override def onMessage(message: Message): Unit = {
|
override def onMessage(message: Message): Unit = {
|
||||||
super.onMessage(message)
|
super.onMessage(message)
|
||||||
if (message.name == "redstone.changed") message.data match {
|
if (message.name == "redstone.changed") message.data match {
|
||||||
case Array(side: EnumFacing, oldMaxValue: Number, newMaxValue: Number) =>
|
case Array(args: RedstoneChangedEventArgs) =>
|
||||||
onRedstoneChanged(Int.box(side.ordinal()), oldMaxValue.intValue(), newMaxValue.intValue())
|
onRedstoneChanged(args)
|
||||||
case _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
163
src/main/scala/li/cil/oc/server/component/RedstoneWireless.scala
Normal file
163
src/main/scala/li/cil/oc/server/component/RedstoneWireless.scala
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
package li.cil.oc.server.component
|
||||||
|
|
||||||
|
import codechicken.lib.vec.Vector3
|
||||||
|
import codechicken.wirelessredstone.api.WirelessReceivingDevice
|
||||||
|
import codechicken.wirelessredstone.api.WirelessTransmittingDevice
|
||||||
|
import li.cil.oc.Constants
|
||||||
|
import li.cil.oc.api.driver.DeviceInfo.DeviceAttribute
|
||||||
|
import li.cil.oc.api.driver.DeviceInfo.DeviceClass
|
||||||
|
import li.cil.oc.Settings
|
||||||
|
import li.cil.oc.api.driver.DeviceInfo
|
||||||
|
import li.cil.oc.api.network.EnvironmentHost
|
||||||
|
import li.cil.oc.api.machine.Arguments
|
||||||
|
import li.cil.oc.api.machine.Callback
|
||||||
|
import li.cil.oc.api.machine.Context
|
||||||
|
import li.cil.oc.api.network._
|
||||||
|
import li.cil.oc.common.EventHandler
|
||||||
|
import li.cil.oc.common.tileentity.traits.RedstoneChangedEventArgs
|
||||||
|
import li.cil.oc.integration.Mods
|
||||||
|
import li.cil.oc.integration.util
|
||||||
|
import net.minecraft.nbt.NBTTagCompound
|
||||||
|
import net.minecraftforge.fml.common.Optional
|
||||||
|
|
||||||
|
import scala.collection.convert.WrapAsJava._
|
||||||
|
|
||||||
|
@Optional.InterfaceList(Array(
|
||||||
|
new Optional.Interface(iface = "codechicken.wirelessredstone.api.WirelessReceivingDevice", modid = Mods.IDs.WirelessRedstoneCBE),
|
||||||
|
new Optional.Interface(iface = "codechicken.wirelessredstone.api.WirelessTransmittingDevice", modid = Mods.IDs.WirelessRedstoneCBE)
|
||||||
|
))
|
||||||
|
trait RedstoneWireless extends RedstoneSignaller with WirelessReceivingDevice with WirelessTransmittingDevice with DeviceInfo {
|
||||||
|
def redstone: EnvironmentHost
|
||||||
|
|
||||||
|
var wirelessFrequency = 0
|
||||||
|
|
||||||
|
var wirelessInput = false
|
||||||
|
|
||||||
|
var wirelessOutput = false
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
|
private final lazy val deviceInfo = Map(
|
||||||
|
DeviceAttribute.Class -> DeviceClass.Communication,
|
||||||
|
DeviceAttribute.Description -> "Wireless redstone controller",
|
||||||
|
DeviceAttribute.Vendor -> Constants.DeviceInfo.DefaultVendor,
|
||||||
|
DeviceAttribute.Product -> "Rw400-M",
|
||||||
|
DeviceAttribute.Capacity -> "1",
|
||||||
|
DeviceAttribute.Width -> "1"
|
||||||
|
)
|
||||||
|
|
||||||
|
override def getDeviceInfo: java.util.Map[String, String] = deviceInfo
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
|
@Callback(doc = """function():number -- Get the wireless redstone input.""")
|
||||||
|
def getWirelessInput(context: Context, args: Arguments): Array[AnyRef] = {
|
||||||
|
wirelessInput = util.WirelessRedstone.getInput(this)
|
||||||
|
result(wirelessInput)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Callback(direct = true, doc = """function():boolean -- Get the wireless redstone output.""")
|
||||||
|
def getWirelessOutput(context: Context, args: Arguments): Array[AnyRef] = result(wirelessOutput)
|
||||||
|
|
||||||
|
@Callback(doc = """function(value:boolean):boolean -- Set the wireless redstone output.""")
|
||||||
|
def setWirelessOutput(context: Context, args: Arguments): Array[AnyRef] = {
|
||||||
|
val oldValue = wirelessOutput
|
||||||
|
val newValue = args.checkBoolean(0)
|
||||||
|
|
||||||
|
if (oldValue != newValue) {
|
||||||
|
wirelessOutput = newValue
|
||||||
|
|
||||||
|
util.WirelessRedstone.updateOutput(this)
|
||||||
|
|
||||||
|
if (Settings.get.redstoneDelay > 0)
|
||||||
|
context.pause(Settings.get.redstoneDelay)
|
||||||
|
}
|
||||||
|
|
||||||
|
result(oldValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Callback(direct = true, doc = """function():number -- Get the currently set wireless redstone frequency.""")
|
||||||
|
def getWirelessFrequency(context: Context, args: Arguments): Array[AnyRef] = result(wirelessFrequency)
|
||||||
|
|
||||||
|
@Callback(doc = """function(frequency:number):number -- Set the wireless redstone frequency to use.""")
|
||||||
|
def setWirelessFrequency(context: Context, args: Arguments): Array[AnyRef] = {
|
||||||
|
val oldValue = wirelessFrequency
|
||||||
|
val newValue = args.checkInteger(0)
|
||||||
|
|
||||||
|
if (oldValue != newValue) {
|
||||||
|
util.WirelessRedstone.removeReceiver(this)
|
||||||
|
util.WirelessRedstone.removeTransmitter(this)
|
||||||
|
|
||||||
|
wirelessFrequency = newValue
|
||||||
|
wirelessInput = false
|
||||||
|
wirelessOutput = false
|
||||||
|
|
||||||
|
util.WirelessRedstone.addReceiver(this)
|
||||||
|
|
||||||
|
context.pause(0.5)
|
||||||
|
}
|
||||||
|
|
||||||
|
result(oldValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
|
@Optional.Method(modid = Mods.IDs.WirelessRedstoneCBE)
|
||||||
|
override def updateDevice(frequency: Int, on: Boolean) {
|
||||||
|
if (frequency == wirelessFrequency && on != wirelessInput) {
|
||||||
|
wirelessInput = on
|
||||||
|
onRedstoneChanged(RedstoneChangedEventArgs(null, if (on) 0 else 1, if (on) 1 else 0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Optional.Method(modid = Mods.IDs.WirelessRedstoneCBE)
|
||||||
|
override def getTransmitPos = new Vector3(redstone.xPosition, redstone.yPosition, redstone.zPosition)
|
||||||
|
|
||||||
|
@Optional.Method(modid = Mods.IDs.WirelessRedstoneCBE)
|
||||||
|
override def getDimension = redstone.world.provider.getDimension
|
||||||
|
|
||||||
|
@Optional.Method(modid = Mods.IDs.WirelessRedstoneCBE)
|
||||||
|
override def getFreq = wirelessFrequency
|
||||||
|
|
||||||
|
@Optional.Method(modid = Mods.IDs.WirelessRedstoneCBE)
|
||||||
|
override def getAttachedEntity = null
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
|
override def onConnect(node: Node) {
|
||||||
|
super.onConnect(node)
|
||||||
|
if (node == this.node) {
|
||||||
|
EventHandler.scheduleWirelessRedstone(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override def onDisconnect(node: Node) {
|
||||||
|
super.onDisconnect(node)
|
||||||
|
if (node == this.node) {
|
||||||
|
util.WirelessRedstone.removeReceiver(this)
|
||||||
|
util.WirelessRedstone.removeTransmitter(this)
|
||||||
|
wirelessOutput = false
|
||||||
|
wirelessFrequency = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
|
private final val WirelessFrequencyTag = "wirelessFrequency"
|
||||||
|
private final val WirelessInputTag = "wirelessInput"
|
||||||
|
private final val WirelessOutputTag = "wirelessOutput"
|
||||||
|
|
||||||
|
override def load(nbt: NBTTagCompound) {
|
||||||
|
super.load(nbt)
|
||||||
|
wirelessFrequency = nbt.getInteger(WirelessFrequencyTag)
|
||||||
|
wirelessInput = nbt.getBoolean(WirelessInputTag)
|
||||||
|
wirelessOutput = nbt.getBoolean(WirelessOutputTag)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def save(nbt: NBTTagCompound) {
|
||||||
|
super.save(nbt)
|
||||||
|
nbt.setInteger(WirelessFrequencyTag, wirelessFrequency)
|
||||||
|
nbt.setBoolean(WirelessInputTag, wirelessInput)
|
||||||
|
nbt.setBoolean(WirelessOutputTag, wirelessOutput)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user