Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- M1cr0man's Programming API v4.0
- local x, y = term.getSize()
- local histable, settings = {}, {false, 1, y}
- function os.pullEvent(waitEvt) --Stops Ctrl+T kill
- repeat
- local event, p1, p2, p3, p4, p5 = os.pullEventRaw()
- if event == "terminate" and settings[1] ~= true then error("Terminated")
- elseif not waitEvt or waitEvt == event then return event, p1, p2, p3, p4, p5 end
- until false
- end
- function wait(seconds, event) --Pauses program for (seconds - int) and waits for (event - string - optional) to happen within time. If it does, returns first 4 vals of os.pullEvent()
- local waitdelay = os.startTimer(seconds)
- local chk, p1, p2, p3;
- repeat chk, p1, p2, p3 = os.pullEvent() until (event ~= nil and chk == event) or (chk == "timer" and p1 == waitdelay)
- return chk, p1, p2, p3
- end
- function bundleset(side, col, status) --Sets (colour - string) to (status - boolean) on (side - string)
- local curr = rs.getBundledInput(side)
- if status then newcol = colors.combine(curr, colors[col])
- else newcol = colors.subtract(curr, colors[col]) end
- rs.setBundledOutput(side, newcol)
- end
- function redchk(side, delay, colour) --Checks for and returns rs state changes on (side - string) within (Delay - int). If no delay is given, will return current state. Colour is a bundled colour (string)
- --Get the original status
- local origstate;
- if type(colour) == "string" then origstate = rs.testBundledInput(side, colors[colour]) else origstate = rs.getInput(side) end
- if (delay ~= nil and delay ~= 0) then
- --Wait for new status
- local delay = os.startTimer(delay)
- local newstate;
- repeat
- local event, timer = os.pullEvent()
- if type(colour) == "string" then newstate = rs.testBundledInput(side, colors[colour]) else newstate = rs.getInput(side) end
- until (event == "redstone" and newstate ~= origstate) or (event == "timer" and timer == delay)
- return newstate or false
- else return origstate or false end
- end
- function redinvert(side, colour) --Inverts current signal on (side). Colour is a bundled colour (string)
- local state = redchk(side, nil, colour)
- if type(colour) == "string" then bundleset(side, colour, not state) else rs.setOutput(side, not state) end
- end
- function redsig(side, delay, colour) --Sends a true signal for (delay - int) seconds on (side - string). Colour is a bundled colour (string)
- if type(delay) ~= "number" or delay < 0.1 then delay = 0.1 end
- redinvert(side, colour)
- wait(delay)
- redinvert(side, colour)
- end
- function readex(line, limit, char, pos, args) --Same as read, only char limit and line to print on can be used. Readme for more info.
- local input = ""
- repeat
- local count = 0
- local event, key = os.pullEvent()
- if event == "key" and key == 14 then input = input:sub(1, #input-1)
- elseif event == "key" and key == 28 then return(input)
- elseif (not limit or (#input <= limit)) and event == "char" and key then input = input..key end
- repeat
- printex(line, " ", pos+count, "noclr")
- count = count+1
- until count >= #input+1
- if not char then printex(line, input, pos, args) else for i = 1, #input do printex(line, char, pos+i, args) end end
- until key == 28
- end
- local function history(monitor) --Keeps record of printex output and displays it between spos and epos
- if histable and #histable >= 1 then
- local amount = settings[2]-settings[3]+2
- for i, data in pairs(histable) do if i > amount then break elseif i > 1 then
- --Print on monitor
- if monitor and i == 1 then printex(settings[2], data, nil, "omonitor ishst")
- elseif monitor then printex(settings[2]-i+2, data, nil, "omonitor ishst") end
- --Print on screen
- if not monitor and i == 1 then printex(settings[2], data, nil, "ishst")
- elseif not monitor then printex(settings[2]-i+2, data, nil, "ishst") end
- end end
- end
- end
- function gethistory(agelimit) --Returns all printex history or X amount of recent history
- if not agelimit then return(histable) else
- local results = {}
- for i,data in ipairs(histable) do if i <= agelimit then table.insert(results, data) end end
- return(results)
- end
- end
- function wrap(str, maxLen, xpos) --Adds \n s to break a string across different lines with max length of newx and account for existing indentation of xpos.
- local result, firstStack, xpos, x, str = "", true, (xpos or 1), (maxLen or x), str:gsub("\n", " N3WL1NE ")
- local len, startSpace, endSpace = xpos, str:sub(1,1) == " ", str:sub(#str, #str) == " "
- --Compensate for the fact that spaces at start and end will get removed
- if startSpace then len = len+1 end
- for part in str:gmatch("[^%s]+") do
- local plen = part:len()
- --Account for existing new lines
- if part == "N3WL1NE" then result, len = result.."\n", 0
- --Wrap and append word
- elseif len+plen > x then result, len = result.."\n"..part, plen
- --Append word
- else result, len = result..(function() if firstStack or len == 0 then return "" end return " " end)()..part, len+plen+1 end
- firstStack = false
- end
- --Compensate for the fact that spaces at start and end got removed
- if startSpace then result = " "..result end
- if endSpace then result = result.." " end
- return(result)
- end
- function printex(line, text, indent, args)
- --Strings Table - Format: {"text", "colourName", "text", "colourName"}
- local line, text, indent, args, index, strings = (line or select(2, term.getCursorPos())), (text or ""), (indent or 1), (args or "none"), 1;
- local origx, origy = term.getCursorPos()
- --Finally output text
- local function output()
- --Set offset from original line for new lines
- local partOffset, firstStack, concated = 0, true, ""
- --Concat the table's text to get real length of lines
- for index, part in pairs(strings) do if part and not colors[part] and not colors[part:sub(3)] then concated = concated..part end end
- --Find the true length of a string to be printed
- local function realLen()
- local start, stop;
- for lines = 0, partOffset do
- if stop then start = stop+2 end
- local newStop = concated:find("\n", start)
- if not newStop then
- stop = #concated
- break
- else stop = newStop-1 end
- end
- return #concated:sub((start or 1), stop)
- end
- --A bit of summarising
- local function print(str)
- local indentTrans, x = {["left"] = 1, ["right"] = x-realLen()+1, ["center"] = x/2-realLen()/2+1};
- if type(indent) == "string" then x = indentTrans[indent] else x = indent end
- term.setCursorPos(x, line+partOffset)
- if not args:match("noclr") then term.clearLine(line) end
- term.write(str)
- firstStack = false
- end
- --Get the lines in strings
- for index, part in pairs(strings) do
- --Set colours
- if colors[part] then if term.isColor() then term.setTextColor(colors[part]) end
- elseif colors[part:sub(3)] then if term.isColor() then term.setBackgroundColor(colors[part:sub(3)]) end
- --If the string has many lines, split it up and print each one on separate lines
- elseif part:match("\n") then
- local firstLoop = true
- for sentence in part:gmatch("[^\n]+") do
- if firstLoop and not firstStack then term.write(sentence) else
- if not firstStack then partOffset = partOffset+1 end
- print(sentence)
- end
- firstLoop = false
- end
- --If the string should be printed on one line, do so. Firststack is for accounting if the cursorPos has not been set
- elseif firstStack then print(part)
- else term.write(part) end
- end
- end
- --Wrap text in strings table after removing colours, and record the string lengths
- local function wrapText()
- --Set initial length
- local len, maxLen = 0;
- if type(indent) == "number" then maxLen = x-indent+1 end
- --Get the lines in strings
- for index, part in pairs(strings) do
- if not colors[part] and not colors[part:sub(3)] and #part > 0 then
- if not args:match("nowrap") then strings[index] = wrap(part, maxLen, len) end
- part = strings[index]
- --Get length of last part of line
- if part:match("\n") then len = #part:match(".*\n(.*)") else len = len+#part end
- end
- end
- end
- --Extract and add the colours defined in the string
- local function getColours(str)
- local before, col, after = (str or text):match("(.-)%%(.-)%%(.*)")
- --If colours are defined in the string
- if col and (colors[col] or colors[col:sub(3)]) then table.insert(strings, index+1, col)
- --If defined colour is not valid, assume it is just part of text
- elseif col then before = before.."%"..col.."%" end
- --Add text before colour change to strings table
- if before then table.insert(strings, index, before) end
- index = index+2
- --Check text after change for more colours
- if after:match("%%(.-)%%") then getColours(after) else table.insert(strings, index, after) end
- end
- function run()
- strings, x, y = {}, term.getSize()
- --Only extract colours if any exist
- if text:match("%%(.-)%%") then getColours() else table.insert(strings, text) end
- wrapText()
- output()
- end
- --Shit happens
- if not args:match("omonitor") and not args:match("oprinter") then run() end
- --Print text on monitor if it should
- if args:match("monitor") and getperipheral("monitor") then
- term.redirect(peripheral.wrap(getperipheral("monitor")))
- run()
- term.restore()
- end
- --History Stuff
- if args:match("history") or args:match("hstmon") then table.insert(histable,1,text) end --Add string to history
- if args:match("hstmon") then history(true) end --Print history on monitor if it should
- if args:match("history") then history() end --Print history if it should
- --Cursor Stuff
- if args:match("nocursor") then term.setCursorPos(origx, origy)
- elseif not args:match("ishst") then term.setCursorPos(1, select(2, term.getCursorPos())+1) end
- end
- function convert(data) --Convert a string into the correct type. Eg. "true" -> to boolean true
- if data == "true" then return true elseif data == "false" then return false end
- return deserialize(data) or tonumber(data) or data
- end
- function load(fileName, startLine, stopLine) --Loads and returns data from <startline - optional> to <stopLine - optional>. Automatically de-serializes tables, turns booleans to booleans and numbers to numbers. Returns false if file does not exist
- local data, currentLine, file = "", (startLine or 1), io.open(fileName, "r")
- if not file then return false end
- --If startLine and stopLine are not defined, read all the text
- if not startLine and not stopLine then
- data = file:read("*a")
- file:close()
- --Convert data to correct type if needed
- return convert(data)
- end
- --Seek to startLine
- if startLine then for i = 1, startLine do file:read() end end
- --Read until EOF or stopLine
- repeat
- local currentData = file:read()
- if not currentData then break end
- data, currentLine = data..currentData, currentLine+1
- until currentLine == stopLine+1
- file:close()
- --Convert data to correct type if needed
- return convert(data)
- end
- function save(fileName, data, append) --Save data to file in serialized form
- local file = io.open(fileName, (function() if append then return "a" end return "w" end)())
- file:write(serialize(data))
- file:close()
- end
- function send(data, pcid, keepOpen) --Sends <data> to <pcid - optional> in serialized form via wifi. If <keepOpen - optional> is true, rednet modem will not be closed afterwards
- rednet.open(getperipheral("modem"))
- local data = serialize(data)
- if type(pcid) == "number" then rednet.send(pcid, data) else rednet.broadcast(data) end
- if not keepOpen then rednet.close(getperipheral("modem")) end
- end
- function receive(pcid, timeout, keepOpen) --Receive data <from pcid - optional> via wifi modem. Will return false after <timeout - optional> seconds if specified. If <keepOpen - optional> is true, rednet modem will not be closed afterwards
- rednet.open(getperipheral("modem"))
- local timer, data, evt, p1, p2;
- if timeout then timer = os.startTimer(timout) end
- repeat
- evt, p1, p2 = os.pullEvent()
- if evt == "rednet_message" and p1 ~= os.computerID() and (not pcid or p1 == pcid) then data = p2 end
- if evt == "timer" then return false end
- until data and data == p2
- if not keepOpen then rednet.close(getperipheral("modem")) end
- return p1, convert(data)
- end
- function tablecontains(tbl, value) --Checks if <value> exists in table.
- for i, data in pairs(tbl) do if data == element then return true end end
- return false
- end
- function tablecompare(tbla, tblb) --Compares table-a to table-b and returns true if they are exactly the same
- for i, data in pairs(tbla) do if not tblb[i] or tblb[i] ~= data then return false end end
- return true
- end
- function getperipheral(device, wrap) --Searches all faces of computer for <device>. Returns wrapped device if <wrap - optional> is true, the face of device, or nil if not found
- local faces, face = {[1] = "top", [2] = "bottom", [3] = "front", [4] = "back", [5] = "left", [6] = "right"};
- for _, testface in pairs(faces) do
- if peripheral.isPresent(testface) and peripheral.getType(testface) == device then if wrap then return peripheral.wrap(testface) else return(testface) end end
- end
- end
- function set(antiterm, spos, epos) --Sets api settings
- settings[1], settings[2], settings[3] = (antiterm or settings[1]), (spos or settings[2]), (epos or settings[3])
- end
- --Below is immibis' Serialize/Deserialize replacements. All credit to him.
- local function serializeInt(i)
- local s = ""
- repeat
- s = s .. string.char((i % 128) + ((i >= 128) and 128 or 0))
- i = math.floor(i / 128)
- until i == 0
- return s
- end
- -- returns int, next position
- local function deserializeInt(s,pos)
- local k = pos
- local i = 0
- local m = 1
- while true do
- local b = string.byte(s:sub(k,k))
- i = i + m * (b % 128)
- m = m * 128
- k = k + 1
- if b < 128 then break end
- end
- return i, k
- end
- local nextid_key = {}
- local function serializeInternal(obj, seen)
- if obj ~= nil and seen[obj] then
- return "\06" .. serializeInt(seen[obj])
- end
- if type(obj) == "table" then
- local id = seen[nextid_key]
- seen[nextid_key] = id + 1
- seen[obj] = id
- local s = "\05"
- local ikeys = {}
- for k,v in ipairs(obj) do
- ikeys[k] = v
- s = s .. serializeInternal(v, seen)
- end
- s = s .. serializeInternal(nil, seen)
- for k,v in pairs(obj) do
- if ikeys[k] == nil then
- s = s .. serializeInternal(k, seen) .. serializeInternal(v, seen)
- end
- end
- s = s .. serializeInternal(nil, seen)
- return s
- elseif type(obj) == "number" then
- local ns = tostring(obj)
- return "\04" .. serializeInt(ns:len()) .. ns
- elseif type(obj) == "string" then
- return "\03" .. serializeInt(obj:len()) .. obj
- elseif type(obj) == "boolean" then
- if obj then
- return "\01"
- else
- return "\02"
- end
- elseif type(obj) == "nil" then
- return "\00"
- elseif type(obj) == "userdata" then
- error("cannot serialize userdata")
- elseif type(obj) == "thread" then
- error("cannot serialize threads")
- elseif type(obj) == "function" then
- error("cannot serialize functions")
- else
- error("unknown type: " .. type(obj))
- end
- end
- function serialize(obj)
- return serializeInternal(obj, {[nextid_key] = 0})
- end
- function deserialize(s)
- local pos = 1
- local seen = {}
- local nextid = 0
- local function internal()
- local tch = s:sub(pos,pos)
- local len
- pos = pos + 1
- if tch == "\00" then
- return nil
- elseif tch == "\01" then
- return true
- elseif tch == "\02" then
- return false
- elseif tch == "\03" then
- len, pos = deserializeInt(s, pos)
- local rv = s:sub(pos, pos+len-1)
- pos = pos + len
- return rv
- elseif tch == "\04" then
- len, pos = deserializeInt(s, pos)
- local rv = s:sub(pos, pos+len-1)
- pos = pos + len
- return tonumber(rv)
- elseif tch == "\05" then
- local id = nextid
- nextid = id + 1
- local t = {}
- seen[id] = t
- local k = 1
- while true do
- local v = internal()
- if v == nil then break end
- t[k] = v
- k = k + 1
- end
- while true do
- local k = internal()
- if k == nil then break end
- local v = internal()
- if v == nil then break end
- t[k] = v
- end
- return t
- elseif tch == "\06" then
- local id
- id, pos = deserializeInt(s, pos)
- return seen[id]
- else
- return nil
- end
- end
- return internal()
- end
Advertisement
Add Comment
Please, Sign In to add comment