diff --git a/builder/apis/turtle.lua b/builder/apis/turtle.lua index 572ae4e..4d5901e 100644 --- a/builder/apis/turtle.lua +++ b/builder/apis/turtle.lua @@ -446,7 +446,7 @@ function TurtleBuilder:inAirDropoff() self:log('Received supply location') os.sleep(0) - turtle._goto(pt) + turtle.go(pt) os.sleep(.1) -- random computer is not connected error local chestAdapter = Adapter.wrap({ direction = 'down', side = 'top' }) @@ -508,7 +508,7 @@ function TurtleBuilder:inAirResupply() self:log('Received supply location') os.sleep(0) - turtle._goto(pt) + turtle.go(pt) os.sleep(.1) -- random computer is not connected error local chestAdapter = Adapter.wrap({ direction = 'down', side = 'top' }) @@ -608,7 +608,7 @@ function TurtleBuilder:gotoSupplyPoint() -- go to the corner closest to the supplies point -- pathfind the rest of the way local pt = self:getBuildingCorner(turtle.point.y) - turtle._goto({ x = pt.x, z = pt.z }) + turtle.go({ x = pt.x, z = pt.z }) turtle.set({ digPolicy = 'digNone', attackPolicy = 'attackNone', @@ -818,8 +818,8 @@ function TurtleBuilder:placePiston(b) return success end -function TurtleBuilder:_goto(x, z, y, heading) - if not turtle._goto({ x = x, z = z, y = y, heading = heading }) then +function TurtleBuilder:go(x, z, y, heading) + if not turtle.go({ x = x, z = z, y = y, heading = heading }) then print('stuck') print('Press enter to continue') os.sleep(1) @@ -844,7 +844,7 @@ function TurtleBuilder:gotoEx(x, z, y, h, travelPlane) elseif distance > 1 then self:gotoTravelPlane(travelPlane) end - self:_goto(x, z, y, h) + self:go(x, z, y, h) end function TurtleBuilder:placeDirectionalBlock(b, slot, travelPlane) @@ -1128,7 +1128,7 @@ function TurtleBuilder:build() if b.y ~= turtle.getPoint().y then turtle.gotoY(b.y) end - self:_goto(b.x, b.z, b.y) + self:go(b.x, b.z, b.y) turtle.digDown() -- if no supplier, then should fill all slots @@ -1171,7 +1171,7 @@ function TurtleBuilder:build() end else self:gotoTravelPlane(travelPlane) - self:_goto(b.x, b.z, b.y) + self:go(b.x, b.z, b.y) b.placed = self:placeDown(slot) end diff --git a/core/apis/turtle/level.lua b/core/apis/turtle/level.lua index 9685614..1d315be 100644 --- a/core/apis/turtle/level.lua +++ b/core/apis/turtle/level.lua @@ -163,7 +163,7 @@ function turtle.level(startPt, endPt, firstPt, verbose) local node = closestPoint(turtle.point, nodes) node = getAdjacentPoint(node) - if not turtle._goto(node) then + if not turtle.go(node) then break end until turtle.isAborted() diff --git a/core/etc/scripts/summon b/core/etc/scripts/summon index b8eae58..45e0c9e 100644 --- a/core/etc/scripts/summon +++ b/core/etc/scripts/summon @@ -32,7 +32,7 @@ local function summon(id) local function doGPS() tFixes = { } for i = 1, 4 do - if not turtle._goto(pts[i]) then + if not turtle.go(pts[i]) then error('turtle: Unable to perform GPS maneuver') end local distance = getDistance() diff --git a/farms/farmer.lua b/farms/farmer.lua index 273edf9..4df9598 100644 --- a/farms/farmer.lua +++ b/farms/farmer.lua @@ -139,7 +139,7 @@ local function harvest(blocks) turtle.digForwardAt(b) elseif b.action == 'drop' and not dropped then - if turtle._goto(Point.above(b)) then + if turtle.go(Point.above(b)) then turtle.eachFilledSlot(function(slot) if not retain[slot.name] and not retain[slot.key] then turtle.select(slot.index) @@ -212,7 +212,7 @@ local function harvest(blocks) local hi = Point.headings[(h + 2) % 4] -- opposite heading -- without pathfinding, will be unable to circle log - if turtle._goto({ x = b.x + hi.xd, z = b.z + hi.zd, heading = h }) then + if turtle.go({ x = b.x + hi.xd, z = b.z + hi.zd, heading = h }) then if turtle.dig() then turtle.place(crops[b.name].seed) end diff --git a/farms/superTreefarm.lua b/farms/superTreefarm.lua index 5de21c0..fdf8ed2 100644 --- a/farms/superTreefarm.lua +++ b/farms/superTreefarm.lua @@ -308,7 +308,7 @@ local function getCobblestone(count) until turtle.getItemCount(COBBLESTONE) >= count - turtle._goto(pt) + turtle.go(pt) turtle.placeDown(DIRT) turtle.drop(DIRT) diff --git a/farms/treefarm.lua b/farms/treefarm.lua index 4e28174..04dbf79 100644 --- a/farms/treefarm.lua +++ b/farms/treefarm.lua @@ -295,7 +295,7 @@ local function getCobblestone(count) until turtle.getItemCount(COBBLESTONE) >= count - turtle._goto(pt) + turtle.go(pt) turtle.placeDown(DIRT) turtle.drop(DIRT) diff --git a/gps/gpsServer.lua b/gps/gpsServer.lua index dc2d354..f247401 100644 --- a/gps/gpsServer.lua +++ b/gps/gpsServer.lua @@ -185,7 +185,7 @@ if args[1] == 'build' then turtle.setPoint({ x = 0, y = -y, z = 0, heading = 0 }) build() - turtle._goto({ x = 0, y = 1, z = 0, heading = 0 }) + turtle.go({ x = 0, y = 1, z = 0, heading = 0 }) configure() diff --git a/miners/findSwarm.lua b/miners/findSwarm.lua index d658838..c987e55 100644 --- a/miners/findSwarm.lua +++ b/miners/findSwarm.lua @@ -2,8 +2,8 @@ local Event = require('event') local GPS = require('gps') local Point = require('point') -local Socket = require('socket') local Sound = require('sound') +local Swarm = require('swarm') local Util = require('util') local device = _G.device @@ -19,7 +19,7 @@ end local paused, abort local chunkIndex = 0 -local pool = { } +local swarm = Swarm() local blocks = Util.transpose({ 'minecraft:chest', -- 'minecraft:mob_spawner', @@ -41,8 +41,6 @@ local function getLocations() end end -getLocations() - for _, b in pairs(scanner.scan()) do if b.name == 'computercraft:turtle_advanced' or b.name == 'computercraft:turtle' then @@ -54,46 +52,20 @@ for _, b in pairs(scanner.scan()) do elseif v.turtle.fuel < 100 then print('not enough fuel: ' .. v.computer.id) else - pool[v.computer.id] = { - id = v.computer.id, + swarm:add(v.computer.id, { point = { x = gpt.x + b.x, y = gpt.y + b.y, z = gpt.z + b.z, heading = Point.facings[v.state.facing].heading, }, - index = Util.size(pool), - } + index = Util.size(swarm.pool), + }) end end end end -local function hijackTurtle(remoteId) - local socket, msg = Socket.connect(remoteId, 188) - - if not socket then - error(msg) - end - - socket:write('turtle') - local methods = socket:read() - - local hijack = { } - for _,method in pairs(methods) do - hijack[method] = function(...) - socket:write({ fn = method, args = { ... } }) - local resp = socket:read() - if not resp then - error('timed out: ' .. method) - end - return table.unpack(resp) - end - end - - return hijack, socket -end - local function getNextPoint(member) local z = math.floor(chunkIndex / COLUMNS) local x = chunkIndex % COLUMNS @@ -107,85 +79,75 @@ local function getNextPoint(member) return { x = gpt.x + (x * 16), y = gpt.y + member.index, - z = gpt.z + (z * 16) } + z = gpt.z + (z * 16) + } end local function run(member) - Event.addRoutine(function() - local turtle, socket - local _, m = pcall(function() - member.active = true - turtle, socket = hijackTurtle(member.id) + local turtle = member.turtle - if turtle then - if not turtle.has('plethora:module:2') then - error('missing scanner') + if not turtle.has('plethora:module:2') then + error('missing scanner') + end + turtle.reset() + turtle.set({ + attackPolicy = 'attack', + digPolicy = 'turtleSafe', + movementStrategy = 'goto', + point = member.point, + }) + turtle.select(1) + + repeat + local pt = getNextPoint(member) + if pt then + turtle.go({ y = pt.y }) + while not turtle.go(pt) do + if abort then + break end - turtle.reset() - turtle.set({ - attackPolicy = 'attack', - digPolicy = 'turtleSafe', - movementStrategy = 'goto', - point = member.point, - }) - turtle.select(1) - - repeat - local pt = getNextPoint(member) - if pt then - turtle.gotoY(pt.y) - while not turtle._goto(pt) do - if abort then - break - end - os.sleep(.5) - end - - for _, v in ipairs(locations) do - if abort then - break - end - turtle.gotoY(v) - turtle.equip('right', 'plethora:module:2') - local found = turtle.scan(blocks) - turtle.equip('right', 'minecraft:diamond_pickaxe') - if Util.size(found) > 0 then - paused = true - local _, b = next(found) - print(string.format('%s:%s:%s %s', b.x, b.y, b.z, b.name)) - print('press r to continue') - for _ = 1, 3 do - Sound.play('block.note.pling') - os.sleep(.3) - end - end - end - turtle.gotoY(pt.y) - end - until abort - - turtle.gotoY(gpt.y + member.index) - turtle._goto({ x = gpt.x, y = gpt.y + member.index, z = gpt.z }) + os.sleep(.5) end - repeat until turtle.gotoY(gpt.y) - end) - - if m then - Sound.play('entity.villager.no') - _G.printError(m) + for _, v in ipairs(locations) do + if abort then + break + end + turtle.go({ y = v }) + turtle.equip('right', 'plethora:module:2') + local found = turtle.scan(blocks) + turtle.equip('right', 'minecraft:diamond_pickaxe') + if Util.size(found) > 0 then + paused = true + local _, b = next(found) + print(string.format('%s:%s:%s %s', b.x, b.y, b.z, b.name)) + print('press r to continue') + for _ = 1, 3 do + Sound.play('block.note.pling') + os.sleep(.3) + end + end + end + turtle.go({ y = pt.y }) end + until abort - pool[member.id] = nil - print('Turtles: ' .. Util.size(pool)) - if Util.size(pool) == 0 then - Event.exitPullEvents() - end + turtle.go({ y = gpt.y + member.index }) + turtle.go({ x = gpt.x, y = gpt.y + member.index, z = gpt.z }) - if socket then - socket:close() - end - end) + repeat until turtle.go({ y = gpt.y }) +end + +function swarm:onRemove(member, success, message) + if not success then + Sound.play('entity.villager.no') + _G.printError(message) + end + + print('Turtles: ' .. Util.size(self.pool)) + if Util.size(self.pool) == 0 then + Event.exitPullEvents() + end end print('press a to abort, r to resume') @@ -200,9 +162,9 @@ Event.on('char', function(_, k) end end) -Util.print('Found %s turtles', Util.size(pool)) -Util.each(pool, function(member) - run(member) -end) +getLocations() + +Util.print('Found %s turtles', Util.size(swarm.pool)) +swarm:run(run) Event.pullEvents() diff --git a/miners/multiMiner.lua b/miners/multiMiner.lua index 8c451be..96ea29b 100644 --- a/miners/multiMiner.lua +++ b/miners/multiMiner.lua @@ -104,7 +104,7 @@ local function run(member, point) local topPoint = Point.copy(chestPoint) topPoint.y = topPoint.y + 2 turtle.gotoY(topPoint.y) - while not turtle._goto(topPoint) do + while not turtle.go(topPoint) do os.sleep(.5) end @@ -196,14 +196,14 @@ local function run(member, point) if chestPoint then dropOff() - while not turtle._goto(Point.above(spt)) do + while not turtle.go(Point.above(spt)) do os.sleep(.5) end turtle.set({ digPolicy = 'dig' }) - turtle._goto(spt) + turtle.go(spt) else turtle.gotoY(spt.y) - turtle._goto(spt) + turtle.go(spt) end end) diff --git a/miners/scanningMiner.lua b/miners/scanningMiner.lua index 83c30b0..702eae5 100644 --- a/miners/scanningMiner.lua +++ b/miners/scanningMiner.lua @@ -233,7 +233,7 @@ end local function safeGoto(x, z, y, h) local oldStatus = turtle.getStatus() - while not turtle._goto({ x = x, z = z, y = y or turtle.point.y, heading = h }) do + while not turtle.go({ x = x, z = z, y = y or turtle.point.y, heading = h }) do status('stuck') if turtle.isAborted() then return false @@ -647,7 +647,7 @@ Event.addRoutine(function() status(success and 'finished' or turtle.isAborted() and 'aborting' or 'error') turtle.gotoY(0) - if turtle._goto(HOME_PT) then + if turtle.go(HOME_PT) then unload() end turtle.reset() diff --git a/miners/simpleMiner.lua b/miners/simpleMiner.lua index 4985d19..8fba0c4 100644 --- a/miners/simpleMiner.lua +++ b/miners/simpleMiner.lua @@ -1,4 +1,4 @@ -local Pathing = require('turtle.pathfind') +local Pathing = require('pathfind') local Point = require('point') local Util = require('util') diff --git a/turtle/.package b/turtle/.package new file mode 100644 index 0000000..82b146e --- /dev/null +++ b/turtle/.package @@ -0,0 +1,6 @@ +{ + title = 'Turtle programs and apis', + repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/turtle', + description = [[Support programs and apis for turtles]], + licence = 'MIT', +} diff --git a/turtle/apis/home.lua b/turtle/apis/home.lua new file mode 100644 index 0000000..4e552c8 --- /dev/null +++ b/turtle/apis/home.lua @@ -0,0 +1,42 @@ +local Config = require('config') +local GPS = require('gps') + +local turtle = _G.turtle + +local Home = { } + +function Home.go() + local config = { } + Config.load('gps', config) + + if config.home then + if turtle.enableGPS() then + return turtle.pathfind(config.home) + end + end +end + +function Home.set() + local config = { } + Config.load('gps', config) + + local pt = GPS.getPoint() + if pt then + local originalHeading = turtle.point.heading + local heading = GPS.getHeading() + if heading then + local turns = (turtle.point.heading - originalHeading) % 4 + pt.heading = (heading - turns) % 4 + config.home = pt + Config.update('gps', config) + + pt = GPS.getPoint() + pt.heading = heading + turtle.setPoint(pt, true) + turtle.go(config.home) + return config.home + end + end +end + +return Home diff --git a/turtle/apis/swarm.lua b/turtle/apis/swarm.lua new file mode 100644 index 0000000..cee1976 --- /dev/null +++ b/turtle/apis/swarm.lua @@ -0,0 +1,72 @@ +local Event = require('event') +local Socket = require('socket') +local Util = require('util') + +local function hijackTurtle(remoteId) + local socket, msg = Socket.connect(remoteId, 188) + + if not socket then + error(msg) + end + + socket:write('turtle') + local methods = socket:read() + + local hijack = { } + for _,method in pairs(methods) do + hijack[method] = function(...) + socket:write({ fn = method, args = { ... } }) + local resp = socket:read() + if not resp then + error('timed out: ' .. method) + end + return table.unpack(resp) + end + end + + return hijack, socket +end +local class = require('class') +local Swarm = class() +function Swarm:init(args) + self.pool = { } + Util.merge(self, args) +end +function Swarm:add(id, args) + local member = Util.shallowCopy(args) + member.id = id + self.pool[id] = member +end +function Swarm:run(fn) + for id, member in pairs(self.pool) do + Event.addRoutine(function() + local s, m = pcall(function() + member.turtle, member.socket = hijackTurtle(id) + + fn(member) + end) + if member.socket then + member.socket:close() + member.socket = nil + end + self.pool[id] = nil + self:onRemove(member, s, m) + end) + end +end +function Swarm:shutdown() + for _, member in pairs(self.pool) do + if member.socket then + member.socket:close() + member.socket = nil + end + end +end +function Swarm:onRemove(member, success, msg) + print('removed from pool: ' .. member.id) + if not success then + _G.printError(msg) + end +end + +return Swarm diff --git a/turtle/autorun/gps.lua b/turtle/autorun/gps.lua new file mode 100644 index 0000000..856ebad --- /dev/null +++ b/turtle/autorun/gps.lua @@ -0,0 +1,43 @@ +local modem = _G.device.wireless_modem +local turtle = _G.turtle + +if turtle and modem then + local s, m = turtle.run(function() + + _G.requireInjector(_ENV) + + local Config = require('config') + local config = { + destructive = false, + } + Config.load('gps', config) + + if config.home then + + local s = turtle.enableGPS(2) + if not s then + s = turtle.enableGPS(2) + end + if not s and config.destructive then + turtle.set({ attackPolicy = 'attack', digPolicy = 'turtleSafe' }) + s = turtle.enableGPS(2) + end + + if not s then + error('Unable to get GPS position') + end + + if config.destructive then + turtle.set({ attackPolicy = 'attack', digPolicy = 'turtleSafe' }) + end + + if not turtle.pathfind(config.home) then + error('Failed to return home') + end + end + end) + + if not s and m then + error(m) + end +end diff --git a/miners/autorun/startup.lua b/turtle/autorun/startup.lua similarity index 84% rename from miners/autorun/startup.lua rename to turtle/autorun/startup.lua index 67e8247..1a6f63d 100644 --- a/miners/autorun/startup.lua +++ b/turtle/autorun/startup.lua @@ -1,9 +1,12 @@ local Util = require('util') local device = _G.device +local fs = _G.fs local turtle = _G.turtle if turtle then + fs.mount('sys/apps/system/turtle.lua', 'linkfs', 'packages/turtle/system/turtle.lua') + function turtle.scan(blocks) local pt = turtle.point local scanner = device['plethora:scanner'] or error('Scanner not equipped') diff --git a/turtle/etc/apps.db b/turtle/etc/apps.db new file mode 100644 index 0000000..d7438d2 --- /dev/null +++ b/turtle/etc/apps.db @@ -0,0 +1,8 @@ +{ + [ "47b3d6dc1170faf4ae496fff3db374817bf32e2f" ] = { + title = "Refuel", + category = "Apps", + run = "lavaRefuel.lua", + requires = 'turtle', + }, +} diff --git a/core/lavaRefuel.lua b/turtle/lavaRefuel.lua similarity index 97% rename from core/lavaRefuel.lua rename to turtle/lavaRefuel.lua index ddd4770..fd3b184 100644 --- a/core/lavaRefuel.lua +++ b/turtle/lavaRefuel.lua @@ -56,7 +56,7 @@ local s, m = turtle.run(function() end) turtle.gotoY(0) -turtle._goto({ x = 0, y = 0, z = 0 }) +turtle.go({ x = 0, y = 0, z = 0 }) turtle.unequip('right') print('Fuel: ' .. turtle.getFuelLevel()) diff --git a/core/etc/scripts/obsidian b/turtle/obsidian.lua similarity index 93% rename from core/etc/scripts/obsidian rename to turtle/obsidian.lua index f4a5597..3be6e9b 100644 --- a/core/etc/scripts/obsidian +++ b/turtle/obsidian.lua @@ -1,5 +1,3 @@ -_G.requireInjector(_ENV) - local Point = require('point') local Util = require('util') @@ -67,7 +65,7 @@ local function findObsidian() end node = Point.closest(turtle.point, nodes) - if not turtle._goto(node) then + if not turtle.go(node) then break end until turtle.isAborted() @@ -91,7 +89,7 @@ turtle.run(function() if not turtle.select('minecraft:water_bucket') then break end - turtle._goto({ x = 0, z = 0 }) + turtle.go({ x = 0, z = 0 }) turtle.placeDown() os.sleep(2) turtle.placeDown() @@ -104,5 +102,5 @@ turtle.run(function() error(m) end - turtle._goto({ x = 0, y = 0, z = 0, heading = 0 }) + turtle.go({ x = 0, y = 0, z = 0, heading = 0 }) end) diff --git a/turtle/system/turtle.lua b/turtle/system/turtle.lua new file mode 100644 index 0000000..3be1e96 --- /dev/null +++ b/turtle/system/turtle.lua @@ -0,0 +1,63 @@ +local Config = require('config') +local UI = require('ui') + +local fs = _G.fs +local turtle = _G.turtle + +if turtle then + local Home = require('turtle.home') + + local values = { } + Config.load('gps', values.home and { values.home } or { }) + + local gpsTab = UI.Tab { + tabTitle = 'GPS', + labelText = UI.Text { + x = 3, y = 2, + value = 'On restart, return to this location' + }, + grid = UI.Grid { + x = 3, ex = -3, y = 4, + height = 2, + values = values, + inactive = true, + columns = { + { heading = 'x', key = 'x' }, + { heading = 'y', key = 'y' }, + { heading = 'z', key = 'z' }, + }, + }, + button1 = UI.Button { + x = 3, y = 7, + text = 'Set home', + event = 'gps_set', + }, + button2 = UI.Button { + ex = -3, y = 7, width = 7, + text = 'Clear', + event = 'gps_clear', + }, + } + function gpsTab:eventHandler(event) + if event.type == 'gps_set' then + self:emit({ type = 'info_message', message = 'Determining location' }) + self:sync() + if Home.set() then + Config.load('gps', values) + self.grid:setValues(values.home and { values.home } or { }) + self.grid:draw() + self:emit({ type = 'success_message', message = 'Location set' }) + else + self:emit({ type = 'error_message', message = 'Unable to determine location' }) + end + return true + elseif event.type == 'gps_clear' then + fs.delete('usr/config/gps') + self.grid:setValues({ }) + self.grid:draw() + return true + end + end + + return gpsTab +end diff --git a/core/t.lua b/turtle/t.lua similarity index 100% rename from core/t.lua rename to turtle/t.lua