IGDev

Teachutils

Sep 4th, 2015
162
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 55.04 KB | None | 0 0
  1. local function main(...)
  2.     --[[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
  3.     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
  4.     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
  5.     and sent over rednet. The recipient ID is used as the sending channel: pastebin:8CfjPgCu]]
  6.     print("Packet Utility 1.0")
  7.     --Utility for quickly yielding. This is very important for the queue functions as they are intended to run parallel to other functions
  8.     function yield()
  9.         --Queue an event and then yield for it. This is faster than using a timer, but does the same job
  10.         os.queueEvent("threadyield")
  11.         coroutine.yield("threadyield")
  12.  
  13.     end
  14.     --[[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
  15.     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
  16.     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]]
  17.     function checkType(mVar, sType, func, nLevel)
  18.         --The function to run if the type doesn't match, or a default function returning false
  19.         local func = func or function() return false end
  20.         --The level above this function's call which caused the issue, or the level above the caller
  21.         local nLevel = nLevel or 2
  22.         --Response to most type mismatches: "Expected [expected type], got [actual type]"
  23.         local function standard_error(gVar, sExpectedType, sType, nLevel)
  24.            
  25.             error(string.format("Expected %s, got %s", sExpectedType, sType), nLevel)
  26.            
  27.         end
  28.        
  29.         if func == "standard_error" then
  30.            
  31.             func = standard_error
  32.            
  33.         end
  34.         --Check our argument's types
  35.         if type(sType) ~= "string" then standard_error(nil, "string", type(sType), 3) end
  36.         if type(func) ~= "function" then standard_error(nil, "function", type(func), 3) end
  37.         if type(nLevel) ~= "number" then standard_error(nil, "number", type(nLevel), 3) end
  38.         --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
  39.         --convenience we add 2 so they may use 1 as their level
  40.         if nLevel ~= 0 then
  41.            
  42.             nLevel = nLevel + 2
  43.            
  44.         end
  45.         --If the type of mVar does not match what is defined in sType, call func
  46.         if type(mVar) ~= sType then
  47.             --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
  48.             return func(mVar, sType, type(mVar), nLevel)
  49.            
  50.         end
  51.         --mVar passed the test
  52.         return true
  53.        
  54.     end
  55.     --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
  56.     --user's mistake
  57.     local function upcallL(nLevel, func, ...)
  58.         --Call the function with pcall
  59.         local tResult = {pcall(func, ...)}
  60.         --Extract the result of pcall's attempt
  61.         local bResult = table.remove(tResult, 1)
  62.         --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
  63.         --convenience we add 1 so they may use 1 as their level
  64.         if nLevel ~= 0 then
  65.            
  66.             nLevel = nLevel + 1
  67.            
  68.         end
  69.        
  70.         if bResult then
  71.             --Unpack what the function returned
  72.             return unpack(tResult)
  73.            
  74.         else
  75.             --Raise an error at the specified level
  76.             error(tResult[1], nLevel)
  77.            
  78.         end
  79.        
  80.     end
  81.     --Utility for quickly clearing a table, used to empty the queues after they've been processed in multi-packet mode
  82.     function clearTable(tTbl)
  83.        
  84.         checkType(tTbl, "table", "standard_error")
  85.         --Get the first index in the table
  86.         local k = next(tTbl)
  87.        
  88.         while k do
  89.             --Set the index's value to nil
  90.             tTbl[k] = nil
  91.             --Set the index to the next non-nil index. This will be nil if the table is empty, which will end the loop
  92.             k = next(tTbl)
  93.  
  94.         end
  95.  
  96.     end
  97.     --Copies a table (in case you didn't know)
  98.     function copyTable(tTbl)
  99.        
  100.         checkType(tTbl, "table", "standard_error")
  101.         --The table to copy to
  102.         local tCopy = {}
  103.         --Get the first index in the table
  104.         local k = next(tTbl)
  105.  
  106.         while k do
  107.             --Set the index's value in the copy, to its value in the original
  108.             tCopy[k] = tTbl[k]
  109.             --Get the next index after the current one. This will be nil if there are no more indexes, which will end the loop
  110.             k = next(tTbl, k)
  111.  
  112.         end
  113.         --Return the copy, and the original next to it for convenience
  114.         return tCopy, tTbl
  115.        
  116.     end
  117.     --Utility for creating packet tables
  118.     function asPacketData(senderType, senderName, senderID, recipientName, recipientID, dataType, data, tOther)
  119.         --[[
  120.             Packet contains:
  121.                 -what kind of server or client is sending the information
  122.                 -the computer name and ID of the sender
  123.                 -the computer name and ID of the computer intended to receive the packet (the ID is usually used as the sending channel)
  124.                 -the type of data being sent
  125.                 -the data itself
  126.                 -any other information which a program may acknowledge, but the packet API will ignore
  127.         ]]
  128.         local tPacket = {
  129.  
  130.             senderType=senderType,
  131.             senderName=senderName,
  132.             senderID=senderID,
  133.             recipientName=recipientName,
  134.             recipientID=recipientID,
  135.             dataType=dataType,
  136.             data=data
  137.  
  138.         }
  139.         --If tOther is nil, set it to an empty table
  140.         local tOther = tOther or {}
  141.         checkType(tOther, "table", "standard_error")
  142.         --Add all the values in tOther to the packet
  143.         for k, v in pairs(tOther) do
  144.            
  145.             tPacket[k] = v
  146.            
  147.         end
  148.        
  149.         return tPacket
  150.        
  151.     end
  152.     --Utility for creating packet tables to be passed to send as send does not need to know the sender name or ID
  153.     function asPacketDataS(senderType, recipientName, recipientID, dataType, data, tOther)
  154.         --Leave the sender name and ID nil, as send will set it to this computer's name and ID for us
  155.         return upcallL(2, asPacketData,
  156.            
  157.             senderType,
  158.             nil, nil,
  159.             recipientName,
  160.             recipientID,
  161.             dataType,
  162.             data,
  163.             tOther
  164.            
  165.         )
  166.        
  167.     end
  168.     --[[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
  169.     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
  170.     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.
  171.     Otherwise, it will return false. Any values in the packet which are not in the sample packet will be ignored]]
  172.     function checkPacket(tSamplePacket, tPacket)
  173.        
  174.         checkType(tSamplePacket, "table", "standard_error")
  175.         checkType(tPacket, "table", "standard_error")
  176.         --We only care about items that tSamplePacket has in it. If tPacket has an item the tSamplePacket doesn't, we'll ignore it
  177.         for k, v in pairs(tSamplePacket) do
  178.             --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
  179.             if not (tPacket[k] == v or (checkType(v, "function") and v(tPacket[k]))) then
  180.  
  181.                 return false
  182.  
  183.             end
  184.  
  185.         end
  186.  
  187.         return true
  188.  
  189.     end
  190.     --Confirms that a table contains all of the parts of a standard packet
  191.     local function validatePacket(tPacket)
  192.        
  193.         --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
  194.         return checkPacket(asPacketData(
  195.            
  196.             function(mData) return checkType(mData, "string") end,--Sender type
  197.             function(mData) return checkType(mData, "string") end,--Sender name
  198.             function(mData) return checkType(mData, "number") end,--Sender ID
  199.             function(mData) return checkType(mData, "string") end,--Recipient name
  200.             function(mData) return checkType(mData, "number") end,--Recipient ID
  201.             function(mData) return checkType(mData, "string") end--Data type
  202.            
  203.         ), tPacket)
  204.  
  205.     end
  206.     --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
  207.     function asPacket(...)
  208.         --Convert the arguments into a packet table
  209.         local tPacket = upcallL(2, asPacketData, ...)
  210.         --Check it against the standard
  211.         if not validatePacket(tPacket) then
  212.            
  213.             error("asPacket: You are not allowed to generate corrupted packets", 2)
  214.  
  215.         end
  216.         --Serialize the packet as a string to be sent over rednet. If the packet contains a function, an error will be raised
  217.         return upcallL(2, textutils.serialize, tPacket)
  218.  
  219.     end
  220.     --Formats a string as a table of data, handling any error caused by the process and returning them as an errorMessage packet
  221.     function asData(sPacket)
  222.        
  223.         checkType(sPacket, "string", "standard_error")
  224.         --Attempt to format the string as a table of information
  225.         local tPacket = textutils.unserialize(sPacket)
  226.         --If the result is a table, and the table passes as a standard packet, return it
  227.         if checkType(tPacket, "table") and validatePacket(tPacket) then
  228.  
  229.             return tPacket
  230.  
  231.         end
  232.         --Corrupted packet error, will usually be ignored as checkPacket will reject it in most cases
  233.         return asPacketData(
  234.  
  235.             "system",
  236.             "asData",
  237.             -1,
  238.             "none",
  239.             -1,
  240.             "errorMessage",
  241.             "Packet corrupted"
  242.  
  243.         )
  244.  
  245.     end
  246.     --Sends a packet on the channel of the given recipient recipient ID
  247.     function send(sSenderType, sRecipientName, nRecipientID, sDataType, mData)
  248.        
  249.         --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
  250.         rednet.send(nRecipientID,
  251.             upcallL(2, asPacket, sSenderType, (os.getComputerLabel() or "LABEL_NOT_SET"), os.getComputerID(),
  252.                 sRecipientName, nRecipientID, sDataType, mData)
  253.         )
  254.  
  255.     end
  256.     --[[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
  257.     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
  258.     function may return blank packets, even if they do not match the sample packet. This defaults to false]]
  259.     function receive(nTimeout, tSamplePacket)
  260.         --nTimeout may be nil, but otherwise it must be a number
  261.         if nTimeout then
  262.            
  263.             checkType(nTimeout, "number", "standard_error")
  264.            
  265.         end
  266.         --If tSamplePacket is nil, set it to an empty table
  267.         local tSamplePacket = tSamplePacket or {}
  268.         checkType(tSamplePacket, "table", "standard_error")
  269.        
  270.         while true do
  271.             --Rednet may raise a termination error, we will catch this and return it as an error message if this is permitted
  272.             local tMessage = {pcall(rednet.receive, nTimeout)}
  273.             local tPacket
  274.            
  275.             if not tMessage[1] then
  276.                 --Some error occurred, create a packet containing the error message
  277.                 tPacket = asPacketData(
  278.                    
  279.                     "system",
  280.                     "receive",
  281.                     -1,
  282.                     "none",
  283.                     -1,
  284.                     "errorMessage",
  285.                     tMessage[2]
  286.                    
  287.                 )
  288.                
  289.             end
  290.             --If receive timed out, it returned nil. If this is the case, create a blank packet
  291.             if not tMessage[3] then
  292.                
  293.                 tPacket = asPacketData(
  294.                    
  295.                     "system",
  296.                     "receive",
  297.                     -1,
  298.                     "none",
  299.                     -1,
  300.                     "blank"
  301.                    
  302.                 )
  303.                
  304.             end
  305.             --If nothing has gone wrong so far, try to convert the received message into a packet
  306.             if not tPacket then
  307.                
  308.                 tPacket = asData(tMessage[3])
  309.                
  310.             end
  311.             --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.
  312.             --If the packet is blank, we'll return it regardless of whether or not it passes
  313.             if checkPacket(tSamplePacket, tPacket) or tPacket.dataType == "blank" then
  314.                
  315.                 return tPacket
  316.                
  317.             end
  318.            
  319.         end
  320.  
  321.     end
  322.     --Receives a packet, or multi-packet, and places it in the queue
  323.     function queueReceiver(tQueue, tSamplePacket)
  324.        
  325.         checkType(tQueue, "table", "standard_error")
  326.         --If tSamplePacket is nil, set it to an empty table
  327.         local tSamplePacket = tSamplePacket or {}
  328.         checkType(tSamplePacket, "table", "standard_error")
  329.  
  330.         while true do
  331.             --Wait indefinitely for a packet which matches the sample packet
  332.             tPacket = receive(nil, tSamplePacket)
  333.             --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
  334.             --multiMode flag in this table will be set to true
  335.             if checkType(tPacket.data, "table") and tPacket.data.multiMode then
  336.                 --Erase the flag, we don't need it any more
  337.                 tPacket.data.multiMode = nil
  338.                 --Add every piece of data being sent to the queue
  339.                 for k, v in pairs(tPacket.data) do
  340.                    
  341.                     table.insert(tQueue, v)
  342.  
  343.                 end
  344.  
  345.             else
  346.                 --Add the data to the queue
  347.                 table.insert(tQueue, tPacket.data)
  348.  
  349.             end
  350.  
  351.         end
  352.  
  353.     end
  354.     --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
  355.     --received, it will be formatted as a packet, or as a multi-packet if the third function argument is true
  356.     function queueSender(tQueue, tTemplatePacket, bMultiPacketMode)
  357.        
  358.         checkType(tQueue, "table", "standard_error")
  359.         checkType(tTemplatePacket, "table", "standard_error")
  360.         checkType(bMultiPacketMode, "boolean", "standard_error")
  361.         --tTemplatePacket may specify multiple recipients. They will be stored here
  362.         local tRecipients = {}
  363.        
  364.         if tTemplatePacket.recipients and checkType(tTemplatePacket.recipients, "table") then
  365.            
  366.             for k, v in pairs(tTemplatePacket.recipients) do
  367.                 --If v is not a table
  368.                 checkType(v, "table",
  369.                     function(gVar, sExpectedType, sType, nLevel)
  370.                        
  371.                         error(string.format("Error processing entry %s, entry: Expected %s, got %s", tostring(k), sExpectedType, sType), nLevel)
  372.                        
  373.                     end)
  374.                 --If v.recipientName is not a string
  375.                 checkType(v.recipientName, "string",
  376.                     function(gVar, sExpectedType, sType, nLevel)
  377.                        
  378.                         error(string.format("Error processing entry %s, recipientName: Expected %s, got %s", tostring(k), sExpectedType, sType), nLevel)
  379.                        
  380.                     end)
  381.                 --If v.recipientID is not a number
  382.                 checkType(v.recipientID, "number",
  383.                     function(gVar, sExpectedType, sType, nLevel)
  384.                        
  385.                         error(string.format("Error processing entry %s, recipientID: Expected %s, got %s", tostring(k), sExpectedType, sType), nLevel)
  386.                        
  387.                     end)
  388.                
  389.                 table.insert(tRecipients, {recipientName=v.recipientName, recipientID=v.recipientID})
  390.                
  391.             end
  392.            
  393.         else
  394.             --Just treat tTemplatePacket as a regular packet
  395.             table.insert(tRecipients, {recipientName=tTemplatePacket.recipientName, recipientID=tTemplatePacket.recipientID})
  396.            
  397.         end
  398.        
  399.         while true do
  400.             --If there is something in the queue
  401.             if #tQueue > 0 then
  402.                
  403.                 for k, v in pairs(tRecipients) do
  404.                     --In multi-mode we send the entire queue at once as a table, then clear the entire queue at once
  405.                     if bMultiPacketMode then
  406.                        
  407.                         tQueue.multiMode = true
  408.                         upcallL(2, send, tTemplatePacket.senderType, v.recipientName,
  409.                         v.recipientID, tTemplatePacket.dataType, tQueue)
  410.                         clearTable(tQueue)
  411.                        
  412.                     else
  413.                         --In single-mode, we clear one entry at a time, sending its value as the packet data
  414.                         upcallL(2, send, tTemplatePacket.senderType, v.recipientName,
  415.                         v.recipientID, tTemplatePacket.dataType, table.remove(tQueue, 1))
  416.                        
  417.                     end
  418.                    
  419.                 end
  420.                
  421.             end
  422.             --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
  423.             pcall(sleep, 0)
  424.  
  425.         end
  426.  
  427.     end
  428.     --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
  429.     function packetListener(func, tSamplePacket)
  430.        
  431.         checkType(func, "function", "standard_error")
  432.         --The queue to receive our packets in
  433.         local tQueue = {}
  434.        
  435.         parallel.waitForAny(
  436.            
  437.             function()
  438.                
  439.                 upcallL(2, queueReceiver, tQueue, tSamplePacket)
  440.  
  441.             end,
  442.             function()
  443.                
  444.                 while true do
  445.                    
  446.                     if #tQueue > 0 then
  447.                         --Empty out the queue
  448.                         while #tQueue > 0 do
  449.                             --Call the function, passing it the value being removed from the queue. If the function returns false, end the loop
  450.                             if not func(table.remove(tQueue, 1)) then
  451.  
  452.                                 return
  453.  
  454.                             end
  455.  
  456.                         end
  457.  
  458.                     end
  459.  
  460.                     yield()
  461.  
  462.                 end
  463.  
  464.             end
  465.  
  466.         )
  467.  
  468.     end
  469.     --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
  470.     function packetSender(func, tTemplatePacket, bMultiPacketMode)
  471.        
  472.         checkType(func, "function", "standard_error")
  473.         --The queue to send out packets from
  474.         local tQueue = {}
  475.  
  476.         parallel.waitForAny(
  477.  
  478.             function()
  479.                
  480.                 upcallL(2, queueSender, tQueue, tTemplatePacket, bMultiPacketMode)
  481.  
  482.             end,
  483.             function()
  484.                 --The running loop must happen in this function
  485.                 func(tQueue)
  486.  
  487.             end
  488.  
  489.         )
  490.  
  491.     end
  492.     --END PACKET API
  493.     --[[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
  494.     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
  495.     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,
  496.     and a modified version of the multishll which doesn't strip mouse click events: pastebin:kjKrUP41]]
  497.     print("TRoR Term Redirect over Rednet 1.0")
  498.     --Kills the current running shell, allowing the given function to run in the top level coroutine
  499.     --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/
  500.     function killShell(func, ...)
  501.        
  502.         checkType(func, "function", "standard_error")
  503.         local tArgs = {...}
  504.         --Store the original versions of the functions we'll be overriding
  505.         local nativeShutdown = os.shutdown
  506.         local nativeYield = coroutine.yield
  507.         --This is called just before the bios exits, after the shell has died
  508.         os.shutdown = function()
  509.             --In theory, if this is true then a program (such as a custom operating system) tried to call shutdown. We need the shell to be completely dead before we can proceed
  510.             if shell or multishell then
  511.                
  512.                 error("Shell death", 0)
  513.                
  514.             end
  515.             --We're ready to move on, set these variables back to their native values for normal operation
  516.             os.shutdown = nativeShutdown
  517.             coroutine.yield = nativeYield
  518.             --Clean out any terms that anyone has set up
  519.             term.redirect(term.native())
  520.             --Make sure these are dead and gone
  521.             multishell = nil
  522.             shell = nil
  523.             --This has to be reset or it won't run again
  524.             os.unloadAPI("rom/apis/rednet")
  525.             os.loadAPI("rom/apis/rednet")
  526.             --Run the function
  527.             ok, err = pcall(func, unpack(tArgs))
  528.            
  529.             if not ok then
  530.                 --Attempt to show the user any errors which may have occurred
  531.                 pcall(function()
  532.                    
  533.                     term.redirect(term.native())
  534.                     printError(err)
  535.                     print("Press any key to continue...")
  536.                     os.pullEventRaw("key")
  537.                    
  538.                 end)
  539.                    
  540.             end
  541.             --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
  542.             os.shutdown()
  543.            
  544.         end
  545.         --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
  546.         --to the bios, where our overridden shutdown function will be called
  547.         coroutine.yield = function(...) error("Shell death", 0) end
  548.         --Begin the chaos
  549.         error("Shell death", 0)
  550.        
  551.     end
  552.     --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
  553.     local function replaceStartup(tArgs)
  554.         --Collection of commands for the new shell to run
  555.         local tConverted = {}
  556.         --A single command being formed
  557.         local tCommand = {}
  558.         --Adds a command to the collection and clears it from the working variable
  559.         local function addCommand()
  560.            
  561.             if #tCommand > 0 then
  562.                
  563.                 table.insert(tConverted, tCommand)
  564.                 tCommand = {}
  565.                
  566.             end
  567.            
  568.         end
  569.        
  570.         for k, v in pairs(tArgs) do
  571.             --If it's a table, convert everything in it to a string.
  572.             if checkType(v, "table") then
  573.                 --Add whatever command was previously being built; we're now working on a new command
  574.                 addCommand()
  575.                
  576.                 for ke, va in pairs(v) do
  577.                    
  578.                     table.insert(tCommand, tostring(va))
  579.                    
  580.                 end
  581.                
  582.             else
  583.                
  584.                 addCommand()
  585.                 --Convert whatever else v is into a string, we'll try to run it anyway
  586.                 table.insert(tCommand, tostring(v))
  587.                
  588.             end
  589.             --Add whatever we've got left
  590.             addCommand()
  591.            
  592.         end
  593.         --If there are no commands to run, we don't need to override the startup
  594.         if #tConverted < 1 then
  595.            
  596.             return
  597.            
  598.         end
  599.         --In case the default name already exists for some reason, we'll find a name which doesn't exist
  600.         local sStartupRelocation
  601.        
  602.         if fs.exists("startup") then
  603.            
  604.             local sName = ".startup"
  605.             local nAttemptCount = 1
  606.             local sStartupRelocation = sName
  607.             --We'll check for ".startup" first, then ".startup2", ".startup3", etc.
  608.             while fs.exists(sStartupRelocation) do
  609.                
  610.                 nAttemptCount = nAttemptCount + 1
  611.                 --".startup[attempt count]"
  612.                 sStartupRelocation = sName .. nAttemptCount
  613.                
  614.             end
  615.            
  616.         end
  617.        
  618.         local sStartup = string.format(
  619.            
  620.             [[
  621.                 --The serialized converted commands, these will just be a normal table when it is loaded
  622.                 local tCommands = %s
  623.                 --The startup location, or nil if there was no startup
  624.                 local sStartupLocation = %s
  625.                 local bRunStartup
  626.                 --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
  627.                 fs.delete("startup")
  628.                 --If there was a startup here before, move it back in place
  629.                 if sStartupLocation then
  630.                    
  631.                     fs.move(sStartupLocation, "startup")
  632.                    
  633.                 end
  634.                
  635.                 if multishell then
  636.                     --On the multishell, we run everything in its own tab all at once
  637.                     for k, v in pairs(tCommands) do
  638.                        
  639.                         --The last command, or only command if there is only one, will be run in the main shell tab
  640.                         if next(tCommands, k) then
  641.                            
  642.                             shell.openTab(unpack(v))
  643.                            
  644.                         else
  645.                            
  646.                             shell.run(unpack(v))
  647.                            
  648.                         end
  649.                        
  650.                     end
  651.                    
  652.                 else
  653.                     --On the shell, we run everything in the order it was provided, one at a time
  654.                     for k, v in pairs(tCommands) do
  655.                        
  656.                         shell.run(unpack(v))
  657.                        
  658.                     end
  659.                    
  660.                 end
  661.                
  662.             ]],
  663.            
  664.             textutils.serialize(tConverted),
  665.             tostring(sStartupRelocation)
  666.            
  667.         )
  668.         --If there is already a startup, move it out of the way
  669.         if sStartupRelocation then
  670.            
  671.             fs.move("startup", sStartupRelocation)
  672.            
  673.         end
  674.         --Write our new startup in the actual startup file
  675.         local tFile = fs.open("startup", "w")
  676.         tFile.write(sStartup)
  677.         tFile.close()
  678.        
  679.     end
  680.     --Clears the screen and prints a message in yellow or white text, depending on if the term is color or not respectively
  681.     local function printStartupMessage(sMessage)
  682.         --User yellow for advanced computers, and white for regular computers
  683.         if term.isColor() then
  684.            
  685.             term.setTextColor(colors.yellow)
  686.            
  687.         else
  688.            
  689.             term.setTextColor(colors.white)
  690.            
  691.         end
  692.         --Background should always be black
  693.         term.setBackgroundColor(colors.black)
  694.         --Clear the screen
  695.         term.clear()
  696.         term.setCursorPos(1, 1)
  697.         print(sMessage)
  698.         term.setTextColor(colors.white)
  699.        
  700.     end
  701.     --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
  702.     function runShell(...)
  703.        
  704.         replaceStartup({...})
  705.         --Display the running mode
  706.         printStartupMessage("Regular shell")
  707.         --Start the shell and rednet coroutines
  708.         parallel.waitForAny(
  709.            
  710.             function()
  711.                
  712.                 os.run({}, "rom/programs/shell")
  713.                
  714.             end,
  715.             function()
  716.                
  717.                 rednet.run()
  718.                
  719.             end
  720.            
  721.         )
  722.        
  723.     end
  724.     --Designed to be passed to killShell as the func argument. Runs the multishell. This program will break mouse click modification
  725.     function runMultishell(...)
  726.         --Same as runShell, but running the multishell program instead
  727.         replaceStartup({...})
  728.         printStartupMessage("Regular multishell")
  729.        
  730.         parallel.waitForAny(
  731.            
  732.             function()
  733.                
  734.                 os.run({}, "rom/programs/advanced/multishell")
  735.                
  736.             end,
  737.             function()
  738.                
  739.                 rednet.run()
  740.                
  741.             end
  742.            
  743.         )
  744.        
  745.     end
  746.     --Runs a modified version of the multishell which doesn't strip the flags off of mouse click events.
  747.     function clickSafeMultishell()
  748.         --The new program will be stored in this string
  749.         local sNewMultiShell = ""
  750.         --Simple convenience function for adding to the program
  751.         local function add(sText) sNewMultiShell = sNewMultiShell.."\n".. sText end
  752.         --This goes after the function definitions, before the multishell starts running
  753.         local sPreStart = [[
  754.             multishell.isClickSafe = true
  755.             local function copyEvent(tEventData)
  756.                 local tEventCopy = {}
  757.                 local k = next(tEventData)
  758.                 while k do
  759.                     tEventCopy[k] = tEventData[k]
  760.                     k = next(tEventData, k)
  761.                 end
  762.                 return tEventCopy
  763.             end
  764.         ]]
  765.         --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
  766.         --copies the whole event, modifies it as needed, and then passes the copy with all its parts intact to the process
  767.         local sClickEvent = [[
  768.             -- Click event
  769.             local button, x, y = tEventData[2], tEventData[3], tEventData[4]
  770.             if bShowMenu and y == 1 then
  771.                 -- Switch process
  772.                 local tabStart = 1
  773.                 for n=1,#tProcesses do
  774.                     tabEnd = tabStart + string.len( tProcesses[n].sTitle ) + 1
  775.                     if x >= tabStart and x <= tabEnd then
  776.                         selectProcess( n )
  777.                         redrawMenu()
  778.                         break
  779.                     end
  780.                     tabStart = tabEnd + 1
  781.                 end
  782.             else
  783.                 -- Passthrough to current process
  784.                 tEventCopy = copyEvent(tEventData)
  785.                 tEventCopy[4] = (bShowMenu and y-1) or y
  786.                 resumeProcess( nCurrentProcess, table.unpack(tEventCopy) )
  787.                 if cullProcess( nCurrentProcess ) then
  788.                     setMenuVisible( #tProcesses >= 2 )
  789.                     redrawMenu()
  790.                 end
  791.             end
  792.  
  793.         elseif sEvent == "mouse_drag" or sEvent == "mouse_up" or sEvent == "mouse_scroll" then
  794.             -- Other mouse event
  795.             local p1, x, y = tEventData[2], tEventData[3], tEventData[4]
  796.             if not (bShowMenu and y == 1) then
  797.                 -- Passthrough to current process
  798.                 tEventCopy = copyEvent(tEventData)
  799.                 tEventCopy[4] = (bShowMenu and y-1) or y
  800.                 resumeProcess( nCurrentProcess, table.unpack(tEventCopy) )
  801.                 if cullProcess( nCurrentProcess ) then
  802.                     setMenuVisible( #tProcesses >= 2 )
  803.                     redrawMenu()
  804.                 end
  805.             end
  806.  
  807.         ]]
  808.         --This is basically a copy of os.run, except in runs strings instead of files
  809.         local function run(tEnv, sFunc, sName, ...)
  810.             --Prints an error message if it isn't nil and isn't an empty string
  811.             local function perr(sErr) if sErr and sErr ~= "" then printError(sErr) end end
  812.             local tEnv_ = tEnv
  813.             --Use the global environment as our index
  814.             setmetatable(tEnv_, {__index=_G})
  815.             --Attempt to load the string
  816.             local func, err = load(sFunc, sName, "t", tEnv_)
  817.            
  818.             if func then
  819.                 --If it loaded, run it
  820.                 ok, err = pcall(func, ...)
  821.                
  822.                 if not ok then
  823.                     --Print out anything that goes wrong and return false
  824.                     perr(err)
  825.                    
  826.                     return false
  827.                    
  828.                 end
  829.                 --If everything went okay, return true
  830.                 return true
  831.                
  832.             end
  833.            
  834.             perr(err)
  835.            
  836.             return false
  837.            
  838.         end
  839.        
  840.         local bAddLines = true
  841.         local tFile = fs.open("rom/programs/advanced/multishell", "r")
  842.         --This can only happen if the function is used on a non-advanced computer
  843.         checkType(tFile, "table", function(gVar, gExpectedType, gType, nLevel) error("Multishell not found", nLevel) end, 1)
  844.        
  845.         repeat
  846.            
  847.             local sLine = tFile.readLine()
  848.             --This is the start of the running loop
  849.             if sLine == "-- Begin" then
  850.                
  851.                 add(sPreStart)
  852.                 add(sLine)
  853.                 --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
  854.             elseif sLine == "    elseif sEvent == \"mouse_click\" then" then
  855.                
  856.                 bAddLines = false
  857.                 add(sLine)
  858.                 add(sClickEvent)
  859.                 --This is the end of the click event handler. Start adding lines again
  860.             elseif sLine == "    else" then
  861.                
  862.                 bAddLines = true
  863.                 add(sLine)
  864.                
  865.             else
  866.                 --If we're not in the click event handler portion, add the line
  867.                 if bAddLines and sLine ~= nil then add(sLine) end
  868.                
  869.             end
  870.             --Until there are no more lines to read
  871.         until sLine == nil
  872.        
  873.         return run({}, sNewMultiShell, "ModdedMultiShell")
  874.        
  875.     end
  876.     --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
  877.     --click modification
  878.     function runClickSafeMultishell(...)
  879.        
  880.         replaceStartup({...})
  881.         printStartupMessage("Click safe multishell")
  882.        
  883.         parallel.waitForAny(
  884.            
  885.             function()
  886.                
  887.                 clickSafeMultishell()
  888.                
  889.             end,
  890.             function()
  891.                
  892.                 rednet.run()
  893.                
  894.             end
  895.            
  896.         )
  897.        
  898.     end
  899.     --Receives terminal commands from a remote computer and draws them on this computers screen
  900.     function receiveScreen(tSamplePacket)
  901.        
  902.         local tSamplePacket = tSamplePacket or {}
  903.         checkType(tSamplePacket, "table", "standard_error")
  904.         --Create a new packet listener
  905.         upcallL(2, packetListener, function(tCommand)
  906.                 --Make sure this is at least an empty table
  907.                 tCommand.args = tCommand.args or {}
  908.                 --The sender terminated, so we will too
  909.                 if tCommand.command == "exit" then
  910.                    
  911.                     return false
  912.                        
  913.                 end
  914.                 --The term commands are the names of the function, so get the function and call it with the args table as its arguments
  915.                 term[tCommand.command](unpack(tCommand.args))
  916.                 --Tell the packet listener to keep running
  917.                 return true
  918.                
  919.             end,
  920.             asPacketData(
  921.                 --We can use all the tests from the sample packet, except the data type must be "termCommand", and the data must be a table
  922.                 tSamplePacket.senderType,
  923.                 tSamplePacket.senderName,
  924.                 tSamplePacket.senderID,
  925.                 tSamplePacket.recipientName,
  926.                 tSamplePacket.recipientID,
  927.                 "termCommand",
  928.                 function(mData) return checkType(mData, "table") end
  929.                
  930.             )
  931.         )
  932.        
  933.     end
  934.     --Sends terminal commands over rednet to be drawn by a remote computer
  935.     function sendScreen(func, tTemplatePacket, bMultiPacketMode)
  936.        
  937.         checkType(func, "function", "standard_error")
  938.         checkType(tTemplatePacket, "table", "standard_error")
  939.        
  940.         upcallL(2, packetSender, function(tQueue)
  941.                
  942.                 local tNewTerm = {}
  943.                 --These functions do not need to be sent
  944.                 local tReturnOnly = {
  945.                    
  946.                     ["getCursorPos"] = true,
  947.                     ["isColor"] = true,
  948.                     ["isColour"] = true,
  949.                     ["getSize"] = true,
  950.                     ["getTextColor"] = true,
  951.                     ["getBackgroundColor"] = true,
  952.                     ["getTextColour"] = true,
  953.                     ["getBackgroundColour"] = true,
  954.                    
  955.                 }
  956.                
  957.                 local tParentTerm
  958.                
  959.                 for k, v in pairs(term.native()) do
  960.                    
  961.                     local sName = k
  962.                    
  963.                     tNewTerm[k] = (tReturnOnly[k] and function()
  964.                         --Return only functions are just called and their return values are returned
  965.                         return tParentTerm[sName]()
  966.                        
  967.                     end)
  968.                         or function(...)
  969.                             --Other functions are called, but not returned, and their name and arguments are sent as a command to any receiving computer
  970.                             tParentTerm[sName](...)
  971.                             table.insert(tQueue, {command=sName, args={...}})
  972.                            
  973.                         end
  974.                    
  975.                 end
  976.                
  977.                 tParentTerm = term.redirect(tNewTerm)
  978.                 --Resets the term to what it was before we got to it
  979.                 term.resetTerm = function()
  980.                    
  981.                     term.redirect(tParentTerm)
  982.                     term.resetTerm = nil
  983.                    
  984.                 end
  985.                 --Set the remote term(s) cursor position
  986.                 term.setCursorPos(term.getCursorPos())
  987.                 --We'll let the function error, but not yet
  988.                 ok, err = pcall(func, tQueue)
  989.                 --Send the exit command
  990.                 table.insert(tQueue, {command="exit"})
  991.                 --Wait for any remain queued commands to be sent
  992.                 while #tQueue > 0 do yield() end
  993.                 --Reset the term if someone hasn't already
  994.                 if term.resetTerm then
  995.                    
  996.                     term.resetTerm()
  997.                    
  998.                 end
  999.                 --Now let the error be raised
  1000.                 if not ok then
  1001.                    
  1002.                     error(err, 0)
  1003.                    
  1004.                 end
  1005.                
  1006.             end,
  1007.             asPacketDataS(
  1008.                
  1009.                 tTemplatePacket.senderType,
  1010.                 tTemplatePacket.recipientName,
  1011.                 tTemplatePacket.recipientID,
  1012.                 "termCommand"
  1013.                
  1014.             ), bMultiPacketMode
  1015.         )
  1016.        
  1017.     end
  1018.     --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
  1019.     --provided. These may return the event as is, modify it as needed, or return nil to cancel the event
  1020.     function receiveEvents(func, tSamplePacket, tEventModifiers)
  1021.         --Default function just queues any even it receives
  1022.         local func = func or function(tEvent) os.queueEvent(unpack(tEvent)) return true end
  1023.         local tSamplePacket = tSamplePacket or {}
  1024.         local tEventModifiers = tEventModifiers or {}
  1025.         checkType(func, "function", "standard_error")
  1026.         checkType(tSamplePacket, "table", "standard_error")
  1027.         checkType(tEventModifiers, "table", "standard_error")
  1028.        
  1029.         for k, v in pairs(tEventModifiers) do
  1030.            
  1031.             checkType(v, "function", function(gVar, gExpectedType, gType, nLevel) error("All elements in the tEventModifiers table must be functions", nLevel) end)
  1032.            
  1033.         end
  1034.        
  1035.         local nativePullEvent = os.pullEventRaw
  1036.        
  1037.         os.pullEventRaw = function(...)
  1038.            
  1039.             while true do
  1040.                 --Get a copy of the event
  1041.                 local tEvent = copyTable({nativePullEvent(...)})
  1042.                 --If there is a modification function, set the event to its call with the first copy as its arguments
  1043.                 if tEventModifiers[tEvent[1]] then
  1044.                    
  1045.                     tEvent = tEventModifiers[tEvent[1]](tEvent)
  1046.                    
  1047.                 end
  1048.                
  1049.                 --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
  1050.                 if checkType(tEvent, "table") then
  1051.                    
  1052.                     return unpack(tEvent)
  1053.                    
  1054.                 end
  1055.                
  1056.             end
  1057.            
  1058.         end
  1059.         --Resets os.pullEventRaw to what it was before we got to it
  1060.         os.resetOsTable = function()
  1061.            
  1062.             os.pullEventRaw = nativePullEvent
  1063.             os.resetOsTable = nil
  1064.            
  1065.         end
  1066.        
  1067.         ok, err = nil
  1068.        
  1069.         upcallL(2, packetListener, function(tEvent)
  1070.                
  1071.                 ok, err = pcall(func, tEvent)
  1072.                
  1073.                 if ok then
  1074.                    
  1075.                     return err
  1076.                    
  1077.                 end
  1078.                
  1079.                 return false
  1080.                
  1081.             end,
  1082.             asPacketData(
  1083.                
  1084.                 tSamplePacket.senderType,
  1085.                 tSamplePacket.senderName,
  1086.                 tSamplePacket.senderID,
  1087.                 tSamplePacket.recipientName,
  1088.                 tSamplePacket.recipientID,
  1089.                 "remoteEvent",
  1090.                 function(mData) return checkType(mData, "table") end
  1091.                
  1092.             )
  1093.         )
  1094.         --Reset the os table if someone hasn't already
  1095.         if os.resetOsTable then
  1096.            
  1097.             os.resetOsTable()
  1098.            
  1099.         end
  1100.        
  1101.         if not ok then
  1102.            
  1103.             error(err, 0)
  1104.            
  1105.         end
  1106.        
  1107.     end
  1108.     --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
  1109.     --provided. These may return the event as is, modify it as needed, or return nil to cancel the event
  1110.     function sendEvents(tTemplatePacket, tEventModifiers, bMultiPacketMode)
  1111.        
  1112.         tEventModifiers = tEventModifiers or {}
  1113.         checkType(tTemplatePacket, "table", "standard_error")
  1114.         checkType(tEventModifiers, "table", "standard_error")
  1115.        
  1116.         upcallL(2, packetSender, function(tQueue)
  1117.                
  1118.                 while true do
  1119.                    
  1120.                     local tEvent = copyTable({os.pullEventRaw()})
  1121.                    
  1122.                     if tEventModifiers[tEvent[1]] then
  1123.                        
  1124.                         tEvent = tEventModifiers[tEvent[1]](tEvent)
  1125.                        
  1126.                     end
  1127.                    
  1128.                     if checkType(tEvent, "table") then
  1129.                        
  1130.                         table.insert(tQueue, tEvent)
  1131.                        
  1132.                     end
  1133.                    
  1134.                 end
  1135.                
  1136.             end,
  1137.             asPacketDataS(
  1138.                
  1139.                 tTemplatePacket.senderType,
  1140.                 tTemplatePacket.recipientName,
  1141.                 tTemplatePacket.recipientID,
  1142.                 "remoteEvent"
  1143.                
  1144.             ), bMultiPacketMode
  1145.         )
  1146.        
  1147.     end
  1148.     --Tests if an event table contains the EventModified flag
  1149.     local function eventModified(tEvent)
  1150.        
  1151.         for k, v in pairs(tEvent) do
  1152.            
  1153.             if v == "EventModified" then
  1154.                
  1155.                 return true
  1156.                
  1157.             end
  1158.            
  1159.         end
  1160.        
  1161.         return false
  1162.        
  1163.     end
  1164.     --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
  1165.     --it processes void. This may be used as an event modifier
  1166.     function eventVoider(tEvent)
  1167.        
  1168.         if eventModified(tEvent) then
  1169.            
  1170.             return tEvent
  1171.            
  1172.         end
  1173.        
  1174.     end
  1175.     --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
  1176.     function createEventSwapper(sNewName, ...)
  1177.        
  1178.         local tArgs = {...}
  1179.        
  1180.         return function(tEvent)
  1181.            
  1182.             if not eventModified(tEvent) then
  1183.                
  1184.                 tEvent[1] = sNewName
  1185.                
  1186.                 for k, v in pairs(tArgs) do
  1187.                    
  1188.                     table.insert(tEvent, v)
  1189.                    
  1190.                 end
  1191.                
  1192.             end
  1193.            
  1194.             return tEvent
  1195.            
  1196.         end
  1197.        
  1198.     end
  1199.     --END TRoR API
  1200.     for k, v in pairs(rs.getSides()) do
  1201.        
  1202.         pcall(rednet.open, v)
  1203.        
  1204.     end
  1205.  
  1206.     if not rednet.isOpen() then
  1207.        
  1208.         error("No modem connected, please connect a modem to this computer")
  1209.        
  1210.     end
  1211.  
  1212.     if not os.getComputerLabel() then
  1213.        
  1214.         error("Computer label not set")
  1215.        
  1216.     end
  1217.  
  1218.     tArgs = {}
  1219.     sUsages = [[
  1220. Usage:
  1221.     teachutils -help [flag name]
  1222.     teachutils -student
  1223.     teachutils -broadScreen -all/(-names [student name(s)])
  1224.     teachutils -sync -all/(-names [student name(s)] -args) (-allowedit)
  1225.     teachutils -setEditable true/false
  1226.     teachutils -force -all/(-names [student name(s)] -args) -runProgram [name] ([args])/-shutdown/-reboot/-terminate
  1227.     teachutils -list
  1228.     teachutils -sendMessage -all/(-names [student name(s)] -args) -args [message]
  1229. ]]
  1230.     --teachutils -FTP -all/(-names [student name(s)] -args) [flag: see -help] ([args]...)
  1231.     tHelp = {
  1232.        
  1233.         ["-help"] = "displays information about the specified flag",--Asking what a flag does as you use that flag
  1234.         ["-student"] = "runs student mode, this program can not be terminated by the host computer",
  1235.         ["-broadScreen"] = "causes all student computers to display a realtime, uneditable version of your screen, also clears your screen",
  1236.         ["-sync"] = "shows you the target student screen(s), you can control it from your terminal or allow them to from theirs",
  1237.         ["-force"] = "forces the target student computer(s) to take the speicified action",
  1238.         ["-list"] = "shows a list of all running, in range student computers",
  1239.         --[[["-FTP"] = "performs the specified FTP action, all writes will overwrite any existing files, transfering multiple files of the "..
  1240.         "same name will cause the files to be broken up into directories named after the computer they were transfered from, "..
  1241.         "not specifying a desination name for a transfer will result in the FTP using the origin name, -delete [file name] deletes "..
  1242.         "a file/directory on the specified student computer(s), -mkdir [file name] creates a new directory on the specified student computer(s), "..
  1243.         "-upload [local file name] ([remote file name]) uploads a file/directory to the specified student computer(s), "..
  1244.         "-download [remote file name] ([local file name]) downloads a file/directory from the specified student computer(s), "..
  1245.         "-list ([file name]) lists the current working dir, or the specified directory on the specified student computer(s), "..
  1246.         "-cd [file name] changes the shell's current working directory on the specified student computer(s)",]]
  1247.         ["-sendMessage"] = "sends a message to the target student computer(s)",
  1248.         ["-setEditable"] = "sets whether or not the current computer can be controlled by the user"
  1249.        
  1250.     }
  1251.     bRun = true
  1252.     tPacket = {}
  1253.     tTeacher = {}
  1254.     tSendableFuncs = {
  1255.        
  1256.         rollCallResponder=[[
  1257.            
  1258.             send(
  1259.                
  1260.                 "student",
  1261.                 tTeacher.name,
  1262.                 tTeacher.ID,
  1263.                 "string",
  1264.                 "rollReponse"
  1265.                
  1266.             )
  1267.            
  1268.         ]],
  1269.        
  1270.         messagePrinter=[[
  1271.            
  1272.             printMessage("%s", "%s", %d, %d)
  1273.            
  1274.         ]],
  1275.        
  1276.         screenReceiver=[[
  1277.            
  1278.             printMessage("Screen Sharer", "Starting screen share with "..tTeacher.name.."...", colors.green, colors.lightBlue)
  1279.             sleep(2)
  1280.             clear()
  1281.            
  1282.             receiveScreen(asPacketData(
  1283.                
  1284.                 "teacher",
  1285.                 nil,
  1286.                 tTeacher.ID,
  1287.                 nil,
  1288.                 rednet.CHANNEL_BROADCAST
  1289.                
  1290.             ))
  1291.            
  1292.             if term.resetTerm then
  1293.                
  1294.                 term.resetTerm()
  1295.                
  1296.             end
  1297.            
  1298.             clear()
  1299.             term.setCursorBlink(false)
  1300.             printMessage("Screen Sharer", "Screen share complete", colors.green, colors.lightBlue)
  1301.            
  1302.         ]],
  1303.        
  1304.         syncClient=[[
  1305.            
  1306.             printMessage("Screen Sharer", "Starting screen share with "..tTeacher.name.."...", colors.green, colors.lightBlue)
  1307.             sleep(4)
  1308.             clear()
  1309.            
  1310.             local bVoid = true
  1311.            
  1312.             local function cVoidEvent(tEvent)
  1313.                
  1314.                 if bVoid
  1315.                    
  1316.                     return eventVoider(tEvent)
  1317.                    
  1318.                 end
  1319.                
  1320.                 return tEvent
  1321.                
  1322.             end
  1323.            
  1324.             parallel.waitForAny(
  1325.                
  1326.                 function()
  1327.                    
  1328.                     receiveEvents(
  1329.                        
  1330.                         function(tEvent)
  1331.                            
  1332.                             if tEvent[1] == "void_events" then
  1333.                                
  1334.                                 bVoid = tEvent[2]
  1335.                                
  1336.                             end
  1337.                            
  1338.                             os.queueEvent(unpack(tEvent))
  1339.                            
  1340.                         end,
  1341.                         asPacketData(
  1342.                            
  1343.                             "teacher",
  1344.                             nil,
  1345.                             teacher.ID
  1346.                            
  1347.                         ),
  1348.                         {
  1349.                            
  1350.                             ["remote_terminate"]=createEventSwapper("terminate", "EventModified"),
  1351.                             ["remote_key"]=createEventSwapper("key", "EventModified"),
  1352.                             ["remote_char"]=createEventSwapper("char", "EventModified"),
  1353.                             ["remote_key_up"]=createEventSwapper("key_up", "EventModified"),
  1354.                             ["remote_mouse_click"]=createEventSwapper("mouse_click", "EventModified"),
  1355.                             ["remote_mouse_up"]=createEventSwapper("mouse_up", "EventModified"),
  1356.                             ["remote_mouse_scroll"]=createEventSwapper("mouse_scroll", "EventModified"),
  1357.                             ["remote_mouse_drag"]=createEventSwapper("mouse_drag", "EventModified"),
  1358.                             ["terminate"]=cVoidEvent,
  1359.                             ["key"]=cVoidEvent,
  1360.                             ["char"]=.cVoidEvent,
  1361.                             ["key_up"]=cVoidEvent,
  1362.                             ["mouse_click"]=cVoidEvent,
  1363.                             ["mouse_up"]=cVoidEvent,
  1364.                             ["mouse_scroll"]=cVoidEvent,
  1365.                             ["mouse_drag"]=cVoidEvent
  1366.                            
  1367.                         }
  1368.                        
  1369.                     )
  1370.                    
  1371.                 end,
  1372.                 function()
  1373.                    
  1374.                     sendScreen(function()
  1375.                        
  1376.                         if term.isColor() then
  1377.                            
  1378.                             clickSafeMultishell()
  1379.                        
  1380.                         else
  1381.                            
  1382.                             shell.run("shell")
  1383.                            
  1384.                         end
  1385.                        
  1386.                     end,
  1387.                     tror.asPacketDataS(
  1388.                        
  1389.                         "student",
  1390.                         teacher.name,
  1391.                         teacher.ID
  1392.                        
  1393.                     ), true
  1394.                    
  1395.                 end
  1396.                
  1397.             )
  1398.            
  1399.         ]],
  1400.        
  1401.         ["-shutdown@force"]=[[
  1402.            
  1403.             printMessage("System", "Shutdown ordered by " .. tTeacher.name .. ", goodbye.", colors.green, colors.red)
  1404.             sleep(2)
  1405.             os.shutdown()
  1406.            
  1407.         ]],
  1408.        
  1409.         ["-reboot@force"]=[[
  1410.            
  1411.             printMessage("System", "Reboot ordered by " .. tTeacher.name .. ", goodbye.", colors.green, colors.red)
  1412.             sleep(2)
  1413.             os.reboot()
  1414.            
  1415.         ]],
  1416.        
  1417.         ["-terminate@force"]=[[
  1418.            
  1419.             printMessage("System", "Exit ordered by " .. tTeacher.name .. ", goodbye.", colors.green, colors.red)
  1420.             sleep(2)
  1421.             clear()
  1422.             bRun = false
  1423.            
  1424.         ]],
  1425.        
  1426.         ["-runProgram@force"]=[[
  1427.            
  1428.             tRun = textutils.unserialize("%s")
  1429.             printMessage("System", "Running %s, as ordered by " .. tTeacher.name .. "...", colors.green, colors.blue)
  1430.             sleep(2)
  1431.             shell.run(unpack(tRun))
  1432.             printMessage("System", "Execution complete.", colors.green, colors.blue)
  1433.            
  1434.         ]]
  1435.        
  1436.     }
  1437.    
  1438.     function printUsage()
  1439.        
  1440.         setTextColor(colors.lime)
  1441.         --print(sUsages)
  1442.         textutils.pagedPrint(sUsages, ({term.getSize()})[2] - 2)
  1443.         setTextColor(colors.white)
  1444.        
  1445.     end
  1446.    
  1447.     function studentArgumentsValid(tArgs)
  1448.        
  1449.         return #(tArgs.students) > 0 or tArgs.broadcast
  1450.        
  1451.     end
  1452.    
  1453.     function clear()
  1454.        
  1455.         term.clear()
  1456.         term.setCursorPos(1, 1)
  1457.        
  1458.     end
  1459.    
  1460.     function setTextColor(nColor)
  1461.        
  1462.         if term.isColor() then
  1463.            
  1464.             term.setTextColor(nColor)
  1465.            
  1466.             return true
  1467.            
  1468.         end
  1469.        
  1470.         return false
  1471.        
  1472.     end
  1473.    
  1474.     function setBackgroundColor(nColor)
  1475.        
  1476.         if term.isColor() then
  1477.            
  1478.             term.setBackgroundColor(nColor)
  1479.            
  1480.             return true
  1481.            
  1482.         end
  1483.        
  1484.         return false
  1485.        
  1486.     end
  1487.    
  1488.     function printMessage(sSender, sMessage, nSenderColor, nMessageColor)
  1489.        
  1490.         nCurrentTextColor = term.getTextColor()
  1491.         setTextColor(nSenderColor)
  1492.         write(sSender .. ">")
  1493.         setTextColor(nMessageColor)
  1494.         print(sMessage)
  1495.         setTextColor(nCurrentTextColor)
  1496.        
  1497.     end
  1498.    
  1499.     function sendFunction(sName, nTarget, sFunction)
  1500.        
  1501.         send("teacher",
  1502.             sName,
  1503.             nTarget,
  1504.             "function",
  1505.             sFunction)
  1506.        
  1507.     end
  1508.    
  1509.     function rollCall()
  1510.        
  1511.         tPacket = {}
  1512.         tPackets = {}
  1513.         sendFunction("all", rednet.CHANNEL_BROADCAST, tSendableFuncs.rollCallResponder)
  1514.        
  1515.         while tPacket.dataType ~= "blank" do
  1516.            
  1517.             tPacket = receive(.5)
  1518.            
  1519.             if tPacket.dataType ~= "blank" then
  1520.                
  1521.                 tCopy = copyTable(tPacket)
  1522.                
  1523.                 table.insert(tPackets, tCopy)
  1524.                
  1525.             end
  1526.            
  1527.         end
  1528.        
  1529.         tStudents = {}
  1530.        
  1531.         for k, v in pairs(tPackets) do
  1532.            
  1533.             if checkPacket(asPacketData("student",
  1534.                 nil, nil, nil,
  1535.                 os.getComputerID(),
  1536.                 "string",
  1537.                 "rollReponse"), v) then
  1538.                
  1539.                 table.insert(tStudents, {name=v.senderName, ID=v.senderID})
  1540.                
  1541.             end
  1542.            
  1543.         end
  1544.        
  1545.         return tStudents
  1546.        
  1547.     end
  1548.    
  1549.     function parseArgs(tArgs)
  1550.        
  1551.         local tProcessedArgs = {superFlag=tostring(tArgs[1]), broadcast=false, superArgs={}, students={}, hasArgs=false, args={}}
  1552.        
  1553.         local tStudents
  1554.         i=2
  1555.         local bUseAll = false
  1556.        
  1557.         function getStudents()
  1558.            
  1559.             if tStudents == nil then
  1560.                
  1561.                 tStudents = rollCall()
  1562.                
  1563.             end
  1564.            
  1565.             return tStudents
  1566.            
  1567.         end
  1568.        
  1569.         function getStudent(sName)
  1570.            
  1571.             for k, v in pairs(getStudents()) do
  1572.                
  1573.                 if v.name == sName then return v end
  1574.                
  1575.             end
  1576.            
  1577.             printError("Ignoring non-existant student name: " ..  sName)
  1578.            
  1579.         end
  1580.        
  1581.         tProcessFuncs = {
  1582.            
  1583.             function()
  1584.                
  1585.                 if tArgs[i] == "-all" or tArgs[i] == "-names" then
  1586.                    
  1587.                     tProcessedArgs.broadcast = tArgs[i] == "-all"
  1588.                     i = i + 1
  1589.                    
  1590.                     if tProcessedArgs.broadcast then
  1591.                        
  1592.                         tProcessedArgs.students = getStudents()
  1593.                        
  1594.                     end
  1595.                    
  1596.                     return false
  1597.                    
  1598.                 end
  1599.                
  1600.                 table.insert(tProcessedArgs.superArgs, tostring(tArgs[i]))
  1601.                 i = i + 1
  1602.                
  1603.                 return true
  1604.                
  1605.             end,
  1606.            
  1607.             function()
  1608.                
  1609.                 if tProcessedArgs.broadcast then
  1610.                    
  1611.                     return false
  1612.                    
  1613.                 end
  1614.                
  1615.                 if tArgs[i] == "-args" then
  1616.                    
  1617.                     i = i + 1
  1618.                    
  1619.                     return false
  1620.                    
  1621.                 end
  1622.                
  1623.                 table.insert(tProcessedArgs.students, getStudent(tostring(tArgs[i])))
  1624.                 i = i + 1
  1625.                
  1626.                 return true
  1627.                
  1628.             end,
  1629.            
  1630.             function()
  1631.                
  1632.                 tProcessedArgs.hasArgs = true
  1633.                 table.insert(tProcessedArgs.args, tostring(tArgs[i]))
  1634.                 i = i + 1
  1635.                
  1636.                 return true
  1637.                
  1638.             end
  1639.            
  1640.         }
  1641.        
  1642.         for k, v in pairs(tProcessFuncs) do
  1643.            
  1644.             while not (i > #tArgs) and v() do end
  1645.            
  1646.         end
  1647.        
  1648.         return tProcessedArgs
  1649.        
  1650.     end
  1651.    
  1652.     tFuncs = {
  1653.        
  1654.         ["-help"]=function()
  1655.            
  1656.             setTextColor(colors.green)
  1657.            
  1658.             if #(tArgs.superArgs) == 0 then
  1659.                
  1660.                 printUsage()
  1661.                
  1662.                 return
  1663.                
  1664.             end
  1665.            
  1666.             sFlag = tArgs.superArgs[1]
  1667.            
  1668.             textutils.pagedPrint(tHelp[sFlag] or "No such flag: " .. sFlag, ({term.getSize()})[2] - 2)
  1669.            
  1670.             setTextColor(colors.white)
  1671.            
  1672.         end,
  1673.        
  1674.         ["-student"]=function()
  1675.            
  1676.             sleep(1)
  1677.             clear()
  1678.             printMessage("teachutils 1.0 student mode", string.format("Welcome, %s!", os.getComputerLabel()), colors.green, colors.white)
  1679.            
  1680.             local err
  1681.            
  1682.             while bRun do
  1683.                
  1684.                 ok, err = pcall(function()
  1685.                    
  1686.                     if err ~= nil then
  1687.                        
  1688.                         if string.find(err, "Terminated") == nil then
  1689.                            
  1690.                             bRun = false
  1691.                            
  1692.                             error(err)
  1693.                            
  1694.                         end
  1695.                        
  1696.                         printMessage("System", "You are not allowed to terminate this program.", colors.red, colors.red)
  1697.                        
  1698.                     end
  1699.                    
  1700.                     while bRun do
  1701.                        
  1702.                         tPacket = receive()
  1703.                        
  1704.                         if checkPacket(asPacketData("teacher",
  1705.                             nil, nil, nil,
  1706.                             function(nID) return nID == os.getComputerID() or nID == rednet.CHANNEL_BROADCAST end,
  1707.                             "function"), tPacket) then
  1708.                            
  1709.                             tTeacher = {name=tPacket.senderName, ID=tPacket.senderID}
  1710.                             func, e = loadstring(tPacket.data)
  1711.                             func = func or function() printMessage("System", e, colors.red, colors.red) end
  1712.                             setfenv(func, getfenv())
  1713.                             ok2, err2 = pcall(func)
  1714.                            
  1715.                             if not ok2 then
  1716.                                
  1717.                                 printMessage("System", err2, colors.red, colors.red)
  1718.                                
  1719.                             end
  1720.                            
  1721.                         end
  1722.                        
  1723.                     end
  1724.                    
  1725.                 end)
  1726.                
  1727.             end
  1728.                
  1729.             if not ok then
  1730.                
  1731.                 error(err)
  1732.                
  1733.             end
  1734.            
  1735.         end,
  1736.        
  1737.         ["-broadScreen"]=function()
  1738.            
  1739.             if not studentArgumentsValid(tArgs) then
  1740.                
  1741.                 printUsage()
  1742.                
  1743.                 return
  1744.                
  1745.             end
  1746.            
  1747.             print("Initializing...")
  1748.            
  1749.             for k, v in pairs(tArgs.students) do
  1750.                
  1751.                 sendFunction(v.name, v.ID,
  1752.                 tSendableFuncs.screenReceiver)
  1753.                
  1754.             end
  1755.            
  1756.             sleep(3)
  1757.             clear()
  1758.             sendScreen(function()
  1759.                    
  1760.                     clickSafeMultishell()
  1761.                    
  1762.                 end,
  1763.                 asPacketDataS(
  1764.                    
  1765.                     "teacher",
  1766.                     "all",
  1767.                     rednet.CHANNEL_BROADCAST
  1768.                    
  1769.                 ), true
  1770.             )
  1771.            
  1772.             sleep(2)
  1773.             clear()
  1774.             print("Screen share stopped")
  1775.            
  1776.         end,
  1777.        
  1778.         ["-sync"]=function()
  1779.            
  1780.             if not term.isColor() then
  1781.                
  1782.                 printError("Sync not supported")
  1783.                
  1784.             end
  1785.            
  1786.             if not studentArgumentsValid(tArgs) then
  1787.                
  1788.                 printUsage()
  1789.                
  1790.                 return
  1791.                
  1792.             end
  1793.            
  1794.             print("Initializing...")
  1795.            
  1796.             local bAllowEdit = false
  1797.            
  1798.             for k, v in pairs(tArgs.args) do
  1799.                
  1800.                 if v == "-allowEdit" then
  1801.                    
  1802.                     bAllowEdit = true
  1803.                    
  1804.                 end
  1805.                
  1806.             end
  1807.            
  1808.             for k, v in pairs(tArgs.students) do
  1809.                
  1810.                 sendFunction(v.name, v.ID,
  1811.                 tSendableFuncs.syncClient)
  1812.                
  1813.             end
  1814.            
  1815.             local tTabs = {}
  1816.            
  1817.             for k, v in pairs(tArgs.students) do
  1818.                
  1819.                 local nID = shell.openTab(shell.getRunningProgram(), "~sync", textutils.serialize(v))
  1820.                 table.insert(tTabs, nID)
  1821.                
  1822.             end
  1823.            
  1824.             sleep(5)
  1825.            
  1826.             if bAllowEdit then
  1827.                
  1828.                 os.queueEvent("void_events", false)
  1829.                
  1830.             end
  1831.            
  1832.             clear()
  1833.             setTextColor(colors.blue)
  1834.             print("CONTROL SCREEN: TERMINATE THIS SCREEN TO TERMINATE ALL SCREENS")
  1835.            
  1836.             parallel.waitForAny(
  1837.                
  1838.                 function()
  1839.                    
  1840.                     os.pullEventRaw("terminate")
  1841.                    
  1842.                 end,
  1843.                 function()
  1844.                    
  1845.                     while #tTabs > 0 do
  1846.                        
  1847.                         local tEvent = {os.pullEventRaw("sync_death")}
  1848.                         local nIndex
  1849.                        
  1850.                         for k, v in pairs(tTabs) do
  1851.                            
  1852.                             if v == tEvent[2] then
  1853.                                
  1854.                                 nIndex = k
  1855.                                
  1856.                             end
  1857.                            
  1858.                         end
  1859.                        
  1860.                         if nIndex then
  1861.                            
  1862.                             table.remove(tTabs, nIndex)
  1863.                            
  1864.                         end
  1865.                        
  1866.                     end
  1867.                    
  1868.                 end
  1869.                
  1870.             )
  1871.            
  1872.             os.queueEvent("*END TRANSMISSION*")
  1873.            
  1874.             sleep(2)
  1875.             clear()
  1876.             print("Sync stopped")
  1877.            
  1878.         end,
  1879.        
  1880.         ["~sync"]=function()
  1881.            
  1882.             local tStudent = textutils.unserializ(tArgs.superArgs[1])
  1883.             if not tStudent and tStudent.name and tStudent.ID then error("INVALID") end
  1884.             sendFunction(tStudent.name, tStudent.ID,
  1885.                 tSendableFuncs.syncClient)
  1886.             parallel.waitForAny(
  1887.                
  1888.                 function()
  1889.                    
  1890.                     local tEventModifiers = {
  1891.                        
  1892.                         ["terminate"]=createEventSwapper("remote_terminate"),
  1893.                         ["*END TRANSMISSION*"]=createEventSwapper("remote_terminate"),
  1894.                         ["key"]=createEventSwapper("remote_key"),
  1895.                         ["char"]=createEventSwapper("remote_char"),
  1896.                         ["key_up"]=createEventSwapper("remote_key_up"),
  1897.                         ["mouse_click"]=createEventSwapper("remote_mouse_click"),
  1898.                         ["mouse_up"]=createEventSwapper("remote_mouse_up"),
  1899.                         ["mouse_scroll"]=createEventSwapper("remote_mouse_scroll"),
  1900.                         ["mouse_drag"]=createEventSwapper("remote_mouse_drag")
  1901.                        
  1902.                     }
  1903.                    
  1904.                     setmetatable(tEventModifiers, {__index = function() return eventVoider end})
  1905.                    
  1906.                     sendEvents(asPacketDataS(
  1907.                            
  1908.                             "teacher",
  1909.                             tStudent.name,
  1910.                             tStudent.ID
  1911.                            
  1912.                         ),
  1913.                         tEventModifiers, true)
  1914.                    
  1915.                 end,
  1916.                 function()
  1917.                    
  1918.                     receiveScreen(asPacketData(
  1919.                            
  1920.                             "student",
  1921.                             nil,
  1922.                             tStudent.ID,
  1923.                             nil,
  1924.                             os.getComputerID()
  1925.                            
  1926.                         ))
  1927.                    
  1928.                 end
  1929.                
  1930.             )
  1931.            
  1932.             os.queueEvent("sync_death", multishell.getCurrent())
  1933.            
  1934.         end,
  1935.        
  1936.         ["-setEditable"]=function()
  1937.            
  1938.             if #(tArgs.superArgs) < 0 then
  1939.                
  1940.                 printUsage()
  1941.                
  1942.                 return
  1943.                
  1944.             end
  1945.            
  1946.             if tArgs.superArgs[1] == "true" then
  1947.                
  1948.                 os.queueEvent("void_events", true)
  1949.                
  1950.             elseif tArgs.superArgs[1] == "false" then
  1951.                
  1952.                 os.queueEvent("void_events", false)
  1953.                
  1954.             else
  1955.                
  1956.                 printUsage()
  1957.                
  1958.             end
  1959.            
  1960.         end,
  1961.        
  1962.         ["-force"]=function()
  1963.            
  1964.             if not studentArgumentsValid(tArgs) then
  1965.                
  1966.                 printUsage()
  1967.                
  1968.                 return
  1969.                
  1970.             end
  1971.            
  1972.             sFlag = table.remove(tArgs.args, 1)
  1973.             sFunc = tSendableFuncs[tostring(sFlag) .. "@force"]
  1974.            
  1975.             if not sFunc then
  1976.                
  1977.                 printUsage()
  1978.                
  1979.                 return
  1980.                
  1981.             end
  1982.            
  1983.             sRun = ""
  1984.            
  1985.             if sFlag == "-runProgram" then
  1986.                
  1987.                 sRun = textutils.serialize(tArgs.args):gsub("\"", "\\\""):gsub("\n", " ")
  1988.                
  1989.                 if sRun == "{}" then
  1990.                    
  1991.                     printUsage()
  1992.                    
  1993.                     return
  1994.                    
  1995.                 end
  1996.                
  1997.             end
  1998.            
  1999.             for k, v in pairs(tArgs.students) do
  2000.                
  2001.                 sendFunction(v.name, v.ID,
  2002.                 string.format(sFunc, sRun, tostring(tArgs.args[1])))
  2003.                
  2004.             end
  2005.            
  2006.         end,
  2007.        
  2008.         ["-list"]=function()
  2009.            
  2010.             tStudentList = rollCall()
  2011.            
  2012.             for k, v in pairs(tStudentList) do
  2013.                
  2014.                 print(string.format("name: %s, ID: %s", v.name, v.ID))
  2015.                
  2016.             end
  2017.            
  2018.         end,
  2019.        
  2020.         ["-FTP"]=function()
  2021.            
  2022.             printError("NYI")
  2023.            
  2024.         end,
  2025.        
  2026.         ["-sendMessage"]=function()
  2027.            
  2028.             if not studentArgumentsValid(tArgs) or not tArgs.hasArgs then
  2029.                
  2030.                 printUsage()
  2031.                
  2032.                 return
  2033.                
  2034.             end
  2035.            
  2036.             sMessage = ""
  2037.            
  2038.             for k, v in pairs(tArgs.args) do
  2039.                
  2040.                 sMessage = sMessage .. v .. " "
  2041.                
  2042.             end
  2043.            
  2044.             for k, v in pairs(tArgs.students) do
  2045.                
  2046.                 sendFunction(v.name, v.ID,
  2047.                 string.format(tSendableFuncs.messagePrinter,
  2048.                 "(Teacher)" .. os.getComputerLabel(), sMessage,
  2049.                 colors.lightBlue, colors.white))
  2050.                
  2051.             end
  2052.            
  2053.         end
  2054.        
  2055.     }
  2056.  
  2057.     tArgs = parseArgs({...})
  2058.     func = tFuncs[tArgs.superFlag] or function() printUsage() end
  2059.     func()
  2060.  
  2061. end
  2062.  
  2063. main(...)
Advertisement
Add Comment
Please, Sign In to add comment