milo better user experience
This commit is contained in:
@@ -31,6 +31,7 @@ local function safeString(text)
|
||||
end
|
||||
|
||||
function itemDB:makeKey(item)
|
||||
if not item then error('itemDB:makeKey: item is required', 2) end
|
||||
return table.concat({ item.name, item.damage or '*', item.nbtHash }, ':')
|
||||
end
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ local function harvest(blocks)
|
||||
elseif b.action == 'bump' then
|
||||
if turtle.faceAgainst(b) then
|
||||
turtle.equip('right', 'plethora:module:3')
|
||||
os.sleep(.3)
|
||||
os.sleep(.5)
|
||||
-- search the ground for the dropped cactus
|
||||
local sensed = peripheral.call('right', 'sense')
|
||||
turtle.equip('right', 'minecraft:diamond_pickaxe')
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
_G.requireInjector(_ENV)
|
||||
|
||||
local Config = require('config')
|
||||
local Event = require('event')
|
||||
local Socket = require('socket')
|
||||
local sync = require('sync').sync
|
||||
@@ -13,48 +14,26 @@ local socket
|
||||
|
||||
local SHIELD_SLOT = 2
|
||||
|
||||
local options = {
|
||||
user = { arg = 'u', type = 'string',
|
||||
desc = 'User name associated with bound manipulator' },
|
||||
slot = { arg = 's', type = 'number',
|
||||
desc = 'Optional inventory slot to use to transfer to milo' },
|
||||
shield = { arg = 'e', type = 'flag',
|
||||
desc = 'Use shield slot to use to transfer to milo' },
|
||||
server = { arg = 'm', type = 'number',
|
||||
desc = 'ID of Milo server' },
|
||||
help = { arg = 'h', type = 'flag', value = false,
|
||||
desc = 'Displays the options' },
|
||||
}
|
||||
|
||||
local args = { ... }
|
||||
if not Util.getOptions(options, args) then
|
||||
print()
|
||||
error('Invalid arguments')
|
||||
end
|
||||
|
||||
if not options.user.value or not options.server.value then
|
||||
Util.showOptions(options)
|
||||
print()
|
||||
error('Invalid arguments')
|
||||
end
|
||||
|
||||
if (options.slot.value or options.shield.value) and
|
||||
not (device.neuralInterface and device.neuralInterface.getInventory) then
|
||||
error('Introspection module is required for transferring items')
|
||||
end
|
||||
local config = Config.load('miloRemote', { })
|
||||
|
||||
local page = UI.Page {
|
||||
dummy = UI.Window {
|
||||
x = 1, ex = -10, y = 1, height = 1,
|
||||
x = 1, ex = -13, y = 1, height = 1,
|
||||
infoBar = UI.StatusBar {
|
||||
backgroundColor = colors.lightGray,
|
||||
},
|
||||
},
|
||||
refresh = UI.Button {
|
||||
y = 1, x = -9,
|
||||
y = 1, x = -12,
|
||||
event = 'refresh',
|
||||
text = 'Refresh',
|
||||
},
|
||||
setupButton = UI.Button {
|
||||
y = 1, x = -3,
|
||||
event = 'setup',
|
||||
text = '\206',
|
||||
help = 'Configuration',
|
||||
},
|
||||
grid = UI.Grid {
|
||||
y = 2, ey = -2,
|
||||
columns = {
|
||||
@@ -106,6 +85,46 @@ local page = UI.Page {
|
||||
|
||||
q = 'quit',
|
||||
},
|
||||
setup = UI.SlideOut {
|
||||
backgroundColor = colors.cyan,
|
||||
titleBar = UI.TitleBar {
|
||||
title = 'Remote Setup',
|
||||
},
|
||||
form = UI.Form {
|
||||
x = 2, ex = -2, y = 2, ey = -1,
|
||||
values = config,
|
||||
[1] = UI.TextEntry {
|
||||
formLabel = 'Server', formKey = 'server',
|
||||
help = 'ID for the server',
|
||||
shadowText = 'Milo server ID',
|
||||
limit = 6,
|
||||
validate = 'numeric',
|
||||
required = true,
|
||||
},
|
||||
[2] = UI.TextEntry {
|
||||
formLabel = 'User Name', formKey = 'user',
|
||||
help = 'User name for bound manipulator',
|
||||
shadowText = 'User name',
|
||||
limit = 50,
|
||||
required = true,
|
||||
},
|
||||
[3] = UI.TextEntry {
|
||||
formLabel = 'Return Slot', formKey = 'slot',
|
||||
help = 'Use a slot for sending to storage',
|
||||
shadowText = 'Inventory slot #',
|
||||
limit = 5,
|
||||
validate = 'numeric',
|
||||
required = true,
|
||||
},
|
||||
[4] = UI.Checkbox {
|
||||
formLabel = 'Shield Slot', formKey = 'useShield',
|
||||
help = 'or use the shield slot for sending'
|
||||
},
|
||||
},
|
||||
statusBar = UI.StatusBar {
|
||||
backgroundColor = colors.cyan,
|
||||
},
|
||||
},
|
||||
displayMode = 0,
|
||||
items = { },
|
||||
}
|
||||
@@ -139,14 +158,22 @@ end
|
||||
function page:sendRequest(data)
|
||||
local response
|
||||
|
||||
if not config.server then
|
||||
self:setStatus('Invalid configuration')
|
||||
Event.onTimeout(2, function()
|
||||
self:setStatus('')
|
||||
end)
|
||||
return
|
||||
end
|
||||
|
||||
sync(self, function()
|
||||
local msg
|
||||
for _ = 1, 2 do
|
||||
if not socket or not socket.connected then
|
||||
self:setStatus('connecting ...')
|
||||
socket, msg = Socket.connect(options.server.value, 4242)
|
||||
socket, msg = Socket.connect(config.server, 4242)
|
||||
if socket then
|
||||
socket:write(options.user.value)
|
||||
socket:write(config.user)
|
||||
local r = socket:read(2)
|
||||
if r and not r.msg then
|
||||
self:setStatus('connected ...')
|
||||
@@ -203,7 +230,6 @@ end
|
||||
function page:transfer(item, count)
|
||||
local response = self:sendRequest({ request = 'transfer', item = item, count = count })
|
||||
if response then
|
||||
_debug(response)
|
||||
item.count = response.current - response.count
|
||||
self.grid:draw()
|
||||
if response.craft > 0 then
|
||||
@@ -214,10 +240,29 @@ function page:transfer(item, count)
|
||||
end
|
||||
end
|
||||
|
||||
function page.setup:eventHandler(event)
|
||||
if event.type == 'focus_change' then
|
||||
self.statusBar:setStatus(event.focused.help)
|
||||
end
|
||||
return UI.SlideOut.eventHandler(self, event)
|
||||
end
|
||||
|
||||
function page:eventHandler(event)
|
||||
if event.type == 'quit' then
|
||||
UI:exitPullEvents()
|
||||
|
||||
elseif event.type == 'setup' then
|
||||
self.setup:show()
|
||||
|
||||
elseif event.type == 'form_complete' then
|
||||
Config.update('miloRemote', config)
|
||||
self.setup:hide()
|
||||
self:refresh()
|
||||
self.grid:draw()
|
||||
|
||||
elseif event.type == 'form_cancel' then
|
||||
self.setup:hide()
|
||||
|
||||
elseif event.type == 'focus_change' then
|
||||
self.dummy.infoBar:setStatus(event.focused.help)
|
||||
|
||||
@@ -290,6 +335,9 @@ end
|
||||
function page:enable()
|
||||
self:setFocus(self.statusBar.filter)
|
||||
UI.Page.enable(self)
|
||||
if not config.server then
|
||||
self.setup:show()
|
||||
end
|
||||
Event.onTimeout(.1, function()
|
||||
self:refresh()
|
||||
self.grid:draw()
|
||||
@@ -311,31 +359,21 @@ function page:applyFilter()
|
||||
self.grid:setValues(t)
|
||||
end
|
||||
|
||||
if options.slot.value or options.shield.value then
|
||||
local inv = 'getInventory'
|
||||
local slotNo = options.slot.value
|
||||
local slotValue = options.slot.value
|
||||
|
||||
if options.shield.value then
|
||||
slotNo = SHIELD_SLOT
|
||||
slotValue = 'shield'
|
||||
inv = 'getEquipment'
|
||||
end
|
||||
|
||||
Event.addRoutine(function()
|
||||
while true do
|
||||
os.sleep(1.5)
|
||||
local neural = device.neuralInterface
|
||||
if not neural or not neural[inv] then
|
||||
_G._debug('missing Introspection module')
|
||||
end
|
||||
|
||||
local method = neural and neural[inv]
|
||||
local item = method and method().getItemMeta(slotNo)
|
||||
Event.addRoutine(function()
|
||||
while true do
|
||||
os.sleep(1.5)
|
||||
local neural = device.neuralInterface
|
||||
local inv = config.useShield and 'getEquipment' or 'getInventory'
|
||||
if not neural or not neural[inv] then
|
||||
_G._debug('missing Introspection module')
|
||||
elseif config.server then
|
||||
local method = neural[inv]
|
||||
local item = method and method().getItemMeta(config.useShield and SHIELD_SLOT or config.slot)
|
||||
if item then
|
||||
local slotNo = config.useShield and 'shield' or config.slot
|
||||
local response = page:sendRequest({
|
||||
request = 'deposit',
|
||||
slot = slotValue,
|
||||
slot = slotNo,
|
||||
count = item.count,
|
||||
key = table.concat({ item.name, item.damage, item.nbtHash }, ':')
|
||||
})
|
||||
@@ -349,8 +387,8 @@ if options.slot.value or options.shield.value then
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
UI:setPage(page)
|
||||
UI:pullEvents()
|
||||
|
||||
8
milo/autorun/milo.lua
Normal file
8
milo/autorun/milo.lua
Normal file
@@ -0,0 +1,8 @@
|
||||
local device = _G.device
|
||||
local shell = _ENV.shell
|
||||
|
||||
if device.workbench then
|
||||
shell.openForegroundTab('Milo')
|
||||
elseif device.neuralInterface then
|
||||
shell.openForegroundTab('MiloRemote')
|
||||
end
|
||||
@@ -62,6 +62,9 @@ local networkPage = UI.Page {
|
||||
backgroundColor = colors.lightGray,
|
||||
},
|
||||
notification = UI.Notification { },
|
||||
accelerators = {
|
||||
delete = 'remove_node',
|
||||
}
|
||||
}
|
||||
|
||||
function networkPage.grid:getDisplayValues(row)
|
||||
@@ -236,7 +239,9 @@ The settings will take effect immediately!]],
|
||||
{ heading = 'Name', key = 'displayName' },
|
||||
},
|
||||
sortColumn = 'displayName',
|
||||
help = 'Select item to export',
|
||||
accelerators = {
|
||||
delete = 'remove_entry',
|
||||
},
|
||||
},
|
||||
remove = UI.Button {
|
||||
x = -4, y = 4,
|
||||
@@ -255,7 +260,7 @@ The settings will take effect immediately!]],
|
||||
{ name = 'Yes', value = true },
|
||||
{ name = 'No', value = false },
|
||||
},
|
||||
help = 'Ignore damage of item when exporting'
|
||||
help = 'Ignore damage of item'
|
||||
},
|
||||
[2] = UI.Chooser {
|
||||
width = 7,
|
||||
@@ -266,7 +271,7 @@ The settings will take effect immediately!]],
|
||||
{ name = 'Yes', value = true },
|
||||
{ name = 'No', value = false },
|
||||
},
|
||||
help = 'Ignore NBT of item when exporting'
|
||||
help = 'Ignore NBT of item'
|
||||
},
|
||||
[3] = UI.Chooser {
|
||||
width = 13,
|
||||
@@ -276,7 +281,7 @@ The settings will take effect immediately!]],
|
||||
{ name = 'whitelist', value = false },
|
||||
{ name = 'blacklist', value = true },
|
||||
},
|
||||
help = 'Ignore damage of item when exporting'
|
||||
help = 'Ignore damage of item'
|
||||
},
|
||||
scan = UI.Button {
|
||||
x = -11, y = 1,
|
||||
@@ -377,7 +382,7 @@ function nodeWizard.wizard.pages.general:enable()
|
||||
self:focusFirst()
|
||||
end
|
||||
|
||||
function nodeWizard.wizard.pages.general:setNode(node)
|
||||
function nodeWizard.wizard.pages.general:showInventory(node)
|
||||
local inventory
|
||||
|
||||
if device[node.name] and device[node.name].list then
|
||||
@@ -437,7 +442,7 @@ _G._p2 = self.node
|
||||
if page.isValidType then
|
||||
-- TODO: dedupe list
|
||||
local choice = page:isValidType(self.node)
|
||||
if choice then
|
||||
if choice and not Util.find(choices, 'value', choice.value) then
|
||||
table.insert(choices, choice)
|
||||
end
|
||||
end
|
||||
@@ -446,6 +451,8 @@ _G._p2 = self.node
|
||||
self.wizard.pages.general.form[2].choices = choices
|
||||
self.wizard.pages.general.form:setValues(self.node)
|
||||
|
||||
self.wizard.pages.general:showInventory(self.node)
|
||||
|
||||
-- restore indices
|
||||
for _, page in pairs(self.wizard.pages) do
|
||||
if not page.oindex then
|
||||
@@ -479,6 +486,15 @@ function nodeWizard:eventHandler(event)
|
||||
|
||||
UI:setPreviousPage()
|
||||
|
||||
elseif event.type == 'choice_change' then
|
||||
local help
|
||||
if event.choice and event.choice.help then -- TODO: new param sent by UI api
|
||||
help = event.choice.help
|
||||
else
|
||||
help = ''
|
||||
end
|
||||
self.statusBar:setStatus(help)
|
||||
|
||||
elseif event.type == 'edit_filter' then
|
||||
self.filter:show(event.entry, event.callback, event.whitelistOnly)
|
||||
|
||||
|
||||
13
milo/etc/apps/apps.db
Normal file
13
milo/etc/apps/apps.db
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
[ "9302912a2d9794a47241faefc475335b4e07a581" ] = {
|
||||
title = "Remote",
|
||||
category = "Apps",
|
||||
run = "MiloRemote",
|
||||
},
|
||||
[ "eea426f9baef72a8fcefd091e0cec5ab94a76698" ] = {
|
||||
title = "Milo",
|
||||
category = "Apps",
|
||||
run = "Milo",
|
||||
requires = 'advancedTurtle',
|
||||
},
|
||||
}
|
||||
@@ -30,7 +30,11 @@ local activityWizardPage = UI.Window {
|
||||
|
||||
function activityWizardPage:isValidType(node)
|
||||
local m = device[node.name]
|
||||
return m and m.type == 'monitor' and { name = 'Activity Monitor', value = 'activity' }
|
||||
return m and m.type == 'monitor' and {
|
||||
name = 'Activity Monitor',
|
||||
value = 'activity',
|
||||
help = 'Display storage activity'
|
||||
}
|
||||
end
|
||||
|
||||
function activityWizardPage:isValidFor(node)
|
||||
|
||||
@@ -27,7 +27,11 @@ local brewingStandView = UI.Window {
|
||||
|
||||
function brewingStandView:isValidType(node)
|
||||
local m = device[node.name]
|
||||
return m and m.type == 'minecraft:brewing_stand'and { name = 'Brewing Stand', value = 'brewingStand' }
|
||||
return m and m.type == 'minecraft:brewing_stand'and {
|
||||
name = 'Brewing Stand',
|
||||
value = 'brewingStand',
|
||||
help = 'Auto-learning brewing stand',
|
||||
}
|
||||
end
|
||||
|
||||
function brewingStandView:isValidFor(node)
|
||||
|
||||
@@ -15,6 +15,9 @@ local exportView = UI.Window {
|
||||
},
|
||||
sortColumn = 'slot',
|
||||
help = 'Edit this entry',
|
||||
accelerators = {
|
||||
delete = 'remove_entry',
|
||||
},
|
||||
},
|
||||
text = UI.Text {
|
||||
x = 2, y = -2,
|
||||
@@ -38,7 +41,11 @@ local exportView = UI.Window {
|
||||
|
||||
function exportView:isValidType(node)
|
||||
local m = device[node.name]
|
||||
return m and m.pullItems and { name = 'Generic Inventory', value = 'machine' }
|
||||
return m and m.pullItems and {
|
||||
name = 'Generic Inventory',
|
||||
value = 'machine',
|
||||
help = 'Chest, furnace... (has an inventory)'
|
||||
}
|
||||
end
|
||||
|
||||
function exportView:isValidFor(node)
|
||||
|
||||
@@ -15,6 +15,9 @@ local importView = UI.Window {
|
||||
},
|
||||
sortColumn = 'slot',
|
||||
help = 'Edit this entry',
|
||||
accelerators = {
|
||||
delete = 'remove_entry',
|
||||
},
|
||||
},
|
||||
text = UI.Text {
|
||||
x = 2, y = -2,
|
||||
@@ -38,7 +41,11 @@ local importView = UI.Window {
|
||||
|
||||
function importView:isValidType(node)
|
||||
local m = device[node.name]
|
||||
return m and m.pullItems and { name = 'Generic Inventory', value = 'machine' }
|
||||
return m and m.pullItems and {
|
||||
name = 'Generic Inventory',
|
||||
value = 'machine',
|
||||
help = 'Chest, furnace... (has an inventory)',
|
||||
}
|
||||
end
|
||||
|
||||
function importView:isValidFor(node)
|
||||
|
||||
@@ -23,7 +23,11 @@ local inputChestWizardPage = UI.Window {
|
||||
|
||||
function inputChestWizardPage:isValidType(node)
|
||||
local m = device[node.name]
|
||||
return m and m.pullItems and { name = 'Input Chest', value = 'input' }
|
||||
return m and m.pullItems and {
|
||||
name = 'Input Chest',
|
||||
value = 'input',
|
||||
help = 'Sends all items to storage',
|
||||
}
|
||||
end
|
||||
|
||||
function inputChestWizardPage:isValidFor(node)
|
||||
|
||||
@@ -29,7 +29,11 @@ local jobsWizardPage = UI.Window {
|
||||
|
||||
function jobsWizardPage:isValidType(node)
|
||||
local m = device[node.name]
|
||||
return m and m.type == 'monitor' and { name = 'Crafting Monitor', value = 'jobs' }
|
||||
return m and m.type == 'monitor' and {
|
||||
name = 'Crafting Monitor',
|
||||
value = 'jobs',
|
||||
help = 'Display crafting progress / jobs'
|
||||
}
|
||||
end
|
||||
|
||||
function jobsWizardPage:isValidFor(node)
|
||||
|
||||
@@ -36,8 +36,8 @@ local listingPage = UI.Page {
|
||||
--{ text = 'Forget', event = 'forget' },
|
||||
{ text = 'Craft', event = 'craft' },
|
||||
{ text = 'Edit', event = 'details' },
|
||||
{ text = 'Network', event = 'network' },
|
||||
{ text = 'Refresh', event = 'refresh', x = -9 },
|
||||
{ text = 'Refresh', event = 'refresh', x = -12 },
|
||||
{ text = '\206', event = 'network', x = -3 },
|
||||
},
|
||||
},
|
||||
grid = UI.Grid {
|
||||
@@ -59,7 +59,7 @@ local listingPage = UI.Page {
|
||||
backgroundColor = colors.cyan,
|
||||
backgroundFocusColor = colors.cyan,
|
||||
accelerators = {
|
||||
[ 'enter' ] = 'craft',
|
||||
[ 'enter' ] = 'eject',
|
||||
},
|
||||
},
|
||||
storageStatus = UI.Text {
|
||||
@@ -198,10 +198,12 @@ function listingPage:eventHandler(event)
|
||||
|
||||
elseif event.type == 'craft' then
|
||||
local item = self.grid:getSelected()
|
||||
if Craft.findRecipe(item) or true then -- or item.is_craftable then
|
||||
UI:setPage('craft', self.grid:getSelected())
|
||||
else
|
||||
self.notification:error('No recipe defined')
|
||||
if item then
|
||||
if Craft.findRecipe(item) then -- or item.is_craftable then
|
||||
UI:setPage('craft', self.grid:getSelected())
|
||||
else
|
||||
self.notification:error('No recipe defined')
|
||||
end
|
||||
end
|
||||
|
||||
elseif event.type == 'text_change' and event.element == self.statusBar.filter then
|
||||
|
||||
@@ -35,7 +35,11 @@ function wizardPage:isValidType(node)
|
||||
return m and
|
||||
m.type == 'manipulator' and
|
||||
m.getEnder and
|
||||
{ name = 'Manipulator', value = 'manipulator' }
|
||||
{
|
||||
name = 'Manipulator',
|
||||
value = 'manipulator',
|
||||
help = 'Manipulator w/bound introspection mod'
|
||||
}
|
||||
end
|
||||
|
||||
function wizardPage:isValidFor(node)
|
||||
|
||||
@@ -12,7 +12,7 @@ local function filter(a)
|
||||
end
|
||||
|
||||
function PotionImportTask:cycle(context)
|
||||
for bs in context.storage:filterActive('machine', filter) do
|
||||
for bs in context.storage:filterActive('brewingStand', filter) do
|
||||
if bs.adapter.getBrewTime() == 0 then
|
||||
local list = bs.adapter.list()
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ local storageView = UI.Window {
|
||||
x = 16, ex = -2, y = 3,
|
||||
value = '',
|
||||
},
|
||||
--[[
|
||||
[4] = UI.Checkbox {
|
||||
formLabel = 'Void', formKey = 'voidExcess',
|
||||
help = 'Void excess if locked - TODO',
|
||||
@@ -36,6 +37,7 @@ local storageView = UI.Window {
|
||||
help = 'TODO',
|
||||
pruneEmpty = true,
|
||||
},
|
||||
]]--
|
||||
},
|
||||
}
|
||||
|
||||
@@ -50,7 +52,11 @@ end
|
||||
|
||||
function storageView:isValidType(node)
|
||||
local m = device[node.name]
|
||||
return m and m.pullItems and { name = 'Storage', value = 'storage' }
|
||||
return m and m.pullItems and {
|
||||
name = 'Storage',
|
||||
value = 'storage',
|
||||
help = 'Use for item storage',
|
||||
}
|
||||
end
|
||||
|
||||
function storageView:isValidFor(node)
|
||||
|
||||
@@ -25,7 +25,11 @@ local trashcanWizardPage = UI.Window {
|
||||
|
||||
function trashcanWizardPage:isValidType(node)
|
||||
local m = device[node.name]
|
||||
return m and m.pullItems and { name = 'Trashcan', value = 'trashcan' }
|
||||
return m and m.pullItems and {
|
||||
name = 'Trashcan',
|
||||
value = 'trashcan',
|
||||
help = 'An inventory to send unwanted items',
|
||||
}
|
||||
end
|
||||
|
||||
function trashcanWizardPage:isValidFor(node)
|
||||
|
||||
Reference in New Issue
Block a user