m1cr0man

M1cr0API 4.0

Jun 26th, 2012
379
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 17.80 KB | None | 0 0
  1. -- M1cr0man's Programming API v4.0
  2. local x, y = term.getSize()
  3. local histable, settings = {}, {false, 1, y}
  4.  
  5. function os.pullEvent(waitEvt)                      --Stops Ctrl+T kill
  6.     repeat
  7.         local event, p1, p2, p3, p4, p5 = os.pullEventRaw()
  8.         if event == "terminate" and settings[1] ~= true then error("Terminated")
  9.         elseif not waitEvt or waitEvt == event then return event, p1, p2, p3, p4, p5 end
  10.     until false
  11. end
  12.  
  13. 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()
  14.   local waitdelay = os.startTimer(seconds)
  15.   local chk, p1, p2, p3;
  16.   repeat chk, p1, p2, p3 = os.pullEvent() until (event ~= nil and chk == event) or (chk == "timer" and p1 == waitdelay)
  17.   return chk, p1, p2, p3
  18. end
  19.  
  20. function bundleset(side, col, status)               --Sets (colour - string) to (status - boolean) on (side - string)
  21.     local curr = rs.getBundledInput(side)
  22.     if status then newcol = colors.combine(curr, colors[col])
  23.     else newcol = colors.subtract(curr, colors[col]) end
  24.     rs.setBundledOutput(side, newcol)
  25. end
  26.  
  27. 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)
  28.     --Get the original status
  29.     local origstate;
  30.     if type(colour) == "string" then origstate = rs.testBundledInput(side, colors[colour]) else origstate = rs.getInput(side) end
  31.     if (delay ~= nil and delay ~= 0) then
  32.         --Wait for new status
  33.         local delay = os.startTimer(delay)
  34.         local newstate;
  35.         repeat
  36.             local event, timer = os.pullEvent()
  37.             if type(colour) == "string" then newstate = rs.testBundledInput(side, colors[colour]) else newstate = rs.getInput(side) end
  38.         until (event == "redstone" and newstate ~= origstate) or (event == "timer" and timer == delay)
  39.         return newstate or false
  40.     else return origstate or false end
  41. end
  42.  
  43. function redinvert(side, colour)                    --Inverts current signal on (side). Colour is a bundled colour (string)
  44.     local state = redchk(side, nil, colour)
  45.     if type(colour) == "string" then bundleset(side, colour, not state) else rs.setOutput(side, not state) end
  46. end
  47.  
  48. function redsig(side, delay, colour)                --Sends a true signal for (delay - int) seconds on (side - string). Colour is a bundled colour (string)
  49.     if type(delay) ~= "number" or delay < 0.1 then delay = 0.1 end
  50.     redinvert(side, colour)
  51.     wait(delay)
  52.     redinvert(side, colour)
  53. end
  54.  
  55. 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.
  56.     local input = ""
  57.     repeat
  58.         local count = 0
  59.         local event, key = os.pullEvent()
  60.         if event == "key" and key == 14 then input = input:sub(1, #input-1)
  61.         elseif event == "key" and key == 28 then return(input)
  62.         elseif (not limit or (#input <= limit)) and event == "char" and key then input = input..key end
  63.         repeat
  64.             printex(line, " ", pos+count, "noclr")
  65.             count = count+1
  66.         until count >= #input+1
  67.         if not char then printex(line, input, pos, args) else for i = 1, #input do printex(line, char, pos+i, args) end end
  68.     until key == 28
  69. end
  70.  
  71. local function history(monitor)                 --Keeps record of printex output and displays it between spos and epos
  72.     if histable and #histable >= 1 then
  73.         local amount = settings[2]-settings[3]+2
  74.         for i, data in pairs(histable) do if i > amount then break elseif i > 1 then
  75.             --Print on monitor
  76.             if monitor and i == 1 then printex(settings[2], data, nil, "omonitor ishst")
  77.             elseif monitor then printex(settings[2]-i+2, data, nil, "omonitor ishst") end
  78.             --Print on screen
  79.             if not monitor and i == 1 then printex(settings[2], data, nil, "ishst")
  80.             elseif not monitor then printex(settings[2]-i+2, data, nil, "ishst") end
  81.         end end
  82.     end
  83. end
  84.  
  85. function gethistory(agelimit)                   --Returns all printex history or X amount of recent history
  86.     if not agelimit then return(histable) else
  87.         local results = {}
  88.         for i,data in ipairs(histable) do if i <= agelimit then table.insert(results, data) end end
  89.         return(results)
  90.     end
  91. end
  92.  
  93. 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.
  94.     local result, firstStack, xpos, x, str = "", true, (xpos or 1), (maxLen or x), str:gsub("\n", " N3WL1NE ")
  95.     local len, startSpace, endSpace = xpos, str:sub(1,1) == " ", str:sub(#str, #str) == " "
  96. --Compensate for the fact that spaces at start and end will get removed
  97.     if startSpace then len = len+1 end
  98.     for part in str:gmatch("[^%s]+") do
  99.         local plen = part:len()
  100.     --Account for existing new lines
  101.         if part == "N3WL1NE" then result, len = result.."\n", 0
  102.     --Wrap and append word
  103.         elseif len+plen > x then result, len = result.."\n"..part, plen
  104.     --Append word
  105.         else result, len = result..(function() if firstStack or len == 0 then return "" end return " " end)()..part, len+plen+1 end
  106.         firstStack = false
  107.     end
  108. --Compensate for the fact that spaces at start and end got removed
  109.     if startSpace then result = " "..result end
  110.     if endSpace then result = result.." " end
  111.     return(result)
  112. end
  113.  
  114. function printex(line, text, indent, args)
  115. --Strings Table - Format: {"text", "colourName", "text", "colourName"}
  116.     local line, text, indent, args, index, strings = (line or select(2, term.getCursorPos())), (text or ""), (indent or 1), (args or "none"), 1;
  117.     local origx, origy = term.getCursorPos()
  118.  
  119. --Finally output text
  120.     local function output()
  121.     --Set offset from original line for new lines
  122.         local partOffset, firstStack, concated = 0, true, ""
  123.     --Concat the table's text to get real length of lines
  124.         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
  125.     --Find the true length of a string to be printed
  126.         local function realLen()
  127.             local start, stop;
  128.             for lines = 0, partOffset do
  129.                 if stop then start = stop+2 end
  130.                 local newStop = concated:find("\n", start)
  131.                 if not newStop then
  132.                     stop = #concated
  133.                     break
  134.                 else stop = newStop-1 end
  135.             end
  136.             return #concated:sub((start or 1), stop)
  137.         end
  138.     --A bit of summarising
  139.         local function print(str)
  140.             local indentTrans, x = {["left"] = 1, ["right"] = x-realLen()+1, ["center"] = x/2-realLen()/2+1};
  141.             if type(indent) == "string" then x = indentTrans[indent] else x = indent end
  142.             term.setCursorPos(x, line+partOffset)
  143.             if not args:match("noclr") then term.clearLine(line) end
  144.             term.write(str)
  145.             firstStack = false
  146.         end
  147.     --Get the lines in strings
  148.         for index, part in pairs(strings) do
  149.         --Set colours
  150.             if colors[part] then if term.isColor() then term.setTextColor(colors[part]) end
  151.             elseif colors[part:sub(3)] then if term.isColor() then term.setBackgroundColor(colors[part:sub(3)]) end
  152.         --If the string has many lines, split it up and print each one on separate lines
  153.             elseif part:match("\n") then
  154.                 local firstLoop = true
  155.                 for sentence in part:gmatch("[^\n]+") do
  156.                     if firstLoop and not firstStack then term.write(sentence) else
  157.                         if not firstStack then partOffset = partOffset+1 end
  158.                         print(sentence)
  159.                     end
  160.                     firstLoop = false
  161.                 end
  162.         --If the string should be printed on one line, do so. Firststack is for accounting if the cursorPos has not been set
  163.             elseif firstStack then print(part)
  164.             else term.write(part) end
  165.         end
  166.     end
  167.  
  168. --Wrap text in strings table after removing colours, and record the string lengths
  169.     local function wrapText()
  170.     --Set initial length
  171.         local len, maxLen = 0;
  172.         if type(indent) == "number" then maxLen = x-indent+1 end
  173.     --Get the lines in strings
  174.         for index, part in pairs(strings) do
  175.             if not colors[part] and not colors[part:sub(3)] and #part > 0 then
  176.                 if not args:match("nowrap") then strings[index] = wrap(part, maxLen, len) end
  177.                 part = strings[index]
  178.             --Get length of last part of line
  179.                 if part:match("\n") then len = #part:match(".*\n(.*)") else len = len+#part end
  180.             end
  181.         end
  182.     end
  183.  
  184. --Extract and add the colours defined in the string
  185.     local function getColours(str)
  186.         local before, col, after = (str or text):match("(.-)%%(.-)%%(.*)")
  187.     --If colours are defined in the string
  188.         if col and (colors[col] or colors[col:sub(3)]) then table.insert(strings, index+1, col)
  189.     --If defined colour is not valid, assume it is just part of text
  190.         elseif col then before = before.."%"..col.."%" end
  191.     --Add text before colour change to strings table
  192.         if before then table.insert(strings, index, before) end
  193.         index = index+2
  194.     --Check text after change for more colours
  195.         if after:match("%%(.-)%%") then getColours(after) else table.insert(strings, index, after) end
  196.     end
  197.  
  198.     function run()
  199.         strings, x, y = {}, term.getSize()
  200.     --Only extract colours if any exist
  201.         if text:match("%%(.-)%%") then getColours() else table.insert(strings, text) end
  202.         wrapText()
  203.         output()
  204.     end
  205.  
  206. --Shit happens
  207.     if not args:match("omonitor") and not args:match("oprinter") then run() end
  208. --Print text on monitor if it should
  209.     if args:match("monitor") and getperipheral("monitor") then
  210.         term.redirect(peripheral.wrap(getperipheral("monitor")))
  211.         run()
  212.         term.restore()
  213.     end
  214.  
  215. --History Stuff
  216.     if args:match("history") or args:match("hstmon") then table.insert(histable,1,text) end --Add string to history
  217.     if args:match("hstmon") then history(true) end                                          --Print history on monitor if it should
  218.     if args:match("history") then history() end                                             --Print history if it should
  219.  
  220. --Cursor Stuff
  221.     if args:match("nocursor") then term.setCursorPos(origx, origy)
  222.     elseif not args:match("ishst") then term.setCursorPos(1, select(2, term.getCursorPos())+1) end
  223. end
  224.  
  225. function convert(data)                              --Convert a string into the correct type. Eg. "true" -> to boolean true
  226.     if data == "true" then return true elseif data == "false" then return false end
  227.     return deserialize(data) or tonumber(data) or data
  228. end
  229.  
  230. 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
  231.     local data, currentLine, file = "", (startLine or 1), io.open(fileName, "r")
  232.     if not file then return false end
  233. --If startLine and stopLine are not defined, read all the text
  234.     if not startLine and not stopLine then
  235.         data = file:read("*a")
  236.         file:close()
  237.     --Convert data to correct type if needed
  238.         return convert(data)
  239.     end
  240. --Seek to startLine
  241.     if startLine then for i = 1, startLine do file:read() end end
  242. --Read until EOF or stopLine
  243.     repeat
  244.         local currentData = file:read()
  245.         if not currentData then break end
  246.         data, currentLine = data..currentData, currentLine+1
  247.     until currentLine == stopLine+1
  248.     file:close()
  249. --Convert data to correct type if needed
  250.     return convert(data)
  251. end
  252.  
  253. function save(fileName, data, append)               --Save data to file in serialized form
  254.     local file = io.open(fileName, (function() if append then return "a" end return "w" end)())
  255.     file:write(serialize(data))
  256.     file:close()
  257. end
  258.  
  259. 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
  260.     rednet.open(getperipheral("modem"))
  261.     local data = serialize(data)
  262.     if type(pcid) == "number" then rednet.send(pcid, data) else rednet.broadcast(data) end
  263.     if not keepOpen then rednet.close(getperipheral("modem")) end
  264. end
  265.  
  266. 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
  267.     rednet.open(getperipheral("modem"))
  268.     local timer, data, evt, p1, p2;
  269.     if timeout then timer = os.startTimer(timout) end
  270.     repeat
  271.         evt, p1, p2 = os.pullEvent()
  272.         if evt == "rednet_message" and p1 ~= os.computerID() and (not pcid or p1 == pcid) then data = p2 end
  273.         if evt == "timer" then return false end
  274.     until data and data == p2
  275.     if not keepOpen then rednet.close(getperipheral("modem")) end
  276.     return p1, convert(data)
  277. end
  278.  
  279. function tablecontains(tbl, value)                  --Checks if <value> exists in table.
  280.   for i, data in pairs(tbl) do if data == element then return true end end
  281.   return false
  282. end
  283.  
  284. function tablecompare(tbla, tblb)                   --Compares table-a to table-b and returns true if they are exactly the same
  285.     for i, data in pairs(tbla) do if not tblb[i] or tblb[i] ~= data then return false end end
  286.     return true
  287. end
  288.  
  289. 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
  290.     local faces, face = {[1] = "top", [2] = "bottom", [3] = "front", [4] = "back", [5] = "left", [6] = "right"};
  291.     for _, testface in pairs(faces) do
  292.         if peripheral.isPresent(testface) and peripheral.getType(testface) == device then if wrap then return peripheral.wrap(testface) else return(testface) end end
  293.     end
  294. end
  295.  
  296. function set(antiterm, spos, epos)              --Sets api settings
  297.   settings[1], settings[2], settings[3] = (antiterm or settings[1]), (spos or settings[2]), (epos or settings[3])
  298. end
  299.  
  300. --Below is immibis' Serialize/Deserialize replacements. All credit to him.
  301.  
  302. local function serializeInt(i)
  303.         local s = ""
  304.         repeat
  305.                 s = s .. string.char((i % 128) + ((i >= 128) and 128 or 0))
  306.                 i = math.floor(i / 128)
  307.         until i == 0
  308.         return s
  309. end
  310. -- returns int, next position
  311. local function deserializeInt(s,pos)
  312.         local k = pos
  313.         local i = 0
  314.         local m = 1
  315.         while true do
  316.                 local b = string.byte(s:sub(k,k))
  317.                 i = i + m * (b % 128)
  318.                 m = m * 128
  319.                 k = k + 1
  320.                 if b < 128 then break end
  321.         end
  322.         return i, k
  323. end
  324.  
  325. local nextid_key = {}
  326. local function serializeInternal(obj, seen)
  327.         if obj ~= nil and seen[obj] then
  328.                 return "\06" .. serializeInt(seen[obj])
  329.         end
  330.         if type(obj) == "table" then
  331.                 local id = seen[nextid_key]
  332.                 seen[nextid_key] = id + 1
  333.                 seen[obj] = id
  334.  
  335.                 local s = "\05"
  336.                 local ikeys = {}
  337.                 for k,v in ipairs(obj) do
  338.                         ikeys[k] = v
  339.                         s = s .. serializeInternal(v, seen)
  340.                 end
  341.                 s = s .. serializeInternal(nil, seen)
  342.                 for k,v in pairs(obj) do
  343.                         if ikeys[k] == nil then
  344.                                 s = s .. serializeInternal(k, seen) .. serializeInternal(v, seen)
  345.                         end
  346.                 end
  347.                 s = s .. serializeInternal(nil, seen)
  348.                 return s
  349.         elseif type(obj) == "number" then
  350.                 local ns = tostring(obj)
  351.                 return "\04" .. serializeInt(ns:len()) .. ns
  352.         elseif type(obj) == "string" then
  353.                 return "\03" .. serializeInt(obj:len()) .. obj
  354.         elseif type(obj) == "boolean" then
  355.                 if obj then
  356.                         return "\01"
  357.                 else
  358.                         return "\02"
  359.                 end
  360.         elseif type(obj) == "nil" then
  361.                 return "\00"
  362.         elseif type(obj) == "userdata" then
  363.                 error("cannot serialize userdata")
  364.         elseif type(obj) == "thread" then
  365.                 error("cannot serialize threads")
  366.         elseif type(obj) == "function" then
  367.                 error("cannot serialize functions")
  368.         else
  369.                 error("unknown type: " .. type(obj))
  370.         end
  371. end
  372. function serialize(obj)
  373.         return serializeInternal(obj, {[nextid_key] = 0})
  374. end
  375. function deserialize(s)
  376.         local pos = 1
  377.         local seen = {}
  378.         local nextid = 0
  379.         local function internal()
  380.                 local tch = s:sub(pos,pos)
  381.                 local len
  382.                 pos = pos + 1
  383.                 if tch == "\00" then
  384.                         return nil
  385.                 elseif tch == "\01" then
  386.                         return true
  387.                 elseif tch == "\02" then
  388.                         return false
  389.                 elseif tch == "\03" then
  390.                         len, pos = deserializeInt(s, pos)
  391.                         local rv = s:sub(pos, pos+len-1)
  392.                         pos = pos + len
  393.                         return rv
  394.                 elseif tch == "\04" then
  395.                         len, pos = deserializeInt(s, pos)
  396.                         local rv = s:sub(pos, pos+len-1)
  397.                         pos = pos + len
  398.                         return tonumber(rv)
  399.                 elseif tch == "\05" then
  400.                         local id = nextid
  401.                         nextid = id + 1
  402.                         local t = {}
  403.                         seen[id] = t
  404.  
  405.                         local k = 1
  406.                         while true do
  407.                                 local v = internal()
  408.                                 if v == nil then break end
  409.                                 t[k] = v
  410.                                 k = k + 1
  411.                         end
  412.  
  413.                         while true do
  414.                                 local k = internal()
  415.                                 if k == nil then break end
  416.                                 local v = internal()
  417.                                 if v == nil then break end
  418.                                 t[k] = v
  419.                         end
  420.                         return t
  421.                 elseif tch == "\06" then
  422.                         local id
  423.                         id, pos = deserializeInt(s, pos)
  424.                         return seen[id]
  425.                 else
  426.                         return nil
  427.                 end
  428.         end
  429.         return internal()
  430. end
Advertisement
Add Comment
Please, Sign In to add comment