Advertisement
blunty666

remoteExecuteServer

Mar 26th, 2015
67
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 11.21 KB | None | 0 0
  1. local tArgs = {...}
  2.  
  3. local turtleID = tonumber(tArgs[1])
  4. if not turtleID or turtleID % 1 ~= 0 then
  5.     error("invalid turtleID")
  6. end
  7.  
  8. local programPath = tostring(tArgs[2])
  9. if not programPath or not fs.exists(programPath) or fs.isDir(programPath) then
  10.     error("invalid program path")
  11. end
  12.  
  13. local found = false
  14. for _, side in ipairs(rs.getSides()) do
  15.     if peripheral.getType(side) == "modem" then
  16.         rednet.open(side)
  17.         found = true
  18.         break
  19.     end
  20. end
  21. if not found then
  22.     error("no modem found")
  23. end
  24.  
  25. local pendingCommands = {}
  26. local commandTimers = {}
  27. local listeningCoroutines = {}
  28. local remoteExecuteProtocol = "remoteExecute"
  29.  
  30. local function makeCommand(func)
  31.     return function(...)
  32.         local commandNum
  33.         repeat
  34.             commandNum = math.random(0, 65535)
  35.         until not pendingCommands[commandNum]
  36.         local thread = coroutine.running()
  37.         local command = {
  38.             attempt = 1,
  39.             func = func,
  40.             args = {...},
  41.             commandNum = commandNum,
  42.         }
  43.         rednet.send(turtleID, command, remoteExecuteProtocol)
  44.         pendingCommands[commandNum] = command
  45.         commandTimers[os.startTimer(1)] = commandNum
  46.         if not listeningCoroutines[thread] then
  47.             listeningCoroutines[thread] = {
  48.                 commandNum = false,
  49.                 queue = {},
  50.             }
  51.         end
  52.         listeningCoroutines[thread].commandNum = commandNum
  53.         local queue = listeningCoroutines[thread].queue
  54.         local event
  55.         while true do
  56.             event = {coroutine.yield()}
  57.             if event[1] == "turtle_response" then
  58.                 if event[2] == commandNum then
  59.                     listeningCoroutines[thread].commandNum = false
  60.                     if not event[3] then
  61.                         error(event[4], 2)
  62.                     else
  63.                         return unpack(event, 4)
  64.                     end
  65.                 end
  66.             else
  67.                 table.insert(queue, event)
  68.             end
  69.         end
  70.     end
  71. end
  72.  
  73. --===== ENVIRONMENT SETUP =====--
  74. local turtleEnv = {}
  75. setmetatable(turtleEnv, {__index = _G})
  76. turtleEnv.global = _G
  77.  
  78. local turtleFunctions = {
  79.     "forward", "back",
  80.     "up", "down",
  81.     "turnLeft", "turnRight",
  82.     "select",
  83.     "getSelectedSlot",
  84.     "getItemCount",
  85.     "getItemSpace",
  86.     "getItemDetail",
  87.     "equipLeft", "equipRight",
  88.     "attack", "attackUp", "attackDown",
  89.     "dig", "digUp", "digDown",
  90.     "place", "placeUp", "placeDown",
  91.     "detect", "detectUp", "detectDown",
  92.     "inspect", "inspectUp", "inspectDown",
  93.     "compare", "compareUp", "compareDown",
  94.     "compareTo",
  95.     "drop",  "dropUp", "dropDown",
  96.     "suck", "suckUp", "suckDown",
  97.     "refuel",
  98.     "getFuelLevel",
  99.     "getFuelLimit",
  100.     "transferTo",
  101. }
  102. local turtleAPI = {}
  103. for _, func in ipairs(turtleFunctions) do
  104.     turtleAPI[func] = makeCommand({"turtle", func})
  105. end
  106. turtleEnv.turtle = turtleAPI
  107.  
  108. local oldCoroutineResume = coroutine.resume
  109. local coroutineAPI = {
  110.     resume = function(thread, ...)
  111.         local passback = {oldCoroutineResume(thread, ...)}
  112.         if listeningCoroutines[thread] then
  113.             if passback[1] and coroutine.status(thread) ~= "dead" then
  114.                 local threadInfo = listeningCoroutines[thread]
  115.                 local filter = passback[2]
  116.                 while (not threadInfo.commandNum) and threadInfo.queue[1] do
  117.                     local event = table.remove(threadInfo.queue, 1)
  118.                     if not filter or event[1] == filter or event[1] == "terminate" then
  119.                         passback = {oldCoroutineResume(thread, unpack(event))}
  120.                         if (not passback[1]) or coroutine.status(thread) == "dead" then
  121.                             return unpack(passback)
  122.                         else
  123.                             filter = passback[2]
  124.                         end
  125.                     end
  126.                 end
  127.             end
  128.         end
  129.         return unpack(passback)
  130.     end,
  131.     wrap = function( _fn )
  132.         local typeT = type( _fn )
  133.         if typeT ~= "function" then
  134.             error( "bad argument #1 to coroutine.wrap (function expected, got "..typeT..")", 2 )
  135.         end
  136.         local co = coroutine.create( _fn )
  137.         return function( ... )
  138.             local tResults = { turtleEnv.coroutine.resume( co, ... ) }
  139.             if tResults[1] then
  140.                 return unpack( tResults, 2 )
  141.             else
  142.                 error( tResults[2], 2 )
  143.             end
  144.         end
  145.     end,
  146. }
  147. turtleEnv.coroutine = setmetatable(coroutineAPI, {__index = _G.coroutine})
  148.  
  149. turtleEnv.xpcall = function( _fn, _fnErrorHandler )
  150.     local typeT = type( _fn )
  151.     assert( typeT == "function", "bad argument #1 to xpcall (function expected, got "..typeT..")" )
  152.     local co = coroutine.create( _fn )
  153.     local tResults = { turtleEnv.coroutine.resume( co ) }
  154.     while coroutine.status( co ) ~= "dead" do
  155.         tResults = { turtleEnv.coroutine.resume( co, coroutine.yield() ) }
  156.     end
  157.     if tResults[1] == true then
  158.         return true, unpack( tResults, 2 )
  159.     else
  160.         return false, _fnErrorHandler( tResults[2] )
  161.     end
  162. end
  163.  
  164. turtleEnv.pcall = function( _fn, ... )
  165.     local typeT = type( _fn )
  166.     assert( typeT == "function", "bad argument #1 to pcall (function expected, got "..typeT..")" )
  167.     local tArgs = { ... }
  168.     return turtleEnv.xpcall(
  169.         function()
  170.             return _fn( unpack( tArgs ) )
  171.         end,
  172.         function( _error )
  173.             return _error
  174.         end
  175.     )
  176. end
  177.  
  178. local oldLoadstring = loadstring
  179. turtleEnv.loadstring = function(...)
  180.     local result = {oldLoadstring(...)}
  181.     if result[1] then
  182.         setfenv(result[1], turtleEnv)
  183.     end
  184.     return unpack(result)
  185. end
  186.  
  187. local oldLoadfile = loadfile
  188. turtleEnv.loadfile = function( _sFile )
  189.     local file = fs.open( _sFile, "r" )
  190.     if file then
  191.         local func, err = turtleEnv.loadstring( file.readAll(), fs.getName( _sFile ) )
  192.         file.close()
  193.         return func, err
  194.     end
  195.     return nil, "File not found"
  196. end
  197.  
  198. local oldDofile = dofile
  199. turtleEnv.dofile = function( _sFile )
  200.     local fnFile, e = turtleEnv.loadfile( _sFile )
  201.     if fnFile then
  202.         setfenv( fnFile, getfenv(2) )
  203.         return fnFile()
  204.     else
  205.         error( e, 2 )
  206.     end
  207. end
  208.  
  209. local tAPIsLoading = {}
  210. local function loadAPI( _sPath )
  211.     local sName = fs.getName( _sPath )
  212.     if tAPIsLoading[sName] == true then
  213.         printError( "API "..sName.." is already being loaded" )
  214.         return false
  215.     end
  216.     tAPIsLoading[sName] = true
  217.        
  218.     local tEnv = {}
  219.     setmetatable( tEnv, { __index = turtleEnv } )
  220.     local fnAPI, err = turtleEnv.loadfile( _sPath )
  221.     if fnAPI then
  222.         setfenv( fnAPI, tEnv )
  223.         local ok, err = turtleEnv.pcall( fnAPI )
  224.         if not ok then
  225.             printError( err )
  226.             tAPIsLoading[sName] = nil
  227.             return false
  228.         end
  229.     else
  230.         printError( err )
  231.         tAPIsLoading[sName] = nil
  232.         return false
  233.     end
  234.    
  235.     local tAPI = {}
  236.     for k,v in pairs( tEnv ) do
  237.         tAPI[k] =  v
  238.     end
  239.    
  240.     turtleEnv[sName] = tAPI    
  241.     tAPIsLoading[sName] = nil
  242.     return true
  243. end
  244.  
  245. local osAPI = {
  246.     --getComputerID = function() return turtleID end,
  247.     getComputerID = makeCommand({"os", "getComputerID"}),
  248.     getComputerLabel = makeCommand({"os", "getComputerLabel"}),
  249.     setComputerLabel = makeCommand({"os", "setComputerLabel"}),
  250.     loadAPI = loadAPI,
  251.     unloadAPI = function( _sName )
  252.         if _sName ~= "_G" and type(turtleEnv[_sName]) == "table" then
  253.             turtleEnv[_sName] = nil
  254.         end
  255.     end,
  256.     run = function( _tEnv, _sPath, ... )
  257.         local tArgs = { ... }
  258.         local fnFile, err = turtleEnv.loadfile( _sPath )
  259.         if fnFile then
  260.             local tEnv = _tEnv
  261.             --setmetatable( tEnv, { __index = function(t,k) return _G[k] end } )
  262.             setmetatable( tEnv, { __index = turtleEnv } )
  263.             setfenv( fnFile, tEnv )
  264.             local ok, err = turtleEnv.pcall( function()
  265.                 fnFile( unpack( tArgs ) )
  266.             end )
  267.             if not ok then
  268.                 if err and err ~= "" then
  269.                     printError( err )
  270.                 end
  271.                 return false
  272.             end
  273.             return true
  274.         end
  275.         if err and err ~= "" then
  276.             printError( err )
  277.         end
  278.         return false
  279.     end,
  280. }
  281. turtleEnv.os = setmetatable(osAPI, {__index = _G.os})
  282.  
  283. local tApis = fs.list( "rom/apis/turtle" )
  284. for n,sFile in ipairs( tApis ) do
  285.     if string.sub( sFile, 1, 1 ) ~= "." then
  286.         local sPath = fs.combine( "rom/apis/turtle", sFile )
  287.         if not fs.isDir( sPath ) then
  288.             if not turtleEnv.os.loadAPI( sPath ) then
  289.                 bAPIError = true
  290.             end
  291.         end
  292.     end
  293. end
  294.  
  295. local nativePeripheralAPI = {
  296.     call = makeCommand({"peripheral", "call"}),
  297.     isPresent = makeCommand({"peripheral", "isPresent"}),
  298.     getType = makeCommand({"peripheral", "getType"}),
  299.     getMethods = makeCommand({"peripheral", "getMethods"}),
  300. }
  301. turtleEnv.peripheral = nativePeripheralAPI
  302. loadAPI("rom/apis/peripheral")
  303.        
  304. loadAPI("rom/apis/rednet")
  305. local rednetRun = coroutine.create(turtleEnv.rednet.run)
  306. coroutine.resume(rednetRun)
  307.  
  308. loadAPI("rom/apis/gps")
  309. loadAPI("rom/apis/parallel")
  310. loadAPI("rom/apis/disk")
  311.  
  312. local redstoneAPI = {}
  313. for name, func in pairs(redstone) do
  314.     if type(func) == "function" then
  315.         redstoneAPI[name] = makeCommand({"redstone", name})
  316.     end
  317. end
  318. turtleEnv.redstone = redstoneAPI
  319. turtleEnv.rs = redstoneAPI
  320. --===== END ENVIRONMENT SETUP =====--
  321.  
  322. local programFunc, err = loadfile(programPath)
  323. if not programFunc then
  324.     if err and err ~= "" then
  325.         error("could not load program with error: "..err)
  326.     end
  327. end
  328. setfenv(programFunc, turtleEnv)
  329.  
  330. local programThread = coroutine.create(programFunc)
  331. local startArgs = {unpack(tArgs, 3)}
  332.  
  333. while true do
  334.     print("attempting to connect to id: ", turtleID)
  335.     rednet.send(turtleID, {connection = true, connect = true}, remoteExecuteProtocol)
  336.     local id, message, protocol = rednet.receive(remoteExecuteProtocol, 3)
  337.     if id == turtleID and message == "connected" then
  338.         print("connected")
  339.         sleep(1)
  340.         term.clear()
  341.         term.setCursorPos(1, 1)
  342.         break
  343.     end
  344. end
  345.  
  346. local running = true
  347. local filter = nil
  348. local function resumeProgram(eventType, ...)
  349.     if not filter or eventType == filter or eventType == "terminate" then
  350.         local passback = {turtleEnv.coroutine.resume(programThread, eventType, ...)}
  351.         if not passback[1] then
  352.             printError(passback[2])
  353.             running = false
  354.         elseif coroutine.status(programThread) == "dead" then
  355.             running = false
  356.         else
  357.             filter = passback[2]
  358.         end
  359.     end
  360. end
  361.  
  362. local event, resume = startArgs, true
  363. while true do
  364.    
  365.     if event[1] == "rednet_message" then
  366.         local id, message, protocol = event[2], event[3], event[4]
  367.         if id == turtleID and protocol == remoteExecuteProtocol and type(message) == "table" then
  368.             if message.commandResponse then
  369.                 local command = pendingCommands[message.commandNum]
  370.                 if command then
  371.                     pendingCommands[message.commandNum] = nil
  372.                     event = {"turtle_response", message.commandNum, message.success, unpack(message.result)}
  373.                 end
  374.             elseif message.remoteEvent then
  375.                 event = message.event
  376.             elseif message.connection then
  377.                 resume = false
  378.             end
  379.         end
  380.     elseif event[1] == "modem_message" and type(event[5]) == "table" and event[5].sProtocol == remoteExecuteProtocol then
  381.         resume = false
  382.     elseif event[1] == "timer" and commandTimers[event[2]] then
  383.         local commandNum = commandTimers[event[2]]
  384.         commandTimers[event[2]] = nil
  385.         local command = pendingCommands[commandNum]
  386.         if command then
  387.             command.attempt = command.attempt + 1
  388.             if command.attempt % 15 == 0 then
  389.                 print("Possible connection problems, please check your turtle")
  390.             end
  391.             rednet.send(turtleID, command, remoteExecuteProtocol)
  392.             commandTimers[os.startTimer(1)] = commandNum
  393.         end
  394.         resume = false
  395.     end
  396.    
  397.     if resume then
  398.         resumeProgram(unpack(event))
  399.     end
  400.    
  401.     if not running then break end
  402.    
  403.     event, resume = {coroutine.yield()}, true
  404.    
  405. end
  406.  
  407. --program finished, send disconnect message
  408. local disconnectMessage = {
  409.     connection = true,
  410.     disconnect = true,
  411. }
  412. rednet.send(turtleID, disconnectMessage, remoteExecuteProtocol)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement