Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local tArgs = {...}
- local turtleID = tonumber(tArgs[1])
- if not turtleID or turtleID % 1 ~= 0 then
- error("invalid turtleID")
- end
- local programPath = tostring(tArgs[2])
- if not programPath or not fs.exists(programPath) or fs.isDir(programPath) then
- error("invalid program path")
- end
- local found = false
- for _, side in ipairs(rs.getSides()) do
- if peripheral.getType(side) == "modem" then
- rednet.open(side)
- found = true
- break
- end
- end
- if not found then
- error("no modem found")
- end
- local pendingCommands = {}
- local commandTimers = {}
- local listeningCoroutines = {}
- local remoteExecuteProtocol = "remoteExecute"
- local function makeCommand(func)
- return function(...)
- local commandNum
- repeat
- commandNum = math.random(0, 65535)
- until not pendingCommands[commandNum]
- local thread = coroutine.running()
- local command = {
- attempt = 1,
- func = func,
- args = {...},
- commandNum = commandNum,
- }
- rednet.send(turtleID, command, remoteExecuteProtocol)
- pendingCommands[commandNum] = command
- commandTimers[os.startTimer(1)] = commandNum
- if not listeningCoroutines[thread] then
- listeningCoroutines[thread] = {
- commandNum = false,
- queue = {},
- }
- end
- listeningCoroutines[thread].commandNum = commandNum
- local queue = listeningCoroutines[thread].queue
- local event
- while true do
- event = {coroutine.yield()}
- if event[1] == "turtle_response" then
- if event[2] == commandNum then
- listeningCoroutines[thread].commandNum = false
- if not event[3] then
- error(event[4], 2)
- else
- return unpack(event, 4)
- end
- end
- else
- table.insert(queue, event)
- end
- end
- end
- end
- --===== ENVIRONMENT SETUP =====--
- local turtleEnv = {}
- setmetatable(turtleEnv, {__index = _G})
- turtleEnv.global = _G
- local turtleFunctions = {
- "forward", "back",
- "up", "down",
- "turnLeft", "turnRight",
- "select",
- "getSelectedSlot",
- "getItemCount",
- "getItemSpace",
- "getItemDetail",
- "equipLeft", "equipRight",
- "attack", "attackUp", "attackDown",
- "dig", "digUp", "digDown",
- "place", "placeUp", "placeDown",
- "detect", "detectUp", "detectDown",
- "inspect", "inspectUp", "inspectDown",
- "compare", "compareUp", "compareDown",
- "compareTo",
- "drop", "dropUp", "dropDown",
- "suck", "suckUp", "suckDown",
- "refuel",
- "getFuelLevel",
- "getFuelLimit",
- "transferTo",
- }
- local turtleAPI = {}
- for _, func in ipairs(turtleFunctions) do
- turtleAPI[func] = makeCommand({"turtle", func})
- end
- turtleEnv.turtle = turtleAPI
- local oldCoroutineResume = coroutine.resume
- local coroutineAPI = {
- resume = function(thread, ...)
- local passback = {oldCoroutineResume(thread, ...)}
- if listeningCoroutines[thread] then
- if passback[1] and coroutine.status(thread) ~= "dead" then
- local threadInfo = listeningCoroutines[thread]
- local filter = passback[2]
- while (not threadInfo.commandNum) and threadInfo.queue[1] do
- local event = table.remove(threadInfo.queue, 1)
- if not filter or event[1] == filter or event[1] == "terminate" then
- passback = {oldCoroutineResume(thread, unpack(event))}
- if (not passback[1]) or coroutine.status(thread) == "dead" then
- return unpack(passback)
- else
- filter = passback[2]
- end
- end
- end
- end
- end
- return unpack(passback)
- end,
- wrap = function( _fn )
- local typeT = type( _fn )
- if typeT ~= "function" then
- error( "bad argument #1 to coroutine.wrap (function expected, got "..typeT..")", 2 )
- end
- local co = coroutine.create( _fn )
- return function( ... )
- local tResults = { turtleEnv.coroutine.resume( co, ... ) }
- if tResults[1] then
- return unpack( tResults, 2 )
- else
- error( tResults[2], 2 )
- end
- end
- end,
- }
- turtleEnv.coroutine = setmetatable(coroutineAPI, {__index = _G.coroutine})
- turtleEnv.xpcall = function( _fn, _fnErrorHandler )
- local typeT = type( _fn )
- assert( typeT == "function", "bad argument #1 to xpcall (function expected, got "..typeT..")" )
- local co = coroutine.create( _fn )
- local tResults = { turtleEnv.coroutine.resume( co ) }
- while coroutine.status( co ) ~= "dead" do
- tResults = { turtleEnv.coroutine.resume( co, coroutine.yield() ) }
- end
- if tResults[1] == true then
- return true, unpack( tResults, 2 )
- else
- return false, _fnErrorHandler( tResults[2] )
- end
- end
- turtleEnv.pcall = function( _fn, ... )
- local typeT = type( _fn )
- assert( typeT == "function", "bad argument #1 to pcall (function expected, got "..typeT..")" )
- local tArgs = { ... }
- return turtleEnv.xpcall(
- function()
- return _fn( unpack( tArgs ) )
- end,
- function( _error )
- return _error
- end
- )
- end
- local oldLoadstring = loadstring
- turtleEnv.loadstring = function(...)
- local result = {oldLoadstring(...)}
- if result[1] then
- setfenv(result[1], turtleEnv)
- end
- return unpack(result)
- end
- local oldLoadfile = loadfile
- turtleEnv.loadfile = function( _sFile )
- local file = fs.open( _sFile, "r" )
- if file then
- local func, err = turtleEnv.loadstring( file.readAll(), fs.getName( _sFile ) )
- file.close()
- return func, err
- end
- return nil, "File not found"
- end
- local oldDofile = dofile
- turtleEnv.dofile = function( _sFile )
- local fnFile, e = turtleEnv.loadfile( _sFile )
- if fnFile then
- setfenv( fnFile, getfenv(2) )
- return fnFile()
- else
- error( e, 2 )
- end
- end
- local tAPIsLoading = {}
- local function loadAPI( _sPath )
- local sName = fs.getName( _sPath )
- if tAPIsLoading[sName] == true then
- printError( "API "..sName.." is already being loaded" )
- return false
- end
- tAPIsLoading[sName] = true
- local tEnv = {}
- setmetatable( tEnv, { __index = turtleEnv } )
- local fnAPI, err = turtleEnv.loadfile( _sPath )
- if fnAPI then
- setfenv( fnAPI, tEnv )
- local ok, err = turtleEnv.pcall( fnAPI )
- if not ok then
- printError( err )
- tAPIsLoading[sName] = nil
- return false
- end
- else
- printError( err )
- tAPIsLoading[sName] = nil
- return false
- end
- local tAPI = {}
- for k,v in pairs( tEnv ) do
- tAPI[k] = v
- end
- turtleEnv[sName] = tAPI
- tAPIsLoading[sName] = nil
- return true
- end
- local osAPI = {
- --getComputerID = function() return turtleID end,
- getComputerID = makeCommand({"os", "getComputerID"}),
- getComputerLabel = makeCommand({"os", "getComputerLabel"}),
- setComputerLabel = makeCommand({"os", "setComputerLabel"}),
- loadAPI = loadAPI,
- unloadAPI = function( _sName )
- if _sName ~= "_G" and type(turtleEnv[_sName]) == "table" then
- turtleEnv[_sName] = nil
- end
- end,
- run = function( _tEnv, _sPath, ... )
- local tArgs = { ... }
- local fnFile, err = turtleEnv.loadfile( _sPath )
- if fnFile then
- local tEnv = _tEnv
- --setmetatable( tEnv, { __index = function(t,k) return _G[k] end } )
- setmetatable( tEnv, { __index = turtleEnv } )
- setfenv( fnFile, tEnv )
- local ok, err = turtleEnv.pcall( function()
- fnFile( unpack( tArgs ) )
- end )
- if not ok then
- if err and err ~= "" then
- printError( err )
- end
- return false
- end
- return true
- end
- if err and err ~= "" then
- printError( err )
- end
- return false
- end,
- }
- turtleEnv.os = setmetatable(osAPI, {__index = _G.os})
- local tApis = fs.list( "rom/apis/turtle" )
- for n,sFile in ipairs( tApis ) do
- if string.sub( sFile, 1, 1 ) ~= "." then
- local sPath = fs.combine( "rom/apis/turtle", sFile )
- if not fs.isDir( sPath ) then
- if not turtleEnv.os.loadAPI( sPath ) then
- bAPIError = true
- end
- end
- end
- end
- local nativePeripheralAPI = {
- call = makeCommand({"peripheral", "call"}),
- isPresent = makeCommand({"peripheral", "isPresent"}),
- getType = makeCommand({"peripheral", "getType"}),
- getMethods = makeCommand({"peripheral", "getMethods"}),
- }
- turtleEnv.peripheral = nativePeripheralAPI
- loadAPI("rom/apis/peripheral")
- loadAPI("rom/apis/rednet")
- local rednetRun = coroutine.create(turtleEnv.rednet.run)
- coroutine.resume(rednetRun)
- loadAPI("rom/apis/gps")
- loadAPI("rom/apis/parallel")
- loadAPI("rom/apis/disk")
- local redstoneAPI = {}
- for name, func in pairs(redstone) do
- if type(func) == "function" then
- redstoneAPI[name] = makeCommand({"redstone", name})
- end
- end
- turtleEnv.redstone = redstoneAPI
- turtleEnv.rs = redstoneAPI
- --===== END ENVIRONMENT SETUP =====--
- local programFunc, err = loadfile(programPath)
- if not programFunc then
- if err and err ~= "" then
- error("could not load program with error: "..err)
- end
- end
- setfenv(programFunc, turtleEnv)
- local programThread = coroutine.create(programFunc)
- local startArgs = {unpack(tArgs, 3)}
- while true do
- print("attempting to connect to id: ", turtleID)
- rednet.send(turtleID, {connection = true, connect = true}, remoteExecuteProtocol)
- local id, message, protocol = rednet.receive(remoteExecuteProtocol, 3)
- if id == turtleID and message == "connected" then
- print("connected")
- sleep(1)
- term.clear()
- term.setCursorPos(1, 1)
- break
- end
- end
- local running = true
- local filter = nil
- local function resumeProgram(eventType, ...)
- if not filter or eventType == filter or eventType == "terminate" then
- local passback = {turtleEnv.coroutine.resume(programThread, eventType, ...)}
- if not passback[1] then
- printError(passback[2])
- running = false
- elseif coroutine.status(programThread) == "dead" then
- running = false
- else
- filter = passback[2]
- end
- end
- end
- local event, resume = startArgs, true
- while true do
- if event[1] == "rednet_message" then
- local id, message, protocol = event[2], event[3], event[4]
- if id == turtleID and protocol == remoteExecuteProtocol and type(message) == "table" then
- if message.commandResponse then
- local command = pendingCommands[message.commandNum]
- if command then
- pendingCommands[message.commandNum] = nil
- event = {"turtle_response", message.commandNum, message.success, unpack(message.result)}
- end
- elseif message.remoteEvent then
- event = message.event
- elseif message.connection then
- resume = false
- end
- end
- elseif event[1] == "modem_message" and type(event[5]) == "table" and event[5].sProtocol == remoteExecuteProtocol then
- resume = false
- elseif event[1] == "timer" and commandTimers[event[2]] then
- local commandNum = commandTimers[event[2]]
- commandTimers[event[2]] = nil
- local command = pendingCommands[commandNum]
- if command then
- command.attempt = command.attempt + 1
- if command.attempt % 15 == 0 then
- print("Possible connection problems, please check your turtle")
- end
- rednet.send(turtleID, command, remoteExecuteProtocol)
- commandTimers[os.startTimer(1)] = commandNum
- end
- resume = false
- end
- if resume then
- resumeProgram(unpack(event))
- end
- if not running then break end
- event, resume = {coroutine.yield()}, true
- end
- --program finished, send disconnect message
- local disconnectMessage = {
- connection = true,
- disconnect = true,
- }
- rednet.send(turtleID, disconnectMessage, remoteExecuteProtocol)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement