SHOW:
|
|
- or go back to the newest paste.
| 1 | - | local ec = peripheral.find "ender_chest" |
| 1 | + | package.path = "/?;/?.lua;" .. package.path |
| 2 | - | local ecinv = peripheral.find "minecraft:ender chest" |
| 2 | + | local chest = settings.get "mail.chest" or error "please set mail.chest to the network name of the (non-ender) chest to use" |
| 3 | local ender_chest = peripheral.find "ender_chest" or error "ender chest connected through adapter + relay required" | |
| 4 | - | local f = fs.open("escan.log", "w")
|
| 4 | + | local ender_chest_inv = peripheral.find "minecraft:ender chest" or error "ender chest directly connected required" |
| 5 | local modem = peripheral.find("modem", function(_, x) return x.isWireless() end) or error "wireless modem required"
| |
| 6 | - | local z = ... |
| 6 | + | local ok, ecnet = pcall(require, "ecnet") |
| 7 | - | if z then |
| 7 | + | if not ok then |
| 8 | - | ec.setFrequency(tonumber(z, 16)) |
| 8 | + | print "Downloading ECNet library (https://forums.computercraft.cc/index.php?topic=181.0)" |
| 9 | - | return |
| 9 | + | shell.run "wget https://gist.githubusercontent.com/migeyel/278f77628248ea991719f0376979b525/raw/ecnet.min.lua ecnet.lua" |
| 10 | end | |
| 11 | ecnet = require "ecnet" | |
| 12 | - | for i = 0, 0xFFF do |
| 12 | + | local label = os.getComputerLabel() or error "Please set a label to use as a device name" |
| 13 | - | ec.setFrequency(i) |
| 13 | + | print("Address is", ecnet.address)
|
| 14 | - | local count = 0 |
| 14 | + | local ecnet_modem = ecnet.wrap(modem) |
| 15 | - | for _, s in pairs(ecinv.list()) do |
| 15 | + | local maildata_path = "maildata" |
| 16 | - | count = count + s.count |
| 16 | + | |
| 17 | local acceptable_mailbox_name_pattern = "^[A-Za-z0-9_]+$" | |
| 18 | - | if count > 0 then |
| 18 | + | if not label:match(acceptable_mailbox_name_pattern) then error("label must match: " .. acceptable_mailbox_name_pattern) end
|
| 19 | - | local log = ("%s %s 0x%03x %d"):format(os.date "!%X", table.concat(ec.getFrequencyColors(), "/"), i, count)
|
| 19 | + | |
| 20 | - | print(log) |
| 20 | + | local function find_channel() |
| 21 | - | f.writeLine(log) |
| 21 | + | for i = 0, 10 do |
| 22 | local new = math.random(0, 0xFFF) | |
| 23 | - | if i % 256 == 255 then |
| 23 | + | ender_chest.setFrequency(new) |
| 24 | - | f.flush() |
| 24 | + | local count = 0 |
| 25 | for _, stack in pairs(ender_chest_inv.list()) do | |
| 26 | - | os.queueEvent "" |
| 26 | + | count = count + stack.count |
| 27 | - | os.pullEvent "" |
| 27 | + | end |
| 28 | if count == 0 then | |
| 29 | return new | |
| 30 | - | f.close() |
| 30 | + | end |
| 31 | end | |
| 32 | error "Available channel scan failed after 10 tries - has someone flooded ender chests with random stuff?" | |
| 33 | end | |
| 34 | ||
| 35 | local function writef(n, c) | |
| 36 | local f = fs.open(n, "w") | |
| 37 | f.write(c) | |
| 38 | f.close() | |
| 39 | end | |
| 40 | ||
| 41 | local function readf(n) | |
| 42 | local f = fs.open(n, "r") | |
| 43 | local out = f.readAll() | |
| 44 | f.close() | |
| 45 | return out | |
| 46 | end | |
| 47 | ||
| 48 | local data = {}
| |
| 49 | if fs.exists(maildata_path) then data = textutils.unserialise(readf(maildata_path)) end | |
| 50 | if type(data.paired) ~= "table" then data.paired = {} end
| |
| 51 | ||
| 52 | local function save_data() writef(maildata_path, textutils.serialise(data)) end | |
| 53 | ||
| 54 | local function split_at_spaces(s) | |
| 55 | local t = {}
| |
| 56 | for i in string.gmatch(s, "%S+") do | |
| 57 | table.insert(t, i) | |
| 58 | end | |
| 59 | return t | |
| 60 | end | |
| 61 | ||
| 62 | local function update_self() | |
| 63 | print "Downloading update." | |
| 64 | local h = http.get "https://pastebin.com/raw/86Kjhq32" | |
| 65 | local t = h.readAll() | |
| 66 | h.close() | |
| 67 | local fn, err = load(t, "@mail") | |
| 68 | if not fn then printError("Not updating: syntax error in new version:\n" .. err) return end
| |
| 69 | local f = fs.open("startup", "w")
| |
| 70 | f.write(t) | |
| 71 | f.close() | |
| 72 | os.reboot() | |
| 73 | end | |
| 74 | ||
| 75 | local function first_letter(s) | |
| 76 | return string.sub(s, 1, 1) | |
| 77 | end | |
| 78 | ||
| 79 | local function send_stack(slot, addr) | |
| 80 | local channel = find_channel() | |
| 81 | print("[OUT] Channel:", channel)
| |
| 82 | ecnet_modem.send(addr, { "stack_request", channel = channel })
| |
| 83 | local _, result = os.pullEvent "stack_request_response" | |
| 84 | if result == true then | |
| 85 | ender_chest_inv.pullItems(chest, slot) | |
| 86 | print("[OUT] Sent stack", slot)
| |
| 87 | local _, result, x = os.pullEvent "stack_result" | |
| 88 | if result == false then | |
| 89 | printError("[OUT] Destination error: " .. tostring(x))
| |
| 90 | for eslot in pairs(ender_chest_inv.list()) do | |
| 91 | ender_chest_inv.pushItems(chest, eslot) | |
| 92 | end | |
| 93 | end | |
| 94 | return result | |
| 95 | else return false end | |
| 96 | end | |
| 97 | ||
| 98 | local function get_name(address) | |
| 99 | for name, addr in pairs(data.paired) do | |
| 100 | if addr == address then return name end | |
| 101 | end | |
| 102 | return address | |
| 103 | end | |
| 104 | ||
| 105 | local last_pair_request = nil | |
| 106 | ||
| 107 | local CLI_commands = {
| |
| 108 | address = function() | |
| 109 | print(ecnet.address) | |
| 110 | end, | |
| 111 | update = update_self, | |
| 112 | pair = function(addr) | |
| 113 | local ok = ecnet_modem.connect(addr, 2) | |
| 114 | if not ok then error("Could not contact " .. addr) end
| |
| 115 | ecnet_modem.send(addr, { "pair", label = label })
| |
| 116 | end, | |
| 117 | accept_pair = function() | |
| 118 | if not last_pair_request then error "no pair request to accept" end | |
| 119 | ecnet_modem.send(last_pair_request.address, { "pair_accept", label = label })
| |
| 120 | data.paired[last_pair_request.label] = last_pair_request.address | |
| 121 | save_data() | |
| 122 | last_pair_request = nil | |
| 123 | end, | |
| 124 | reject_pair = function() | |
| 125 | if not last_pair_request then error "no pair request to reject" end | |
| 126 | ecnet_modem.send(last_pair_request.address, { "pair_reject", label = label })
| |
| 127 | last_pair_request = nil | |
| 128 | end, | |
| 129 | paired = function() | |
| 130 | print "Paired:" | |
| 131 | for label, addr in pairs(data.paired) do | |
| 132 | print(label, addr) | |
| 133 | end | |
| 134 | end, | |
| 135 | unpair = function(name) | |
| 136 | data.paired[name] = nil | |
| 137 | save_data() | |
| 138 | end, | |
| 139 | send = function(name) | |
| 140 | local addr = data.paired[name] | |
| 141 | if not addr then error(name .. " not found") end | |
| 142 | if not ecnet_modem.connect(addr, 3) then error("Connection to " .. name .. " failed") end
| |
| 143 | print "Connected" | |
| 144 | for slot, contents in pairs(peripheral.call(chest, "list")) do | |
| 145 | print("[OUT] Sending stack", slot)
| |
| 146 | local timed_out, result = false, nil | |
| 147 | parallel.waitForAny(function() result = send_stack(slot, addr) end, function() sleep(5) timed_out = true end) | |
| 148 | if not timed_out then print("[OUT] Destination success") else printError "[OUT] Timed out." end
| |
| 149 | end | |
| 150 | end, | |
| 151 | help = function() | |
| 152 | write([[EnderMail UI commands: | |
| 153 | address - print address | |
| 154 | update - update the code | |
| 155 | pair [address] - send a pairing request to the specified address | |
| 156 | accept_pair - accept the latest pairing request | |
| 157 | deny_pair - reject the latest pairing request | |
| 158 | paired - list all paired mailboxes | |
| 159 | unpair [name] - remove the named mailbox from your paired list | |
| 160 | send [name] - send contents of chest to specified paired mailbox | |
| 161 | ]]) | |
| 162 | end | |
| 163 | } | |
| 164 | ||
| 165 | local function handle_commands() | |
| 166 | print "Mailbox UI" | |
| 167 | local history = {}
| |
| 168 | while true do | |
| 169 | write "|> " | |
| 170 | local text = read(nil, history) | |
| 171 | ||
| 172 | if text ~= "" then table.insert(history, text) end | |
| 173 | ||
| 174 | local tokens = split_at_spaces(text) | |
| 175 | local command = table.remove(tokens, 1) | |
| 176 | local args = tokens | |
| 177 | local fn = CLI_commands[command] | |
| 178 | ||
| 179 | if not fn then | |
| 180 | for command_name, func in pairs(CLI_commands) do | |
| 181 | if command and first_letter(command_name) == first_letter(command) then fn = func end | |
| 182 | end | |
| 183 | end | |
| 184 | if not fn then | |
| 185 | print("Command", command, "not found.")
| |
| 186 | else | |
| 187 | local ok, err = pcall(fn, table.unpack(args)) | |
| 188 | if not ok then printError(err) end | |
| 189 | end | |
| 190 | end | |
| 191 | end | |
| 192 | ||
| 193 | local function handle_message(addr, msg) | |
| 194 | if type(msg) == "table" then | |
| 195 | if msg[1] == "pair" then | |
| 196 | if not msg.label or not msg.label:match(acceptable_mailbox_name_pattern) then return end | |
| 197 | print(("Pair request from %s (%s)"):format(addr, msg.label))
| |
| 198 | print "`accept_pair` to accept, `reject_pair` to deny" | |
| 199 | last_pair_request = { address = addr, label = msg.label }
| |
| 200 | elseif msg[1] == "pair_accept" then | |
| 201 | if not msg.label or not msg.label:match(acceptable_mailbox_name_pattern) then return end | |
| 202 | print(("%s (%s) accepted pairing"):format(addr, msg.label))
| |
| 203 | data.paired[msg.label] = addr | |
| 204 | save_data() | |
| 205 | elseif msg[1] == "pair_reject" then | |
| 206 | if not msg.label or not msg.label:match(acceptable_mailbox_name_pattern) then return end | |
| 207 | print(("%s (%s) rejected pairing"):format(addr, msg.label))
| |
| 208 | elseif msg[1] == "stack_request" then | |
| 209 | if not msg.channel or msg.channel < 0 or msg.channel > 0xFFF then ecnet_modem.send(addr, { "stack_request_response", false, "channel missing/invalid" }) end
| |
| 210 | ender_chest.setFrequency(msg.channel) | |
| 211 | ecnet_modem.send(addr, { "stack_request_response", true })
| |
| 212 | local start = os.clock() | |
| 213 | -- constantly attempt to move items until done | |
| 214 | while os.clock() - start <= 5 do | |
| 215 | for slot, stack in pairs(ender_chest_inv.list()) do | |
| 216 | local moved = ender_chest_inv.pushItems(chest, slot) | |
| 217 | print("[IN]", get_name(addr), stack.name, moved)
| |
| 218 | if moved > 0 then | |
| 219 | ecnet_modem.send(addr, { "stack_result", true, channel = msg.channel })
| |
| 220 | return | |
| 221 | else | |
| 222 | ecnet_modem.send(addr, { "stack_result", false, "out of space", channel = msg.channel })
| |
| 223 | return | |
| 224 | end | |
| 225 | end | |
| 226 | end | |
| 227 | ecnet_modem.send(addr, { "stack_result", false, channel = msg.channel })
| |
| 228 | elseif msg[1] == "stack_request_response" then | |
| 229 | os.queueEvent("stack_request_response", msg[2])
| |
| 230 | elseif msg[1] == "stack_result" then | |
| 231 | os.queueEvent("stack_result", msg[2], msg[3])
| |
| 232 | end | |
| 233 | end | |
| 234 | end | |
| 235 | ||
| 236 | local function handle_messages() | |
| 237 | while true do | |
| 238 | handle_message(ecnet_modem.receive()) | |
| 239 | end | |
| 240 | end | |
| 241 | ||
| 242 | parallel.waitForAll(handle_commands, handle_messages) |