diff --git a/mnt/backend-chests.lua b/mnt/backend-chests.lua deleted file mode 100644 index bedbbfa..0000000 --- a/mnt/backend-chests.lua +++ /dev/null @@ -1,149 +0,0 @@ --- Chest backend --- Currently just the one for Dragon. Will not actually work yet. - -local util = require "util" -local conf = util.conf - -rednet.open(conf.modem) - --- Find all chests or shulker boxes -local inventories = {} -for _, n in pairs(peripheral.getNames()) do - local p = peripheral.wrap(n) - if - string.find(n, "chest") or - string.find(n, "shulker") then - inventories[n] = p - end -end - -local nameCache = {} - --- Gets the display name of the given item (in the given chest peripheral & slot) --- If its name is not cached, cache it. --- If it is, just return the cached name -function cache(item, chest, slot) - local idx = item.name .. ":" .. item.damage - - if nameCache[idx] then - return nameCache[idx] - else - local n = chest.getItemMeta(slot).displayName - nameCache[idx] = n - return n - end -end - -local index = {} -function updateIndexFor(name) - local inv = inventories[name] - local data = inv.list() - - for slot, item in pairs(data) do - data[slot].displayName = cache(item, inv, slot) - end - - index[name] = data -end - -function updateIndex() - for n in pairs(inventories) do - updateIndexFor(n) - sleep() - end - print "Indexing complete." -end - --- Finds all items matching a certain predicate -function find(predicate) - for name, items in pairs(index) do - for slot, item in pairs(items) do - if predicate(item) then - return name, slot, item - end - end - end -end - --- Finds space in the chest system -function findSpace() - for name, items in pairs(index) do - if #items < inventories[name].size() then - return name - end - end -end - -function search(msg) - return find(function(item) - return - (not msg.meta or item.damage == msg.meta) and - (not msg.name or item.name == msg.name) and - (not msg.dname or string.find(item.displayName:lower(), msg.dname:lower())) - end) -end - -function processRequest(msg) - print(textutils.serialise(msg)) - - -- Extract an item. If meta and name are supplied, each supplied value must match exactly. - -- Applies a fuzzy search to display names - -- Extracted items are either deposited in buffer or directly in target inventory. - if msg.cmd == "extract" then - local inv, slot, item = search(msg) - - local qty = msg.qty or 64 - - updateIndexFor(inv) - - local moved = peripheral.call(conf.bufferOutInternal, "pullItems", inv, slot, qty, 1) - - if msg.destInv then - moved = peripheral.call(conf.bufferOutExternal, "pushItems", msg.destInv, 1, 64, msg.destSlot) - end - - return {moved, item} - -- Pulls items from an external inventory into storage. - elseif msg.cmd == "insert" then - if msg.fromInv and msg.fromSlot then - peripheral.call(conf.bufferInExternal, "pullItems", msg.fromInv, msg.fromSlot, msg.qty or 64, 1) - end - - local toInv = findSpace() - if not toInv then return "ERROR" end - - peripheral.call(conf.bufferInInternal, "pushItems", toInv, 1) - - updateIndexFor(toInv) -- I don't know a good way to figure out where exactly the items went - - return "OK" - -- Just return the external network names of the buffers - elseif msg.cmd == "buffers" then - return { conf.bufferInExternal, conf.bufferOutExternal } - -- Reindexes system - elseif msg.cmd == "reindex" then - updateIndex() - return "OK" - -- Returns entire index - elseif msg.cmd == "list" then - return util.collate(index) - -- Looks up supplied name in the cache. - elseif msg.cmd == "name" then - msg.meta = msg.meta or 0 - return msg.name and msg.meta and nameCache[msg.name .. ":" .. msg.meta] - end -end - -function processRequests() - while true do - util.processMessage(function(msg) - local ok, r = pcall(processRequest, msg) - if not ok then r = "ERROR" end - - return true, r - end) - end -end - -updateIndex() -processRequests() \ No newline at end of file diff --git a/mnt/lib.lua b/mnt/lib.lua deleted file mode 100644 index d597594..0000000 --- a/mnt/lib.lua +++ /dev/null @@ -1,139 +0,0 @@ ---[[ -Wyvern utility/API library -Contains: -error handling (a set of usable errors and human-readable printing for them), -networking (a simple node-based system on top of rednet) -configuration (basically just loading serialized tables from a file) -]] - -local d = require "luadash" - --- ERRORS - -local errors = { - INTERNAL = 0, -- Internal error data can be in any format or not exist - INVALID = 1, -- Invalid message errors don't require data at all - NOPATTERN = 2, -- No pattern errors should contain a human-readable pattern name in their data - NOITEMS = 3, -- No item errors should either provide a table of { type = "human-readable name if available or internal ID if not", quantity = number of items missing } or a human-readable string - NORESPONSE = 4, -- No response errors (should only be produced by query_ functions may contain a description of which node the error is caused by in their data - NOMATCHINGNODE = 5, -- No matching node errors (should only be prodcuced by query_ functions) may contain a description of which type of node cannot be found. - make = function(e, d) - return { type = "error", error = e, data = d } - end -} - --- Converts an error into human-readable format -errors.format = function(e) - if not (e.type and e.type == "error" and e.data and e.error) then return "Not actually an error." end - if e.error == errors.INTERNAL then - return "Internal error - provided info: " .. textutils.serialise(e.data) .. "." - elseif e.error == errors.INVALID then - return "Request invalid." - elseif e.errors == errors.NOPATTERN then - return "Missing pattern " .. textutils.serialise(e.data) .. "." - elseif e.errors == errors.NOITEMS then - local thing_missing = "???" - if type(e.data) == "table" and e.data.type and e.data.quantity then - thing_missing = tostring(e.data.quantity) .. " " .. e.data.type - elseif type(e.data) == "string" then - thing_missing = e.data - end - - return "Missing " .. thing_missing .. " to fulfil request." - elseif e.errors == errors.NORESPONSE then - local text = "No response" - if e.data then text = text .. " from " .. textutils.serialise(e.data) end - return text .. "." - elseif e.errors == errors.NOMATCHINGNODE then - if e.data then - return "No " .. textutils.serialise(e.data) .. " node found." - else - return "No node of desired type found." - end - else - return "Error is invalid. Someone broke it." - end -end - --- NETWORKING - -local protocol = "wyvern" - --- Runs a Wyvern node server. --- First argument is a function to be run for requests. It will be provided the request data and must return the value to respond with. --- If it errors, an internal error will be returned. --- Second argument is the type of node to host as. Other nodes may attempt to use this to discover other local-network nodes. -local function serve(fn, nodeType) - rednet.host(protocol .. "/" .. nodeType, nodeType) - - while true do - local sender, message = rednet.receive(protocol) - - -- As a default response, send an "invalid request" error - local response = errors.make(errors.INVALID) - - -- If the message actually is a compliant Wyvern request (is a table, containing a message ID, request, and a type saying "request") then run - -- the provided server function, and package successful results into a response type - if type(message) == "table" and message.type and message.type == "request" and message.request then - local ok, result = pcall(fn, request) - if not ok then response = errors.make(errors.INTERNAL, result) end - else response = { type = "response", response = result } - end - - rednet.send(sender, response, protocol) - end -end - --- Attempts to send "request" to "ID", with the maximum number of allowable tries being "tries" -local function query_by_ID(ID, request, tries) - local max_tries = tries or 3 - local request_object = { type = "request", request = request } - local result, tries - - repeat - rednet.send(id, request_object, protocol) - _, result = rednet.receive(protocol, 1) - sleep(1) - until result ~= nil or tries >= max_tries - - if result == nil then result = errors.make(errors.NORESPONSE, ID) end - - return result -end - -local function query_by_type(type, request, tries) - local ID = rednet.lookup(protocol .. "/" .. type) - if not ID then return errors.make(errors.NOMATCHINGNODE, type) end - return query_by_ID(ID, request, tries) -end - --- GENERAL STUFF - --- Loads a config file (in serialized-table format) from "filename" or wyvern_config.tbl --- "required_data" is a list of keys which must be in the config file's data --- "defaults" is a map of keys and default values for them, which will be used if there is no matching key in the data -local function load_config(required_data, defaults, filename) - local filename = filename or "wyvern_config.tbl" - local f = fs.open(filename, "r") - local data = textutils.unserialise(f.readAll()) - f.close() - - for k, required_key in pairs(required_data) do - if not data[required_key] then - if defaults[required_key] then data[required_key] = defaults[required_key] - else error({"Missing config key!", required_key, data}) end - end - end - - return data -end - -local function find_peripherals(predicate) - return d.map(d.filter(d.map(peripheral.getNames(), function(name) return { name, functions = peripheral.wrap(name) } end), predicate), function(obj) return obj.functions) -end - -local function init(config) - d.map(find_peripherals(function(p) return p.functions.open and p.functions.close end) -end - -return { errors, serve, query_by_ID, query_by_type, load_config, find_peripherals, init } \ No newline at end of file diff --git a/mnt/luadash.lua b/mnt/luadash.lua deleted file mode 100644 index 3e6ff0d..0000000 --- a/mnt/luadash.lua +++ /dev/null @@ -1,358 +0,0 @@ --- luadash, from https://github.com/tmpim/luadash, not actually mine --- It's bundled with the Wyvern source code here because laziness --- I also added the Levenstein (probably spelt that wrong) distance implementation to it, as it's useful. - -local _mt, _ = {}, {} -setmetatable(_, _mt) - -local function skip1(f) - return function(x, _, ...) - return f(x, ...) - end -end - -function _.expect(n, arg, t, v) - if t == 'value' then - if v == nil then - return error(('%s: bad argument #%d (got nil)'):format(n, arg)) - end - elseif type(v) ~= t then - return error(('%s: bad argument #%d (expected %s, got %s)'):format(n, arg, t, type(v))) - end -end - -function _.partial(f, ...) - _.expect('partial', 1, 'function', f) - local args = table.pack(...) - return function(...) - local args2, actual = table.pack(...), { } - for i = 1, args.n do - actual[i] = args[i] - end - for i = 1, args2.n do - actual[args.n + i] = args2[i] - end - return f(unpack(actual, 1, args.n + args2.n)) - end -end - -function _.map_with_key(tab, f) - _.expect('map_with_key', 1, 'table', tab) - _.expect('map_with_key', 2, 'function', f) - local out = {} - for k, v in pairs(tab) do - local k, v = f(k, v) - out[k] = v - end - return out -end - -function _.reduce_with_index(tab, f, z) - _.expect('reduce_with_index', 1, 'table', tab) - _.expect('reduce_with_index', 2, 'function', f) - _.expect('reduce_with_index', 3, 'value', z) - local out = z - for i = 1, #tab do - out = f(out, i, tab[i]) - end - return out -end - -function _.reduce(tab, f, z) - return _.reduce_with_index(tab, skip1(f), z) -end - -function _.apply(f, t) - _.expect('apply', 1, 'function', f) - _.expect('apply', 2, 'table', t) - return f(unpack(t, 1, #t)) -end - -function _.map(t1, f, ...) - _.expect('map', 1, 'table', t1) - _.expect('map', 2, 'function', f) - return _.flat_map(t1, function(...) return { (f(...)) } end, ...) -end - -function _.zip(...) - local args = table.pack(...) - for i = 1, args.n do - _.expect('zip', 1, 'table', args[i]) - end - return _.map(function(...) return {...} end, ...) -end - -function _.push(t, ...) - _.expect('push', 1, 'table', t) - local args = table.pack(...) - for i = 1, args.n do - table.insert(t, args[i]) - end - return t -end - -function _.intersperse(t, x) - _.expect('intersperse', 1, 'table', t) - local out = {} - for i = 1, #t, 1 do - _.push(out, t[i], x) - end - return out -end - -function _.flatten(t) - _.expect('flatten', 1, 'table', t) - local out, li = {}, 1 - for i = 1, #t do - if type(t[i]) == 'table' then - for j = 1, #t[i] do - out[li] = t[i][j] - li = li + 1 - end - else - out[li] = t[i] - li = li + 1 - end - end - return out -end - -function _.flat_map(t1, f, ...) - _.expect('flat_map', 1, 'table', t1) - _.expect('flat_map', 2, 'function', f) - local args, n = table.pack(t1, ...), 0 - for i = 1, args.n do - _.expect('flat_map', 1 + i, 'table', args[i]) - n = math.max(n, #args[i]) - end - local out, li = {}, 0 - for i = 1, n do - local these = {} - for j = 1, args.n do - these[j] = args[j][i] - end - local r = _.apply(f, these) - if type(r) == 'table' then - for i = 1, #r do - out[li + i] = r[i] - end - li = li + #r - else - out[li + 1] = r - li = li + 1 - end - end - return out -end - -function _.filter(t, p) - _.expect('filter', 1, 'table', t) - _.expect('filter', 2, 'function', p) - local out, li = {}, 1 - for i = 1, #t do - if p(t[i]) then - out[li] = t[i] - li = li + 1 - end - end - return out -end - -function _.id(v) - _.expect('id', 1, 'value', v) - return v -end - -function _.sort_by(t, f) - _.expect('sort_by', 1, 'table', t) - _.expect('sort_by', 2, 'function', f) - local nt = _.map(t, _.id) - - table.sort(nt, function(a, b) return f(a) < f(b) end) - return nt -end - -function _.sort(t) - _.expect('sort', 1, 'table', t) - - return _.sort_by(t, _.id) -end - -function _.sample_size(t, n) - _.expect('sample_size', 1, 'table', t) - _.expect('sample_size', 2, 'number', n) - - if #t <= n then - return t - end - - local src = _.keys(t) - local out = {} - for i = 1, n do - local k = _.sample(src) - out[i] = t[k] - - src[k] = src[#src] - src[#src] = nil - end - return out -end - -function _.sample(t) - _.expect('sample', 1, 'table', t) - return t[math.random(1, #t)] -end - -function _.head(t) - _.expect('head', 1, 'table', t) - return x[1] -end - -function _.tail(t) - _.expect('tail', 1, 'table', t) - local out = {} - for i = 2, #t do - out[i - 1] = t[i] - end - return out -end - -function _.every(t, p) - _.expect('every', 1, 'table', t) - _.expect('every', 1, 'function', p) - for i = 1, #t do - if not p(t[i]) then - return false - end - end - return true -end - -function _.some(t, p) - _.expect('some', 1, 'table', t) - _.expect('some', 1, 'function', p) - for i = 1, #t do - if p(t[i]) then - return true - end - end - return false -end - -function _.initial(t) - _.expect('initial', 1, 'table', t) - local out = {} - for i = 1, #t - 1 do - out[i] = t[i] - end - return out -end - -function _.last(t) - _.expect('last', 1, 'table', t) - return t[#t] -end - -function _.nth(t, i) - _.expect('nth', 1, 'table', t) - _.expect('nth', 2, 'value', i) - return t[i] -end - -function _.keys(t) - _.expect('keys', 1, 'table', t) - local out, i = {}, 1 - for k, v in pairs(t) do - out[i] = k - i = i + 1 - end - return out -end - -function _.values(t) - _.expect('values', 1, 'table', t) - local out, i = {}, 1 - for k, v in pairs(t) do - out[i] = v - i = i + 1 - end - return out -end - -function _mt.__call(_, x) - local function wrap(f) - return function(...) - return _(f(...)) - end - end - if type(x) == 'table' then - return setmetatable(x, - { __index = function(t, k) - return wrap(_[k]) - end }) - else - return x - end -end - -function _.distance(str1, str2) - local v0 = {} - local v1 = {} - - for i = 0, #str2 do - v0[i] = i - end - - for i = 0, #str1 - 1 do - v1[0] = i + 1 - - for j = 0, #str2 - 1 do - local delCost = v0[j + 1] + 1 - local insertCost = v1[j] + 1 - local subCost - - if str1:sub(i + 1, i + 1) == str2:sub(j + 1, j + 1) then - subCost = v0[j] - else - subCost = v0[j] + 1 - end - - v1[j + 1] = math.min(delCost, insertCost, subCost) - end - - local t = v0 - v0 = v1 - v1 = t - end - - return v0[#str2] -end - -_.ops = { - plus = function(a, b) return a + b end, - minus = function(a, b) return a - b end, - times = function(a, b) return a * b end, - over = function(a, b) return a / b end, - power = function(a, b) return a ^ b end, - modulo = function(a, b) return a % b end, - remainder = function(a, b) return a % b end, - rem = function(a, b) return a % b end, - mod = function(a, b) return a % b end, - conj = function(a, b) return a and b end, - disj = function(a, b) return a or b end, - equals = function(a, b) return a == b end, - divisible_by = function(a, b) - return b % a == 0 - end, - ['>'] = function(a, b) return a > b end, - ['>='] = function(a, b) return a >= b end, - ['<'] = function(a, b) return a < b end, - ['<='] = function(a, b) return a <= b end, -} - -function string.starts_with(self, s) - _.expect('starts_with', 1, 'string', s) - return self:find('^' .. s) ~= nil -end - -return _