From 4a844eebfe84c052e464e5513d16b346f1c80527 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 1 Dec 2018 19:08:57 -0500 Subject: [PATCH] milo: smart transfer --- milo/Milo.lua | 24 +++++- milo/apis/craft2.lua | 23 +++--- milo/apis/milo.lua | 6 +- milo/apis/storage.lua | 125 +++++++++++++++++++++++------- milo/core/machines.lua | 7 +- milo/plugins/brewingStandView.lua | 2 +- milo/plugins/craftTask.lua | 5 +- milo/plugins/exportTask.lua | 6 +- milo/plugins/exportView.lua | 2 +- milo/plugins/importTask.lua | 2 +- milo/plugins/importView.lua | 2 +- milo/plugins/inputChestTask.lua | 6 +- milo/plugins/learn.lua | 6 +- milo/plugins/limitTask.lua | 2 +- milo/plugins/machineLearn.lua | 2 - milo/plugins/manipulatorView.lua | 14 +--- milo/plugins/potionImportTask.lua | 4 +- milo/plugins/remote.lua | 58 +++++++------- milo/plugins/turtleLearn.lua | 2 - 19 files changed, 177 insertions(+), 121 deletions(-) diff --git a/milo/Milo.lua b/milo/Milo.lua index 945a0d7..a7350f1 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -47,7 +47,7 @@ if nodes.nodes then input = 'custom', trashcan = 'custom', machine = 'machine', - brewingStand = 'custom', + brewingStand = 'machine', activity = 'display', jobs = 'display', ignore = 'ignore', @@ -105,6 +105,8 @@ if not device.workbench then end end +local localName = modem.getNameLocal() + local context = { nodes = nodes, resources = Util.readTable(Milo.RESOURCE_FILE) or { }, @@ -115,20 +117,28 @@ local context = { tasks = { }, queue = { }, - localName = modem.getNameLocal(), storage = Storage(nodes), - turtleInventory = introspection.getInventory(), + turtleInventory = { + name = localName, + mtype = 'hidden', + adapter = introspection.getInventory(), + } } -device[context.localName] = introspection.getInventory() +nodes[localName] = context.turtleInventory +nodes[localName].adapter.name = localName _G._p = context --debug Milo:init(context) context.storage:initStorage() +-- TODO: fix +context.storage.turtleInventory = context.turtleInventory + local function loadDirectory(dir) for _, file in pairs(fs.list(dir)) do +_debug('loading: ' .. file) local s, m = Util.run(_ENV, fs.combine(dir, file)) if not s and m then error(m or 'Unknown error') @@ -190,6 +200,12 @@ Event.on('milo_queue', function() end end) +Event.on('turtle_inventory', function() + if not processing and not Milo:isCraftingPaused() then + Milo:clearGrid() + end +end) + Event.onInterval(5, function() if not Milo:isCraftingPaused() then os.queueEvent('milo_cycle') diff --git a/milo/apis/craft2.lua b/milo/apis/craft2.lua index 3d6c470..ed48863 100644 --- a/milo/apis/craft2.lua +++ b/milo/apis/craft2.lua @@ -35,17 +35,14 @@ local function makeRecipeKey(item) end function Craft.clearGrid(storage) - turtle.eachFilledSlot(function(slot) - storage:import(storage.localName, slot.index, slot.count, slot) - end) + local success = true - for i = 1, 16 do - if turtle.getItemCount(i) ~= 0 then - return false + for index, slot in pairs(storage.turtleInventory.adapter.list()) do + if storage:import(storage.turtleInventory, index, slot.count, slot) ~= slot.count then + success = false end end - - return true + return success end function Craft.getItemCount(items, item) @@ -77,7 +74,7 @@ function Craft.sumIngredients(recipe) end local function machineCraft(recipe, storage, machineName, request, count, item) - local machine = device[machineName] + local machine = storage.nodes[machineName] if not machine then request.status = 'machine not found' request.statusCode = Craft.STATUS_ERROR @@ -85,7 +82,7 @@ local function machineCraft(recipe, storage, machineName, request, count, item) end for k in pairs(recipe.ingredients) do - if machine.getItemMeta(k) then + if machine.adapter.getItemMeta(k) then request.status = 'machine in use' request.statusCode = Craft.STATUS_WARNING return @@ -94,7 +91,7 @@ local function machineCraft(recipe, storage, machineName, request, count, item) local xferred = { } for k,v in pairs(recipe.ingredients) do - local provided = storage:export(machineName, k, count, splitKey(v)) + local provided = storage:export(machine, k, count, splitKey(v)) xferred[k] = { key = v, count = provided, @@ -103,7 +100,7 @@ local function machineCraft(recipe, storage, machineName, request, count, item) -- take back out whatever we put in for k2,v2 in pairs(xferred) do if v2.count > 0 then - storage:import(machineName, k2, v2.count, splitKey(v2.key)) + storage:import(machine, k2, v2.count, splitKey(v2.key)) end end request.status = 'Invalid recipe' @@ -125,7 +122,7 @@ local function turtleCraft(recipe, storage, request, count) for k,v in pairs(recipe.ingredients) do local item = splitKey(v) - if storage:export(storage.localName, k, count, item) ~= count then + if storage:export(storage.turtleInventory, k, count, item) ~= count then request.status = 'unknown error' request.statusCode = Craft.STATUS_ERROR return diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index 90d0928..6e1ba94 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -134,8 +134,8 @@ end function Milo:getTurtleInventory() local list = { } - for i = 1,16 do - local item = self.context.turtleInventory.getItemMeta(i) + for i in pairs(self.context.turtleInventory.adapter.list()) do + local item = self.context.turtleInventory.adapter.getItemMeta(i) if item and not itemDB:get(item) then itemDB:add(item) end @@ -225,7 +225,7 @@ function Milo:makeRequest(item, count, callback) end function Milo:eject(item, count) - count = self.context.storage:export(self.context.storage.localName, nil, count, item) + count = self.context.storage:export(self.context.turtleInventory, nil, count, item) turtle.emptyInventory() return count end diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 962e3a7..3b5e2d1 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -1,9 +1,8 @@ -local class = require('class') -local Event = require('event') -local InventoryAdapter = require('inventoryAdapter') -local itemDB = require('itemDB') -local Peripheral = require('peripheral') -local Util = require('util') +local class = require('class') +local Event = require('event') +local Adapter = require('inventoryAdapter') +local itemDB = require('itemDB') +local Util = require('util') local device = _G.device local os = _G.os @@ -20,9 +19,6 @@ function Storage:init(nodes) } Util.merge(self, defaults) - local modem = Peripheral.get('wired_modem') or error('Wired modem not attached') - self.localName = modem.getNameLocal() - Event.on({ 'device_attach', 'device_detach' }, function(e, dev) _G._debug('%s: %s', e, tostring(dev)) self:initStorage() @@ -34,9 +30,13 @@ end function Storage:showStorage() local t = { } + local ignores = { + ignore = true, + hidden = true, + } for k,v in pairs(self.nodes) do local online = v.adapter and v.adapter.online - if not online and v.mtype ~= 'ignore' then + if not online and not ignores[v.mtype] then table.insert(t, k) end end @@ -58,18 +58,20 @@ function Storage:initStorage() _G._debug('Initializing storage') for k,v in pairs(self.nodes) do - if v.adapter then - v.adapter.online = not not device[k] - elseif device[k] and device[k].list and device[k].size and device[k].pullItems then - v.adapter = InventoryAdapter.wrap({ side = k }) - v.adapter.online = true - v.adapter.dirty = true - elseif device[k] then - v.adapter = device[k] - v.adapter.online = true - end - if v.mtype == 'storage' then - online = online and not not (v.adapter and v.adapter.online) + if v.mtype ~= 'hidden' then + if v.adapter then + v.adapter.online = not not device[k] + elseif device[k] and device[k].list and device[k].size and device[k].pullItems then + v.adapter = Adapter.wrap({ side = k }) + v.adapter.online = true + v.adapter.dirty = true + elseif device[k] then + v.adapter = device[k] + v.adapter.online = true + end + if v.mtype == 'storage' then + online = online and not not (v.adapter and v.adapter.online) + end end end @@ -247,6 +249,10 @@ function Storage:updateCache(adapter, item, count) end function Storage:_sn(name) + if not name then + error('Invalid target', 3) + end + local node = self.nodes[name] if node and node.displayName then return node.displayName @@ -258,16 +264,58 @@ function Storage:_sn(name) return table.concat(t, '_') end +local function isValidTransfer(adapter, target) + for _,v in pairs(adapter.getTransferLocations()) do + if v == target then + return true + end + end +end + +local function rawExport(source, target, item, qty, slot) + local total = 0 + local push = isValidTransfer(source, target.name) + + local s, m = pcall(function() + local stacks = source.list() + for key,stack in Util.rpairs(stacks) do + if stack.name == item.name and + stack.damage == item.damage and + stack.nbtHash == item.nbtHash then + local amount = math.min(qty, stack.count) + if amount > 0 then + if push then + amount = source.pushItems(target.name, key, amount, slot) + else + amount = target.pullItems(source.name, key, amount, slot) + end + end + qty = qty - amount + total = total + amount + if qty <= 0 then + break + end + end + end + end) + + if not s and m then + _debug(m) + end + + return total, m +end + function Storage:export(target, slot, count, item) local total = 0 local key = item.key or table.concat({ item.name, item.damage, item.nbtHash }, ':') local function provide(adapter) - local amount = adapter:provide(item, count, slot, target) + local amount = rawExport(adapter, target.adapter, item, count, slot) if amount > 0 then _G._debug('EXT: %s(%d): %s -> %s%s', - item.displayName or item.name, amount, self:_sn(adapter.name), self:_sn(target), + item.displayName or item.name, amount, self:_sn(adapter.name), self:_sn(target.name), slot and string.format('[%d]', slot) or '[*]') self:updateCache(adapter, item, -amount) @@ -287,7 +335,7 @@ function Storage:export(target, slot, count, item) end _G._debug('MISS: %s(%d): %s%s %s', - item.displayName or item.name, count, self:_sn(target), + item.displayName or item.name, count, self:_sn(target.name), slot and string.format('[%d]', slot) or '[*]', key) -- TODO: If there are misses when a slot is specified than something is wrong... @@ -298,6 +346,23 @@ function Storage:export(target, slot, count, item) return total end +local function rawInsert(source, target, slot, qty) + local count = 0 + + local s, m = pcall(function() + if isValidTransfer(source, target.name) then + count = source.pullItems(target.name, slot, qty) + else + count = target.pushItems(source.name, slot, qty) + end + end) + if not s and m then + _debug(m) + end + + return count +end + function Storage:import(source, slot, count, item) if not source then error('Storage:import: source is required') end if not slot then error('Storage:import: slot is required') end @@ -316,19 +381,19 @@ function Storage:import(source, slot, count, item) entry = itemDB:add(item) else -- get the metadata from the device and add to db - entry = itemDB:add(device[source].getItemMeta(slot)) + entry = itemDB:add(source.adapter.getItemMeta(slot)) end itemDB:flush() end item = entry local function insert(adapter) - local amount = adapter:insert(slot, count, nil, source) + local amount = rawInsert(adapter, source.adapter, slot, count) if amount > 0 then _G._debug('INS: %s(%d): %s[%d] -> %s', item.displayName or item.name, amount, - self:_sn(source), slot, self:_sn(adapter.name)) + self:_sn(source.name), slot, self:_sn(adapter.name)) self:updateCache(adapter, item, amount) @@ -384,9 +449,9 @@ function Storage:trash(source, slot, count) local trashcan = Util.find(self.nodes, 'mtype', 'trashcan') if trashcan and trashcan.adapter and trashcan.adapter.online then - _G._debug('TRA: %s[%d] (%d)', self:_sn(source), slot, count or 64) + _G._debug('TRA: %s[%d] (%d)', self:_sn(source.name), slot, count or 64) - return trashcan.adapter.pullItems(source, slot, count) + return trashcan.adapter.pullItems(source.name, slot, count) end return 0 end diff --git a/milo/core/machines.lua b/milo/core/machines.lua index 9856e98..6a88a29 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -2,7 +2,6 @@ local Config = require('config') local Event = require('event') local itemDB = require('itemDB') local Milo = require('milo') -local sync = require('sync') local UI = require('ui') local Util = require('util') @@ -48,8 +47,7 @@ local networkPage = UI.Page { { key = 'suffix', width = 4, justify = 'right' }, { heading = 'Name', key = 'displayName' }, { heading = 'Type', key = 'mtype', width = 4 }, - { heading = 'Cat', key = 'category', width = 3 }, - --{ heading = 'Pri', key = 'priority', width = 3 }, + { heading = 'Pri', key = 'priority', width = 3 }, }, sortColumn = 'displayName', help = 'Select Node', @@ -136,7 +134,6 @@ function networkPage:enable() self:getList() self:applyFilter() self.grid:draw() - self.grid:sync() updateStatus() self:sync() end) @@ -331,13 +328,11 @@ function nodeWizard.filter:show(entry, callback, whitelistOnly) self:setFocus(self.form.scan) Milo:pauseCrafting({ key = 'gridInUse', msg = 'Crafting paused' }) - sync.lock(turtle) end function nodeWizard.filter:hide() UI.SlideOut.hide(self) Milo:resumeCrafting({ key = 'gridInUse' }) - sync.release(turtle) end function nodeWizard.filter:resetGrid() diff --git a/milo/plugins/brewingStandView.lua b/milo/plugins/brewingStandView.lua index ec7aba4..c63cdc1 100644 --- a/milo/plugins/brewingStandView.lua +++ b/milo/plugins/brewingStandView.lua @@ -30,7 +30,7 @@ function brewingStandView:isValidType(node) return m and m.type == 'minecraft:brewing_stand'and { name = 'Brewing Stand', value = 'brewingStand', - category = 'custom', + category = 'machine', help = 'Auto-learning brewing stand', } end diff --git a/milo/plugins/craftTask.lua b/milo/plugins/craftTask.lua index a00b606..d748e6d 100644 --- a/milo/plugins/craftTask.lua +++ b/milo/plugins/craftTask.lua @@ -1,6 +1,5 @@ local Craft = require('craft2') local Milo = require('milo') -local sync = require('sync').sync local Util = require('util') local context = Milo:getContext() @@ -63,9 +62,7 @@ function craftTask:cycle() if item.requested - item.crafted > 0 then local recipe = Craft.findRecipe(key) if recipe then - sync(turtle, function() - self:craft(recipe, item) - end) + self:craft(recipe, item) if item.callback and item.crafted >= item.requested then item.callback(item) -- invoke callback end diff --git a/milo/plugins/exportTask.lua b/milo/plugins/exportTask.lua index ef00d3f..2e90c5b 100644 --- a/milo/plugins/exportTask.lua +++ b/milo/plugins/exportTask.lua @@ -39,7 +39,7 @@ function ExportTask:cycle(context) local _, item = next(items) if item then local count = math.min(item.count, slot.maxCount - slot.count) - context.storage:export(node.name, entry.slot, count, item) + context.storage:export(node, entry.slot, count, item) end break end @@ -53,7 +53,7 @@ function ExportTask:cycle(context) local _, item = next(items) if item then local count = math.min(item.count, itemDB:getMaxCount(item)) - context.storage:export(node.name, entry.slot, count, item) + context.storage:export(node, entry.slot, count, item) break end end @@ -63,7 +63,7 @@ function ExportTask:cycle(context) for key in pairs(entry.filter) do local items = Milo:getMatches(itemDB:splitKey(key), entry) for _,item in pairs(items) do - if context.storage:export(node.name, nil, item.count, item) == 0 then + if context.storage:export(node, nil, item.count, item) == 0 then -- TODO: really shouldn't break here as there may be room in other slots -- leaving for now for performance reasons break diff --git a/milo/plugins/exportView.lua b/milo/plugins/exportView.lua index 192ab4d..7b1aa53 100644 --- a/milo/plugins/exportView.lua +++ b/milo/plugins/exportView.lua @@ -6,7 +6,7 @@ local colors = _G.colors local device = _G.device local exportView = UI.Window { - title = 'Export item into machine', + title = 'Export item into inventory', index = 3, grid = UI.ScrollingGrid { x = 2, ex = -6, y = 2, ey = -4, diff --git a/milo/plugins/importTask.lua b/milo/plugins/importTask.lua index 1ec15b5..5664c3c 100644 --- a/milo/plugins/importTask.lua +++ b/milo/plugins/importTask.lua @@ -44,7 +44,7 @@ function ImportTask:cycle(context) local function importSlot(slotNo) local item = node.adapter.getItemMeta(slotNo) if item and matchesFilter(item) then - context.storage:import(node.name, slotNo, item.count, item) + context.storage:import(node, slotNo, item.count, item) end end diff --git a/milo/plugins/importView.lua b/milo/plugins/importView.lua index 80131dd..671821b 100644 --- a/milo/plugins/importView.lua +++ b/milo/plugins/importView.lua @@ -6,7 +6,7 @@ local colors = _G.colors local device = _G.device local importView = UI.Window { - title = 'Import item from machine', + title = 'Import item from inventory', index = 4, grid = UI.ScrollingGrid { x = 2, ex = -6, y = 2, ey = -4, diff --git a/milo/plugins/inputChestTask.lua b/milo/plugins/inputChestTask.lua index 198d1e8..85a20d2 100644 --- a/milo/plugins/inputChestTask.lua +++ b/milo/plugins/inputChestTask.lua @@ -6,9 +6,9 @@ local InputChest = { } function InputChest:cycle(context) - for inventory in context.storage:filterActive('input') do - for slot, item in pairs(inventory.adapter.list()) do - context.storage:import(inventory.name, slot, item.count, item) + for node in context.storage:filterActive('input') do + for slot, item in pairs(node.adapter.list()) do + context.storage:import(node, slot, item.count, item) end end end diff --git a/milo/plugins/learn.lua b/milo/plugins/learn.lua index 0c9460e..21fa1ef 100644 --- a/milo/plugins/learn.lua +++ b/milo/plugins/learn.lua @@ -1,9 +1,8 @@ local Milo = require('milo') -local sync = require('sync') local UI = require('ui') local context = Milo:getContext() -local turtle = _G.turtle +local turtle = _G.turtle local learnPage = UI.Dialog { height = 9, width = UI.term.width - 6, @@ -39,7 +38,6 @@ function learnPage:enable() self.grid:setSelected('name', Milo:getState('learnType') or '') Milo:pauseCrafting({ key = 'gridInUse', msg = 'Crafting paused' }) - sync.lock(turtle) self:focusFirst() UI.Dialog.enable(self) @@ -51,8 +49,8 @@ end function learnPage:eventHandler(event) if event.type == 'cancel' then - sync.release(turtle) Milo:resumeCrafting({ key = 'gridInUse' }) + turtle.emptyInventory() UI:setPreviousPage() elseif event.type == 'accept' or event.type == 'grid_select' then diff --git a/milo/plugins/limitTask.lua b/milo/plugins/limitTask.lua index ec072d4..1de414e 100644 --- a/milo/plugins/limitTask.lua +++ b/milo/plugins/limitTask.lua @@ -16,7 +16,7 @@ function LimitTask:cycle(context) local amount = count - res.limit for _, item in pairs(items) do amount = amount - context.storage:export( - trashcan.name, + trashcan, nil, math.min(amount, item.count), item) diff --git a/milo/plugins/machineLearn.lua b/milo/plugins/machineLearn.lua index b729ce5..9a743c4 100644 --- a/milo/plugins/machineLearn.lua +++ b/milo/plugins/machineLearn.lua @@ -1,6 +1,5 @@ local itemDB = require('itemDB') local Milo = require('milo') -local sync = require('sync') local UI = require('ui') local Util = require('util') @@ -133,7 +132,6 @@ end function machineLearnWizard:disable() Milo:resumeCrafting({ key = 'gridInUse' }) - sync.release(turtle) UI.Page.disable(self) end diff --git a/milo/plugins/manipulatorView.lua b/milo/plugins/manipulatorView.lua index 87b2335..87bd2b2 100644 --- a/milo/plugins/manipulatorView.lua +++ b/milo/plugins/manipulatorView.lua @@ -1,11 +1,9 @@ local Ansi = require('ansi') local Milo = require('milo') -local Sync = require('sync') local UI = require('ui') local colors = _G.colors local device = _G.device -local turtle = _G.turtle --[[ Configuration Screen ]]-- local wizardPage = UI.Window { @@ -57,7 +55,7 @@ function wizardPage:validate() return self.form:save() end -UI:getPage('nodeWizard').wizard:add({ manipulator = wizardPage }) +--UI:getPage('nodeWizard').wizard:add({ manipulator = wizardPage }) --[[ Task ]]-- local task = { @@ -72,15 +70,9 @@ function task:cycle(context) for manipulator in context.storage:filterActive('manipulator', filter) do for slot, item in pairs(manipulator.adapter.getEnder().list()) do - Sync.sync(turtle, function() - manipulator.adapter.getEnder().pushItems( - context.localName, - slot, - item.count) - Milo:clearGrid() - end) + context.storage:import('joebodo:enderChest', slot, item.count, item) end end end -Milo:registerTask(task) +--Milo:registerTask(task) diff --git a/milo/plugins/potionImportTask.lua b/milo/plugins/potionImportTask.lua index e5b6e9c..a455793 100644 --- a/milo/plugins/potionImportTask.lua +++ b/milo/plugins/potionImportTask.lua @@ -23,7 +23,7 @@ function PotionImportTask:cycle(context) if not list[5] then local blazePowder = context.storage.cache[BLAZE_POWDER] if blazePowder then - context.storage:export(bs.name, 5, 1, blazePowder) + context.storage:export(bs, 5, 1, blazePowder) else local item = itemDB:get(BLAZE_POWDER) if item then @@ -45,7 +45,7 @@ function PotionImportTask:cycle(context) for slot = 1, 3 do if list[slot] then - context.storage:import(bs.name, slot, 1, list[slot]) + context.storage:import(bs, slot, 1, list[slot]) end end end diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index 1623168..ee74d13 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -2,10 +2,8 @@ local Event = require('event') local itemDB = require('itemDB') local Milo = require('milo') local Socket = require('socket') -local Sync = require('sync') local device = _G.device -local turtle = _G.turtle local SHIELD_SLOT = 2 @@ -61,6 +59,17 @@ local function client(socket) data = 'ok', }) + local function makeNode(devType) + local devName = user .. ':' .. devType + local adapter = device[devName] + if adapter then + return { + adapter = adapter, + name = devName, + } + end + end + repeat local data = socket:read() if not data then @@ -77,20 +86,20 @@ local function client(socket) elseif data.request == 'deposit' then local function deposit() - Sync.sync(turtle, function() - if data.slot == 'shield' then - manipulator.getEquipment().pushItems( - context.localName, - SHIELD_SLOT, - data.count) - else - manipulator.getInventory().pushItems( - context.localName, - data.slot, - data.count) + local devType = 'inventory' + local slotNo = data.slot + if data.slot == 'shield' then + slotNo = SHIELD_SLOT + devType = 'equipment' + end + + local node = makeNode(devType) + if node then + local slot = node.adapter.getItemMeta(slotNo) + if slot then + context.storage:import(node, slotNo, slot.count, slot) end - Milo:clearGrid() - end) + end end local list = Milo:listItems() @@ -114,23 +123,14 @@ local function client(socket) end local function transfer(request) - Sync.sync(turtle, function() - local transferred = context.storage:export( - context.localName, + local target = makeNode('inventory') + if target then + context.storage:export( + target, nil, request.requested, data.item) - - if transferred > 0 then - turtle.eachFilledSlot(function(slot) - manipulator.getInventory().pullItems( - context.localName, - slot.index, - slot.count) - end) - end - Milo:clearGrid() -- in case all items do not fit in user's inventory - end) + end end local request = Milo:makeRequest(data.item, count, transfer) diff --git a/milo/plugins/turtleLearn.lua b/milo/plugins/turtleLearn.lua index 10948c7..008a4a8 100644 --- a/milo/plugins/turtleLearn.lua +++ b/milo/plugins/turtleLearn.lua @@ -1,7 +1,6 @@ local Craft = require('craft2') local itemDB = require('itemDB') local Milo = require('milo') -local sync = require('sync') local UI = require('ui') local Util = require('util') @@ -125,7 +124,6 @@ local turtleLearnWizard = UI.Page { function turtleLearnWizard:disable() Milo:resumeCrafting({ key = 'gridInUse' }) - sync.release(turtle) UI.Page.disable(self) end