Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[PACKET API: This API allows information to be easily transferred between computers. Packets are formed in a similar fashion to real network packets. Each packet
- contains the type of the program sending it ("server", "client", "browser", etc.), the sender's computer name and ID, the recipient's (destination's) computer name and
- ID, the type of the data being sent, and the data itself. The data may be anything except a function or a table containing a function. Packets are serialized as strings
- and sent over rednet. The recipient ID is used as the sending channel: pastebin:8CfjPgCu]]
- print("Packet Utility 1.0")
- --Utility for quickly yielding. This is very important for the queue functions as they are intended to run parallel to other functions
- function yield()
- --Queue an event and then yield for it. This is faster than using a timer, but does the same job
- os.queueEvent("threadyield")
- coroutine.yield("threadyield")
- end
- --[[Utility for checking if a variable is of a required type. If the type matches, true is returned. Otherwise false may be returned, or a function may be provided to
- take come custom action such as raising an error or returning an error message. Passing "standard_error" as the function will cause an error to be raised with a common
- error message. The level argument is the stack level above this function call which is responsible for the error. This defaults to the level above the caller]]
- function checkType(mVar, sType, func, nLevel)
- --The function to run if the type doesn't match, or a default function returning false
- local func = func or function() return false end
- --The level above this function's call which caused the issue, or the level above the caller
- local nLevel = nLevel or 2
- --Response to most type mismatches: "Expected [expected type], got [actual type]"
- local function standard_error(gVar, sExpectedType, sType, nLevel)
- error(string.format("Expected %s, got %s", sExpectedType, sType), nLevel)
- end
- if func == "standard_error" then
- func = standard_error
- end
- --Check our argument's types
- if type(sType) ~= "string" then standard_error(nil, "string", type(sType), 3) end
- if type(func) ~= "function" then standard_error(nil, "function", type(func), 3) end
- if type(nLevel) ~= "number" then standard_error(nil, "number", type(nLevel), 3) end
- --If the level is 0, leave it. Otherwise, add 2 to compensate for the two function calls to get to func. The level of the calling function is technically 3, but for
- --convenience we add 2 so they may use 1 as their level
- if nLevel ~= 0 then
- nLevel = nLevel + 2
- end
- --If the type of mVar does not match what is defined in sType, call func
- if type(mVar) ~= sType then
- --func is passed the variable which failed the test, the type which it needed to be, the actual type, and the level to raise the error at
- return func(mVar, sType, type(mVar), nLevel)
- end
- --mVar passed the test
- return true
- end
- --UnProtected CALL at a specified Level. Re-raises any error raised by the given function at the specified level. This makes sure our functions don't get blamed for a
- --user's mistake
- local function upcallL(nLevel, func, ...)
- --Call the function with pcall
- local tResult = {pcall(func, ...)}
- --Extract the result of pcall's attempt
- local bResult = table.remove(tResult, 1)
- --If the level is 0, leave it. Otherwise, add 1 to compensate for the call to this function. The level of the calling function is technically 2, but for
- --convenience we add 1 so they may use 1 as their level
- if nLevel ~= 0 then
- nLevel = nLevel + 1
- end
- if bResult then
- --Unpack what the function returned
- return unpack(tResult)
- else
- --Raise an error at the specified level
- error(tResult[1], nLevel)
- end
- end
- --Utility for quickly clearing a table, used to empty the queues after they've been processed in multi-packet mode
- function clearTable(tTbl)
- checkType(tTbl, "table", "standard_error")
- --Get the first index in the table
- local k = next(tTbl)
- while k do
- --Set the index's value to nil
- tTbl[k] = nil
- --Set the index to the next non-nil index. This will be nil if the table is empty, which will end the loop
- k = next(tTbl)
- end
- end
- --Copies a table (in case you didn't know)
- function copyTable(tTbl)
- checkType(tTbl, "table", "standard_error")
- --The table to copy to
- local tCopy = {}
- --Get the first index in the table
- local k = next(tTbl)
- while k do
- --Set the index's value in the copy, to its value in the original
- tCopy[k] = tTbl[k]
- --Get the next index after the current one. This will be nil if there are no more indexes, which will end the loop
- k = next(tTbl, k)
- end
- --Return the copy, and the original next to it for convenience
- return tCopy, tTbl
- end
- --Utility for creating packet tables
- function asPacketData(senderType, senderName, senderID, recipientName, recipientID, dataType, data, tOther)
- --[[
- Packet contains:
- -what kind of server or client is sending the information
- -the computer name and ID of the sender
- -the computer name and ID of the computer intended to receive the packet (the ID is usually used as the sending channel)
- -the type of data being sent
- -the data itself
- -any other information which a program may acknowledge, but the packet API will ignore
- ]]
- local tPacket = {
- senderType=senderType,
- senderName=senderName,
- senderID=senderID,
- recipientName=recipientName,
- recipientID=recipientID,
- dataType=dataType,
- data=data
- }
- --If tOther is nil, set it to an empty table
- local tOther = tOther or {}
- checkType(tOther, "table", "standard_error")
- --Add all the values in tOther to the packet
- for k, v in pairs(tOther) do
- tPacket[k] = v
- end
- return tPacket
- end
- --Utility for creating packet tables to be passed to send as send does not need to know the sender name or ID
- function asPacketDataS(senderType, recipientName, recipientID, dataType, data, tOther)
- --Leave the sender name and ID nil, as send will set it to this computer's name and ID for us
- return upcallL(2, asPacketData,
- senderType,
- nil, nil,
- recipientName,
- recipientID,
- dataType,
- data,
- tOther
- )
- end
- --[[Compares two packets, a sample and a real packet, to check for things like who the sender was, and who they were sending to. This is intended to be used to check if
- this computer was the intended recipient. If all values in the sample packet match the same values in the real packet, all values in the sample packets are functions
- which take the same value of the sample packet as an argument and calling these functions return true, or any combination of the two, this function will return true.
- Otherwise, it will return false. Any values in the packet which are not in the sample packet will be ignored]]
- function checkPacket(tSamplePacket, tPacket)
- checkType(tSamplePacket, "table", "standard_error")
- checkType(tPacket, "table", "standard_error")
- --We only care about items that tSamplePacket has in it. If tPacket has an item the tSamplePacket doesn't, we'll ignore it
- for k, v in pairs(tSamplePacket) do
- --If the packet value doesn't match the sample packet value, and the sample packet value is not a function or is a function and it returns false
- if not (tPacket[k] == v or (checkType(v, "function") and v(tPacket[k]))) then
- return false
- end
- end
- return true
- end
- --Confirms that a table contains all of the parts of a standard packet
- local function validatePacket(tPacket)
- --Create a sample packet of functions which returns if the types of the packet match the defined standard component types, and check it against the packet with checkPacket
- return checkPacket(asPacketData(
- function(mData) return checkType(mData, "string") end,--Sender type
- function(mData) return checkType(mData, "string") end,--Sender name
- function(mData) return checkType(mData, "number") end,--Sender ID
- function(mData) return checkType(mData, "string") end,--Recipient name
- function(mData) return checkType(mData, "number") end,--Recipient ID
- function(mData) return checkType(mData, "string") end--Data type
- ), tPacket)
- end
- --Formats informations about who's sending, who they're sending to, and what they're sending and returns it as a string which can be sent over rednet
- function asPacket(...)
- --Convert the arguments into a packet table
- local tPacket = upcallL(2, asPacketData, ...)
- --Check it against the standard
- if not validatePacket(tPacket) then
- error("asPacket: You are not allowed to generate corrupted packets", 2)
- end
- --Serialize the packet as a string to be sent over rednet. If the packet contains a function, an error will be raised
- return upcallL(2, textutils.serialize, tPacket)
- end
- --Formats a string as a table of data, handling any error caused by the process and returning them as an errorMessage packet
- function asData(sPacket)
- checkType(sPacket, "string", "standard_error")
- --Attempt to format the string as a table of information
- local tPacket = textutils.unserialize(sPacket)
- --If the result is a table, and the table passes as a standard packet, return it
- if checkType(tPacket, "table") and validatePacket(tPacket) then
- return tPacket
- end
- --Corrupted packet error, will usually be ignored as checkPacket will reject it in most cases
- return asPacketData(
- "system",
- "asData",
- -1,
- "none",
- -1,
- "errorMessage",
- "Packet corrupted"
- )
- end
- --Sends a packet on the channel of the given recipient recipient ID
- function send(sSenderType, sRecipientName, nRecipientID, sDataType, mData)
- --The packet is sent to the specified recipient's computer ID, assuming it will be receiving over rednet. This computers label and ID are used to form the packet
- rednet.send(nRecipientID,
- upcallL(2, asPacket, sSenderType, (os.getComputerLabel() or "LABEL_NOT_SET"), os.getComputerID(),
- sRecipientName, nRecipientID, sDataType, mData)
- )
- end
- --[[Receives a packet and converts it into data. If a timeout is specified, and no data is received in that amount of time, an empty or "blank" packet will be returned
- If a sample packet is specified, this function will not return until it receives a packet which passes the check against the specified sample packet. If specified, this
- function may return blank packets, even if they do not match the sample packet. This defaults to false]]
- function receive(nTimeout, tSamplePacket)
- --nTimeout may be nil, but otherwise it must be a number
- if nTimeout then
- checkType(nTimeout, "number", "standard_error")
- end
- --If tSamplePacket is nil, set it to an empty table
- local tSamplePacket = tSamplePacket or {}
- checkType(tSamplePacket, "table", "standard_error")
- while true do
- --Rednet may raise a termination error, we will catch this and return it as an error message if this is permitted
- local tMessage = {pcall(rednet.receive, nTimeout)}
- local tPacket
- if not tMessage[1] then
- --Some error occurred, create a packet containing the error message
- tPacket = asPacketData(
- "system",
- "receive",
- -1,
- "none",
- -1,
- "errorMessage",
- tMessage[2]
- )
- end
- --If receive timed out, it returned nil. If this is the case, create a blank packet
- if not tMessage[3] then
- tPacket = asPacketData(
- "system",
- "receive",
- -1,
- "none",
- -1,
- "blank"
- )
- end
- --If nothing has gone wrong so far, try to convert the received message into a packet
- if not tPacket then
- tPacket = asData(tMessage[3])
- end
- --Compare the packet to the sample packet. If it fails, we'll receive again until we get a packet that passes. When we get a packet that passes, we'll return it.
- --If the packet is blank, we'll return it regardless of whether or not it passes
- if checkPacket(tSamplePacket, tPacket) or tPacket.dataType == "blank" then
- return tPacket
- end
- end
- end
- --Receives a packet, or multi-packet, and places it in the queue
- function queueReceiver(tQueue, tSamplePacket)
- checkType(tQueue, "table", "standard_error")
- --If tSamplePacket is nil, set it to an empty table
- local tSamplePacket = tSamplePacket or {}
- checkType(tSamplePacket, "table", "standard_error")
- while true do
- --Wait indefinitely for a packet which matches the sample packet
- tPacket = receive(nil, tSamplePacket)
- --If the packet was sent in multi-mode, it must be received in multi-mode. This means the data will be a table containing multiple pieces of data, and a flag the
- --multiMode flag in this table will be set to true
- if checkType(tPacket.data, "table") and tPacket.data.multiMode then
- --Erase the flag, we don't need it any more
- tPacket.data.multiMode = nil
- --Add every piece of data being sent to the queue
- for k, v in pairs(tPacket.data) do
- table.insert(tQueue, v)
- end
- else
- --Add the data to the queue
- table.insert(tQueue, tPacket.data)
- end
- end
- end
- --Runs parallel to a function which should fill the given queue with information to send. This information should NOT be formatted as a packet. As information is
- --received, it will be formatted as a packet, or as a multi-packet if the third function argument is true
- function queueSender(tQueue, tTemplatePacket, bMultiPacketMode)
- checkType(tQueue, "table", "standard_error")
- checkType(tTemplatePacket, "table", "standard_error")
- checkType(bMultiPacketMode, "boolean", "standard_error")
- --tTemplatePacket may specify multiple recipients. They will be stored here
- local tRecipients = {}
- if tTemplatePacket.recipients and checkType(tTemplatePacket.recipients, "table") then
- for k, v in pairs(tTemplatePacket.recipients) do
- --If v is not a table
- checkType(v, "table",
- function(gVar, sExpectedType, sType, nLevel)
- error(string.format("Error processing entry %s, entry: Expected %s, got %s", tostring(k), sExpectedType, sType), nLevel)
- end)
- --If v.recipientName is not a string
- checkType(v.recipientName, "string",
- function(gVar, sExpectedType, sType, nLevel)
- error(string.format("Error processing entry %s, recipientName: Expected %s, got %s", tostring(k), sExpectedType, sType), nLevel)
- end)
- --If v.recipientID is not a number
- checkType(v.recipientID, "number",
- function(gVar, sExpectedType, sType, nLevel)
- error(string.format("Error processing entry %s, recipientID: Expected %s, got %s", tostring(k), sExpectedType, sType), nLevel)
- end)
- table.insert(tRecipients, {recipientName=v.recipientName, recipientID=v.recipientID})
- end
- else
- --Just treat tTemplatePacket as a regular packet
- table.insert(tRecipients, {recipientName=tTemplatePacket.recipientName, recipientID=tTemplatePacket.recipientID})
- end
- while true do
- --If there is something in the queue
- if #tQueue > 0 then
- for k, v in pairs(tRecipients) do
- --In multi-mode we send the entire queue at once as a table, then clear the entire queue at once
- if bMultiPacketMode then
- tQueue.multiMode = true
- upcallL(2, send, tTemplatePacket.senderType, v.recipientName,
- v.recipientID, tTemplatePacket.dataType, tQueue)
- clearTable(tQueue)
- else
- --In single-mode, we clear one entry at a time, sending its value as the packet data
- upcallL(2, send, tTemplatePacket.senderType, v.recipientName,
- v.recipientID, tTemplatePacket.dataType, table.remove(tQueue, 1))
- end
- end
- end
- --In single packet mode, if this runs too fast, the receiver will glitch out. yield is too fast, so we use sleep to slow the loop down a little
- pcall(sleep, 0)
- end
- end
- --Runs the given function parallel to the queueReceiver. The function is called every time a new piece of data is received and is passed that piece of data as an argument
- function packetListener(func, tSamplePacket)
- checkType(func, "function", "standard_error")
- --The queue to receive our packets in
- local tQueue = {}
- parallel.waitForAny(
- function()
- upcallL(2, queueReceiver, tQueue, tSamplePacket)
- end,
- function()
- while true do
- if #tQueue > 0 then
- --Empty out the queue
- while #tQueue > 0 do
- --Call the function, passing it the value being removed from the queue. If the function returns false, end the loop
- if not func(table.remove(tQueue, 1)) then
- return
- end
- end
- end
- yield()
- end
- end
- )
- end
- --Runs the given function parallel to the queueSender. The queue is passed to the function as an argument, anything placed in the queue will be sent over rednet and deleted
- function packetSender(func, tTemplatePacket, bMultiPacketMode)
- checkType(func, "function", "standard_error")
- --The queue to send out packets from
- local tQueue = {}
- parallel.waitForAny(
- function()
- upcallL(2, queueSender, tQueue, tTemplatePacket, bMultiPacketMode)
- end,
- function()
- --The running loop must happen in this function
- func(tQueue)
- end
- )
- end
- --END PACKET API
- --[[TRoR API: This API allows a program to share screen output with other computers, or receive it from another computer. It allows events to be transferred between
- computers, and it allows them to be modified, both before and after being sent, and just before they are pulled. It allows the shell to be escaped and a function to be
- run in the top level coroutine. This is because the multishell will strip flags off of mouse click events. Three built in functions will run the shell, the multishell,
- and a modified version of the multishll which doesn't strip mouse click events: pastebin:kjKrUP41]]
- print("TRoR Term Redirect over Rednet 1.0")
- --Kills the current running shell, allowing the given function to run in the top level coroutine
- --TLCO: http://www.computercraft.info/forums2/index.php?/topic/22078-extremely-short-no-multishell-tlco-kills-multishell-runs-normal-shell-as-the-top-level-coroutine/
- function killShell(func, ...)
- checkType(func, "function", "standard_error")
- local tArgs = {...}
- --Store the original versions of the functions we'll be overriding
- local nativeShutdown = os.shutdown
- local nativeYield = coroutine.yield
- --This is called just before the bios exits, after the shell has died
- os.shutdown = function()
- --We're ready to move on, set these variables back to their native values for normal operation
- os.shutdown = nativeShutdown
- coroutine.yield = nativeYield
- --Clean out any terms that anyone has set up
- term.redirect(term.native())
- --This has to be reset or it won't run again
- os.unloadAPI("rom/apis/rednet")
- os.loadAPI("rom/apis/rednet")
- --Run the function
- ok, err = pcall(func, unpack(tArgs))
- if not ok then
- --Attempt to show the user any errors which may have occurred
- pcall(function()
- term.redirect(term.native())
- printError(err)
- print("Press any key to continue...")
- os.pullEventRaw("key")
- end)
- end
- --The bios won't attempt another shutdown, so we will. This also allows the shell to be killed again, the overridden shutdown will be called here
- os.shutdown()
- end
- --When whatever program is currently running tries to yield or pull an event, this will crash it. This will eventually crash the shell, which will return control
- --to the bios, where our overridden shutdown function will be called
- coroutine.yield = function(...) error("Shell death", 0) end
- --Begin the chaos
- error("Shell death", 0)
- end
- --Creates a startup file which runs a set of specified commands. If a startup file already exists, it will be moved by this function, then moved back by the new startup
- local function replaceStartup(tArgs)
- --Collection of commands for the new shell to run
- local tConverted = {}
- --A single command being formed
- local tCommand = {}
- --Adds a command to the collection and clears it from the working variable
- local function addCommand()
- if #tCommand > 0 then
- table.insert(tConverted, tCommand)
- tCommand = {}
- end
- end
- for k, v in pairs(tArgs) do
- --If it's a table, convert everything in it to a string.
- if checkType(v, "table") then
- --Add whatever command was previously being built; we're now working on a new command
- addCommand()
- for ke, va in pairs(v) do
- table.insert(tCommand, tostring(va))
- end
- else
- addCommand()
- --Convert whatever else v is into a string, we'll try to run it anyway
- table.insert(tCommand, tostring(v))
- end
- --Add whatever we've got left
- addCommand()
- end
- --If there are no commands to run, we don't need to override the startup
- if #tConverted < 1 then
- return
- end
- --In case the default name already exists for some reason, we'll find a name which doesn't exist
- local sStartupRelocation
- if fs.exists("startup") then
- local sName = ".startup"
- local nAttemptCount = 1
- local sStartupRelocation = sName
- --We'll check for ".startup" first, then ".startup2", ".startup3", etc.
- while fs.exists(sStartupRelocation) do
- nAttemptCount = nAttemptCount + 1
- --".startup[attempt count]"
- sStartupRelocation = sName .. nAttemptCount
- end
- end
- local sStartup = string.format(
- [[
- --The serialized converted commands, these will just be a normal table when it is loaded
- local tCommands = %s
- --The startup location, or nil if there was no startup
- local sStartupLocation = %s
- local bRunStartup
- --We need to make sure this happens as soon as possible, we don't want to leave a mess if we crash or the user reboots the computer
- fs.delete("startup")
- --If there was a startup here before, move it back in place
- if sStartupLocation then
- fs.move(sStartupLocation, "startup")
- end
- if multishell then
- --On the multishell, we run everything in its own tab all at once
- for k, v in pairs(tCommands) do
- --The last command, or only command if there is only one, will be run in the main shell tab
- if next(tCommands, k) then
- shell.openTab(unpack(v))
- else
- shell.run(unpack(v))
- end
- end
- else
- --On the shell, we run everything in the order it was provided, one at a time
- for k, v in pairs(tCommands) do
- shell.run(unpack(v))
- end
- end
- ]],
- textutils.serialize(tConverted),
- tostring(sStartupRelocation)
- )
- --If there is already a startup, move it out of the way
- if sStartupRelocation then
- fs.move("startup", sStartupRelocation)
- end
- --Write our new startup in the actual startup file
- local tFile = fs.open("startup", "w")
- tFile.write(sStartup)
- tFile.close()
- end
- --Clears the screen and prints a message in yellow or white text, depending on if the term is color or not respectively
- local function printStartupMessage(sMessage)
- --User yellow for advanced computers, and white for regular computers
- if term.isColor() then
- term.setTextColor(colors.yellow)
- else
- term.setTextColor(colors.white)
- end
- --Background should always be black
- term.setBackgroundColor(colors.black)
- --Clear the screen
- term.clear()
- term.setCursorPos(1, 1)
- print(sMessage)
- term.setTextColor(colors.white)
- end
- --Designed to be passed to killShell as the func argument. Runs the regular shell. This is one of the two functions which may be run to fix mouse click modification
- function runShell(...)
- replaceStartup({...})
- --Display the running mode
- printStartupMessage("Regular shell")
- --Start the shell and rednet coroutines
- parallel.waitForAny(
- function()
- os.run({}, "rom/programs/shell")
- end,
- function()
- rednet.run()
- end
- )
- end
- --Designed to be passed to killShell as the func argument. Runs the multishell. This program will break mouse click modification
- function runMultishell(...)
- --Same as runShell, but running the multishell program instead
- replaceStartup({...})
- printStartupMessage("Regular multishell")
- parallel.waitForAny(
- function()
- os.run({}, "rom/programs/advanced/multishell")
- end,
- function()
- rednet.run()
- end
- )
- end
- --Runs a modified version of the multishell which doesn't strip the flags off of mouse click events.
- function clickSafeMultishell()
- --The new program will be stored in this string
- local sNewMultiShell = ""
- --Simple convenience function for adding to the program
- local function add(sText) sNewMultiShell = sNewMultiShell.."\n".. sText end
- --This goes after the function definitions, before the multishell starts running
- local sPreStart = [[
- multishell.isClickSafe = true
- local function copyEvent(tEventData)
- local tEventCopy = {}
- local k = next(tEventData)
- while k do
- tEventCopy[k] = tEventData[k]
- k = next(tEventData, k)
- end
- return tEventCopy
- end
- ]]
- --This replaces the portion of the running loop which handles click events. Rather than extracting the data normally found in a click event and passing that on, it
- --copies the whole event, modifies it as needed, and then passes the copy with all its parts intact to the process
- local sClickEvent = [[
- -- Click event
- local button, x, y = tEventData[2], tEventData[3], tEventData[4]
- if bShowMenu and y == 1 then
- -- Switch process
- local tabStart = 1
- for n=1,#tProcesses do
- tabEnd = tabStart + string.len( tProcesses[n].sTitle ) + 1
- if x >= tabStart and x <= tabEnd then
- selectProcess( n )
- redrawMenu()
- break
- end
- tabStart = tabEnd + 1
- end
- else
- -- Passthrough to current process
- tEventCopy = copyEvent(tEventData)
- tEventCopy[4] = (bShowMenu and y-1) or y
- resumeProcess( nCurrentProcess, table.unpack(tEventCopy) )
- if cullProcess( nCurrentProcess ) then
- setMenuVisible( #tProcesses >= 2 )
- redrawMenu()
- end
- end
- elseif sEvent == "mouse_drag" or sEvent == "mouse_up" or sEvent == "mouse_scroll" then
- -- Other mouse event
- local p1, x, y = tEventData[2], tEventData[3], tEventData[4]
- if not (bShowMenu and y == 1) then
- -- Passthrough to current process
- tEventCopy = copyEvent(tEventData)
- tEventCopy[4] = (bShowMenu and y-1) or y
- resumeProcess( nCurrentProcess, table.unpack(tEventCopy) )
- if cullProcess( nCurrentProcess ) then
- setMenuVisible( #tProcesses >= 2 )
- redrawMenu()
- end
- end
- ]]
- --This is basically a copy of os.run, except in runs strings instead of files
- local function run(tEnv, sFunc, sName, ...)
- --Prints an error message if it isn't nil and isn't an empty string
- local function perr(sErr) if sErr and sErr ~= "" then printError(sErr) end end
- local tEnv_ = tEnv
- --Use the global environment as our index
- setmetatable(tEnv_, {__index=_G})
- --Attempt to load the string
- local func, err = load(sFunc, sName, "t", tEnv_)
- if func then
- --If it loaded, run it
- ok, err = pcall(func, ...)
- if not ok then
- --Print out anything that goes wrong and return false
- perr(err)
- return false
- end
- --If everything went okay, return true
- return true
- end
- perr(err)
- return false
- end
- local bAddLines = true
- local tFile = fs.open("rom/programs/advanced/multishell", "r")
- --This can only happen if the function is used on a non-advanced computer
- checkType(tFile, "table", function(gVar, gExpectedType, gType, nLevel) error("Multishell not found", nLevel) end, 1)
- repeat
- local sLine = tFile.readLine()
- --This is the start of the running loop
- if sLine == "-- Begin" then
- add(sPreStart)
- add(sLine)
- --This is the start of the click event handler. We stop adding lines after this until we reach the "else" statement, which marks the end of the click handler
- elseif sLine == " elseif sEvent == \"mouse_click\" then" then
- bAddLines = false
- add(sLine)
- add(sClickEvent)
- --This is the end of the click event handler. Start adding lines again
- elseif sLine == " else" then
- bAddLines = true
- add(sLine)
- else
- --If we're not in the click event handler portion, add the line
- if bAddLines and sLine ~= nil then add(sLine) end
- end
- --Until there are no more lines to read
- until sLine == nil
- return run({}, sNewMultiShell, "ModdedMultiShell")
- end
- --Designed to be passed to killShell as the func argument. Runs the multishell in the top level coroutine. This is one of the two functions which may be run to fix mouse
- --click modification
- function runClickSafeMultishell(...)
- replaceStartup({...})
- printStartupMessage("Click safe multishell")
- parallel.waitForAny(
- function()
- clickSafeMultishell()
- end,
- function()
- rednet.run()
- end
- )
- end
- --Receives terminal commands from a remote computer and draws them on this computers screen
- function receiveScreen(tSamplePacket)
- local tSamplePacket = tSamplePacket or {}
- checkType(tSamplePacket, "table", "standard_error")
- --Create a new packet listener
- upcallL(2, packetListener, function(tCommand)
- --Make sure this is at least an empty table
- tCommand.args = tCommand.args or {}
- --The sender terminated, so we will too
- if tCommand.command == "exit" then
- return false
- end
- --The term commands are the names of the function, so get the function and call it with the args table as its arguments
- term[tCommand.command](unpack(tCommand.args))
- --Tell the packet listener to keep running
- return true
- end,
- asPacketData(
- --We can use all the tests from the sample packet, except the data type must be "termCommand", and the data must be a table
- tSamplePacket.senderType,
- tSamplePacket.senderName,
- tSamplePacket.senderID,
- tSamplePacket.recipientName,
- tSamplePacket.recipientID,
- "termCommand",
- function(mData) return checkType(mData, "table") end
- )
- )
- end
- --Sends terminal commands over rednet to be drawn by a remote computer
- function sendScreen(func, tTemplatePacket, bMultiPacketMode)
- checkType(func, "function", "standard_error")
- checkType(tTemplatePacket, "table", "standard_error")
- upcallL(2, packetSender, function(tQueue)
- local tNewTerm = {}
- --These functions do not need to be sent
- local tReturnOnly = {
- ["getCursorPos"] = true,
- ["isColor"] = true,
- ["isColour"] = true,
- ["getSize"] = true,
- ["getTextColor"] = true,
- ["getBackgroundColor"] = true,
- ["getTextColour"] = true,
- ["getBackgroundColour"] = true,
- }
- local tParentTerm
- for k, v in pairs(term.native()) do
- local sName = k
- tNewTerm[k] = (tReturnOnly[k] and function()
- --Return only functions are just called and their return values are returned
- return tParentTerm[sName]()
- end)
- or function(...)
- --Other functions are called, but not returned, and their name and arguments are sent as a command to any receiving computer
- tParentTerm[sName](...)
- table.insert(tQueue, {command=sName, args={...}})
- end
- end
- tParentTerm = term.redirect(tNewTerm)
- --Resets the term to what it was before we got to it
- term.resetTerm = function()
- term.redirect(tParentTerm)
- term.resetTerm = nil
- end
- --Set the remote term(s) cursor position
- term.setCursorPos(term.getCursorPos())
- --We'll let the function error, but not yet
- ok, err = pcall(func, tQueue)
- --Send the exit command
- table.insert(tQueue, {command="exit"})
- --Wait for any remain queued commands to be sent
- while #tQueue > 0 do yield() end
- --Reset the term if someone hasn't already
- if term.resetTerm then
- term.resetTerm()
- end
- --Now let the error be raised
- if not ok then
- error(err, 0)
- end
- end,
- asPacketDataS(
- tTemplatePacket.senderType,
- tTemplatePacket.recipientName,
- tTemplatePacket.recipientID,
- "termCommand"
- ), bMultiPacketMode
- )
- end
- --Receives events and queues them. A table containing functions which receive a table containing an event and return a table containing a modified version may be
- --provided. These may return the event as is, modify it as needed, or return nil to cancel the event
- function receiveEvents(func, tSamplePacket, tEventModifiers)
- --Default function just queues any even it receives
- local func = func or function(tEvent) os.queueEvent(unpack(tEvent)) return true end
- local tSamplePacket = tSamplePacket or {}
- local tEventModifiers = tEventModifiers or {}
- checkType(func, "function", "standard_error")
- checkType(tSamplePacket, "table", "standard_error")
- checkType(tEventModifiers, "table", "standard_error")
- for k, v in pairs(tEventModifiers) do
- checkType(v, "function", function(gVar, gExpectedType, gType, nLevel) error("All elements in the tEventModifiers table must be functions", nLevel) end)
- end
- local nativePullEvent = os.pullEventRaw
- os.pullEventRaw = function(...)
- while true do
- --Get a copy of the event
- local tEvent = copyTable({nativePullEvent(...)})
- --If there is a modification function, set the event to its call with the first copy as its arguments
- if tEventModifiers[tEvent[1]] then
- tEvent = tEventModifiers[tEvent[1]](tEvent)
- end
- --If the modifications function returned something other than a table, usually nil, we assume it wants us to void this event and wait for another one
- if checkType(tEvent, "table") then
- return unpack(tEvent)
- end
- end
- end
- --Resets os.pullEventRaw to what it was before we got to it
- os.resetOsTable = function()
- os.pullEventRaw = nativePullEvent
- os.resetOsTable = nil
- end
- ok, err = nil
- upcallL(2, packetListener, function(tEvent)
- ok, err = pcall(func, tEvent)
- if ok then
- return err
- end
- return false
- end,
- asPacketData(
- tSamplePacket.senderType,
- tSamplePacket.senderName,
- tSamplePacket.senderID,
- tSamplePacket.recipientName,
- tSamplePacket.recipientID,
- "remoteEvent",
- function(mData) return checkType(mData, "table") end
- )
- )
- --Reset the os table if someone hasn't already
- if os.resetOsTable then
- os.resetOsTable()
- end
- if not ok then
- error(err, 0)
- end
- end
- --Sends events to a remote computer. A table containing functions which receive a table containing an event and return a table containing a modified version may be
- --provided. These may return the event as is, modify it as needed, or return nil to cancel the event
- function sendEvents(tTemplatePacket, tEventModifiers, bMultiPacketMode)
- tEventModifiers = tEventModifiers or {}
- checkType(tTemplatePacket, "table", "standard_error")
- checkType(tEventModifiers, "table", "standard_error")
- upcallL(2, packetSender, function(tQueue)
- while true do
- local tEvent = copyTable({os.pullEventRaw()})
- if tEventModifiers[tEvent[1]] then
- tEvent = tEventModifiers[tEvent[1]](tEvent)
- end
- if checkType(tEvent, "table") then
- table.insert(tQueue, tEvent)
- end
- end
- end,
- asPacketDataS(
- tTemplatePacket.senderType,
- tTemplatePacket.recipientName,
- tTemplatePacket.recipientID,
- "remoteEvent"
- ), bMultiPacketMode
- )
- end
- --Tests if an event table contains the EventModified flag
- local function eventModified(tEvent)
- for k, v in pairs(tEvent) do
- if v == "EventModified" then
- return true
- end
- end
- return false
- end
- --This function returns nil on any even which hasn't been modified, but just returns the event otherwise. The return result will always be nil, which will make the event
- --it processes void. This may be used as an event modifier
- function eventVoider(tEvent)
- if eventModified(tEvent) then
- return tEvent
- end
- end
- --When called, this creates a function which swaps out the name of the event for whatever the specified new name is. The return value may be used as an event modifier
- function createEventSwapper(sNewName, ...)
- local tArgs = {...}
- return function(tEvent)
- if not eventModified(tEvent) then
- tEvent[1] = sNewName
- for k, v in pairs(tArgs) do
- table.insert(tEvent, v)
- end
- end
- return tEvent
- end
- end
Advertisement
Add Comment
Please, Sign In to add comment