Try to update Milo for 1.19
Removes Milo trying to access damage on items (`nil` because of The Flattening). We might also need to reimplement showing durability in the item's display name - I got a little too carried away with removing mentions of "damage". Also renames nbtHash to nbt to be consistent with new CC:T naming. I tried not to touch anything related to MiloRemote for now, and there are probably still many bugs remaining that need to be ironed out. Most of the basic functionality works now, though.
This commit is contained in:
@@ -2,6 +2,7 @@ local Event = require('opus.event')
|
||||
local Milo = require('milo')
|
||||
local Sound = require('opus.sound')
|
||||
local Storage = require('milo.storage')
|
||||
local TurtleInv = require('milo.turtleInv')
|
||||
local UI = require('opus.ui')
|
||||
local Util = require('opus.util')
|
||||
|
||||
@@ -18,7 +19,6 @@ multishell.setTitle(multishell.getCurrent(), 'Milo')
|
||||
local function Syntax(msg)
|
||||
print([[
|
||||
Turtle must be provided with:
|
||||
* Introspection module (never bound)
|
||||
* Workbench
|
||||
|
||||
Turtle must be connected to:
|
||||
@@ -46,10 +46,6 @@ if not modem.getNameLocal() then
|
||||
Syntax('Wired modem is not active')
|
||||
end
|
||||
|
||||
local introspection = device['plethora:introspection'] or
|
||||
turtle.equip('left', 'plethora:module:0') and device['plethora:introspection'] or
|
||||
Syntax('Introspection module missing')
|
||||
|
||||
if not device.workbench then
|
||||
turtle.equip('right', 'minecraft:crafting_table:0')
|
||||
if not device.workbench then
|
||||
@@ -58,6 +54,7 @@ if not device.workbench then
|
||||
end
|
||||
|
||||
local localName = modem.getNameLocal()
|
||||
TurtleInv.setLocalName(localName)
|
||||
|
||||
local context = {
|
||||
resources = Util.readTable(Milo.RESOURCE_FILE) or { },
|
||||
@@ -76,7 +73,7 @@ local context = {
|
||||
turtleInventory = {
|
||||
name = localName,
|
||||
mtype = 'hidden',
|
||||
adapter = introspection.getInventory(),
|
||||
adapter = TurtleInv,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -283,7 +283,7 @@ local function splitKey(key)
|
||||
local t = Util.split(key, '(.-):')
|
||||
local item = { }
|
||||
if #t[#t] > 8 then
|
||||
item.nbtHash = table.remove(t)
|
||||
item.nbt = table.remove(t)
|
||||
end
|
||||
item.damage = tonumber(table.remove(t))
|
||||
item.name = table.concat(t, ':')
|
||||
|
||||
@@ -20,10 +20,11 @@ local Craft = {
|
||||
local function splitKey(key)
|
||||
local t = Util.split(key, '(.-):')
|
||||
local item = { }
|
||||
if #t[#t] > 8 then
|
||||
item.nbtHash = table.remove(t)
|
||||
|
||||
if t[3] then
|
||||
item.nbt = t[3]
|
||||
end
|
||||
item.damage = tonumber(table.remove(t))
|
||||
|
||||
item.name = table.concat(t, ':')
|
||||
return item
|
||||
end
|
||||
@@ -32,7 +33,7 @@ local function makeRecipeKey(item)
|
||||
if type(item) == 'string' then
|
||||
item = splitKey(item)
|
||||
end
|
||||
return table.concat({ item.name, item.damage or 0, item.nbtHash }, ':')
|
||||
return table.concat({ item.name, item.nbt }, ':')
|
||||
end
|
||||
|
||||
local function convert(ingredient)
|
||||
@@ -47,8 +48,7 @@ local function getCraftingTool(storage, item)
|
||||
|
||||
for _,v in pairs(items) do
|
||||
if item.name == v.name and
|
||||
(not item.damage or item.damage == v.damage) and
|
||||
(not item.nbtHash or item.nbtHash == v.nbtHash) then
|
||||
(not item.nbt or item.nbt == v.nbt) then
|
||||
return v
|
||||
end
|
||||
end
|
||||
@@ -93,11 +93,7 @@ function Craft.getItemCount(items, item)
|
||||
local count = 0
|
||||
for _,v in pairs(items) do
|
||||
if v.name == item.name and
|
||||
(not item.damage or v.damage == item.damage) and
|
||||
v.nbtHash == item.nbtHash then
|
||||
if item.damage then
|
||||
return v.count
|
||||
end
|
||||
v.nbt == item.nbt then
|
||||
count = count + v.count
|
||||
end
|
||||
end
|
||||
@@ -387,18 +383,7 @@ function Craft.findRecipe(key)
|
||||
end
|
||||
|
||||
local item = itemDB:splitKey(key)
|
||||
if item.damage then
|
||||
return Craft.recipes[makeRecipeKey(item)]
|
||||
end
|
||||
|
||||
-- handle cases where the request is like : IC2:reactorVent:*
|
||||
for rkey,recipe in pairs(Craft.recipes) do
|
||||
local r = itemDB:splitKey(rkey)
|
||||
if item.name == r.name and
|
||||
(not item.nbtHash or r.nbtHash == item.nbtHash) then
|
||||
return recipe
|
||||
end
|
||||
end
|
||||
return Craft.recipes[makeRecipeKey(item)]
|
||||
end
|
||||
|
||||
-- determine the full list of ingredients needed to craft
|
||||
|
||||
@@ -93,7 +93,7 @@ function Milo:getMatches(item, flags)
|
||||
local count = 0
|
||||
local items = self:listItems()
|
||||
|
||||
if not flags.ignoreDamage and not flags.ignoreNbtHash then
|
||||
if not flags.ignoreNbt then
|
||||
local key = item.key or itemDB:makeKey(item)
|
||||
local v = items[key]
|
||||
if v then
|
||||
@@ -104,8 +104,7 @@ function Milo:getMatches(item, flags)
|
||||
else
|
||||
for key,v in pairs(items) do
|
||||
if item.name == v.name and
|
||||
(flags.ignoreDamage or item.damage == v.damage) and
|
||||
(flags.ignoreNbtHash or item.nbtHash == v.nbtHash) then
|
||||
(flags.ignoreNbt or item.nbt == v.nbt) then
|
||||
|
||||
t[key] = Util.shallowCopy(v)
|
||||
count = count + v.count
|
||||
@@ -125,7 +124,7 @@ function Milo:getTurtleInventory()
|
||||
|
||||
for i, v in pairs(self.context.turtleInventory.adapter.list()) do
|
||||
list[i] = itemDB:get(v, function()
|
||||
return self.context.turtleInventory.adapter.getItemMeta(i)
|
||||
return self.context.turtleInventory.adapter.getItemDetail(i)
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -277,22 +276,15 @@ function Milo:learnRecipe()
|
||||
for _,v1 in pairs(results) do
|
||||
for _,v2 in pairs(ingredients) do
|
||||
if v1.name == v2.name and
|
||||
v1.nbtHash == v2.nbtHash and
|
||||
(v1.damage == v2.damage or
|
||||
(v1.maxDamage > 0 and v2.maxDamage > 0 and
|
||||
v1.damage ~= v2.damage)) then
|
||||
v1.nbt == v2.nbt then
|
||||
if not newRecipe.crafingTools then
|
||||
newRecipe.craftingTools = { }
|
||||
end
|
||||
local tool = Util.shallowCopy(v2)
|
||||
if tool.maxDamage > 0 then
|
||||
tool.damage = '*'
|
||||
v2.damage = '*'
|
||||
end
|
||||
|
||||
--[[
|
||||
Turtles can only craft one item at a time using a tool :(
|
||||
]]--
|
||||
]] -- Todo: check if this still applies
|
||||
maxCount = 1
|
||||
|
||||
newRecipe.craftingTools[itemDB:makeKey(tool)] = true
|
||||
|
||||
@@ -21,13 +21,13 @@ function Adapter:listItems(throttle)
|
||||
|
||||
for k,v in pairs(self.list()) do
|
||||
if v.count > 0 then
|
||||
local key = table.concat({ v.name, v.damage, v.nbtHash }, ':')
|
||||
local key = table.concat({ v.name, v.nbt }, ':')
|
||||
|
||||
local entry = cache[key]
|
||||
if entry then
|
||||
entry.count = entry.count + v.count
|
||||
else
|
||||
cache[key] = itemDB:get(v, function() return self.getItemMeta(k) end)
|
||||
cache[key] = itemDB:get(v, function() return self.getItemDetail(k) end)
|
||||
end
|
||||
throttle()
|
||||
end
|
||||
|
||||
@@ -321,7 +321,7 @@ function Storage:listItemsRaw(throttle)
|
||||
local items = chest.list()
|
||||
|
||||
for slot, item in pairs(items) do
|
||||
items[slot] = itemDB:get(item, function() return chest.getItemMeta(slot) end)
|
||||
items[slot] = itemDB:get(item, function() return chest.getItemDetail(slot) end)
|
||||
end
|
||||
|
||||
res[v.name] = items
|
||||
@@ -340,7 +340,7 @@ function Storage:listProviders(throttle)
|
||||
|
||||
for chest, items in pairs(rawItems) do
|
||||
for slot, item in pairs(items) do
|
||||
local key = table.concat({item.name, item.damage, item.nbtHash}, ":")
|
||||
local key = table.concat({item.name, item.nbt}, ":")
|
||||
if not res[key] then
|
||||
res[key] = {}
|
||||
end
|
||||
@@ -443,7 +443,7 @@ function Storage:updateCache(adapter, item, count)
|
||||
return
|
||||
end
|
||||
|
||||
local key = item.key or table.concat({ item.name, item.damage, item.nbtHash }, ':')
|
||||
local key = item.key or table.concat({ item.name, item.nbt }, ':')
|
||||
local entry = adapter.cache[key]
|
||||
|
||||
if not entry then
|
||||
@@ -490,14 +490,14 @@ end
|
||||
|
||||
local function isValidTransfer(adapter, target)
|
||||
-- lazily cache transfer locations
|
||||
if not adapter.transferLocations then
|
||||
--[[if not adapter.transferLocations then
|
||||
adapter.transferLocations = adapter.getTransferLocations()
|
||||
end
|
||||
for _,v in pairs(adapter.transferLocations) do
|
||||
if v == target then
|
||||
for _,v in pairs(adapter.transferLocations) do]]
|
||||
-- if v == target then
|
||||
return true
|
||||
end
|
||||
end
|
||||
-- end
|
||||
--end
|
||||
end
|
||||
|
||||
local function rawExport(source, target, item, qty, slot)
|
||||
@@ -529,8 +529,7 @@ local function rawExport(source, target, item, qty, slot)
|
||||
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
|
||||
stack.nbt == item.nbt then
|
||||
local amount = math.min(qty, stack.count)
|
||||
if amount > 0 then
|
||||
amount = transfer(key, amount, slot)
|
||||
@@ -560,7 +559,7 @@ end
|
||||
function Storage:export(target, slot, count, item)
|
||||
local timer = Util.timer()
|
||||
local total = 0
|
||||
local key = item.key or table.concat({ item.name, item.damage, item.nbtHash }, ':')
|
||||
local key = item.key or table.concat({ item.name, item.nbt }, ':')
|
||||
|
||||
local function provide(adapter, pcount)
|
||||
-- update cache before export to allow for simultaneous calls
|
||||
@@ -651,7 +650,7 @@ function Storage:import(source, slot, count, item)
|
||||
|
||||
local timer = Util.timer()
|
||||
local total = 0
|
||||
local key = item.key or table.concat({ item.name, item.damage, item.nbtHash }, ':')
|
||||
local key = item.key or table.concat({ item.name, item.nbt }, ':')
|
||||
|
||||
if not self.cache then
|
||||
self:listItems()
|
||||
@@ -664,7 +663,7 @@ 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(source.adapter.getItemMeta(slot))
|
||||
entry = itemDB:add(source.adapter.getItemDetail(slot))
|
||||
end
|
||||
itemDB:flush()
|
||||
end
|
||||
|
||||
41
milo/apis/turtleInv.lua
Normal file
41
milo/apis/turtleInv.lua
Normal file
@@ -0,0 +1,41 @@
|
||||
--[[ Emulate a CC:T inventory on the turtle
|
||||
]]
|
||||
|
||||
local TurtleInv = {}
|
||||
|
||||
local turtle = _G.turtle
|
||||
|
||||
local inventorySize = 16
|
||||
|
||||
local localName = "milo_local_name_unset"
|
||||
|
||||
function TurtleInv.setLocalName(name)
|
||||
localName = name
|
||||
end
|
||||
|
||||
function TurtleInv.size()
|
||||
return inventorySize
|
||||
end
|
||||
|
||||
function TurtleInv.list()
|
||||
local list = {}
|
||||
for slot = 1, inventorySize do
|
||||
list[slot] = turtle.getItemDetail(slot)
|
||||
end
|
||||
return list
|
||||
end
|
||||
|
||||
function TurtleInv.getItemDetail(slot)
|
||||
return turtle.getItemDetail(slot, true)
|
||||
end
|
||||
|
||||
function TurtleInv.pullItems(fromName, fromSlot, limit, toSlot)
|
||||
return peripheral.call(fromName, "pushItems", localName, fromSlot, limit, toSlot)
|
||||
end
|
||||
|
||||
function TurtleInv.pushItems(toName, fromSlot, limit, toSlot)
|
||||
return peripheral.call(toName, "pullItems", localName, fromSlot, limit, toSlot)
|
||||
end
|
||||
|
||||
|
||||
return TurtleInv
|
||||
@@ -312,14 +312,10 @@ The settings will take effect immediately!]],
|
||||
margin = 1,
|
||||
manualControls = true,
|
||||
[1] = UI.Checkbox {
|
||||
formLabel = 'Ignore Dmg', formKey = 'ignoreDamage',
|
||||
help = 'Ignore damage of item',
|
||||
},
|
||||
[2] = UI.Checkbox {
|
||||
formLabel = 'Ignore NBT', formKey = 'ignoreNbtHash',
|
||||
formLabel = 'Ignore NBT', formKey = 'ignoreNbt',
|
||||
help = 'Ignore NBT of item',
|
||||
},
|
||||
[3] = UI.Chooser {
|
||||
[2] = UI.Chooser {
|
||||
width = 13,
|
||||
formLabel = 'Mode', formKey = 'blacklist',
|
||||
nochoice = 'whitelist',
|
||||
@@ -327,7 +323,7 @@ The settings will take effect immediately!]],
|
||||
{ name = 'whitelist', value = false },
|
||||
{ name = 'blacklist', value = true },
|
||||
},
|
||||
help = 'Ignore damage of item'
|
||||
help = 'Accept item by default or deny by default'
|
||||
},
|
||||
scan = UI.Button {
|
||||
x = -11, y = 1,
|
||||
@@ -349,7 +345,7 @@ The settings will take effect immediately!]],
|
||||
self.form:setValues(entry)
|
||||
self:resetGrid()
|
||||
|
||||
self.form[3].inactive = whitelistOnly
|
||||
self.form[2].inactive = whitelistOnly
|
||||
|
||||
UI.SlideOut.show(self)
|
||||
self:setFocus(self.form.scan)
|
||||
|
||||
@@ -30,7 +30,7 @@ function ExportTask:cycle(context)
|
||||
end
|
||||
|
||||
local function exportSingleSlot()
|
||||
local slot = node.adapter.getItemMeta(entry.slot)
|
||||
local slot = node.adapter.getItemDetail(entry.slot)
|
||||
|
||||
if slot and slot.count == slot.maxCount then
|
||||
return
|
||||
@@ -41,8 +41,7 @@ function ExportTask:cycle(context)
|
||||
for key in pairs(entry.filter) do
|
||||
local filterItem = itemDB:splitKey(key)
|
||||
if (slot.name == filterItem.name and
|
||||
(entry.ignoreDamage or slot.damage == filterItem.damage) and
|
||||
(entry.ignoreNbtHash or slot.nbtHash == filterItem.nbtHash)) then
|
||||
(entry.ignoreNbt or slot.nbt == filterItem.nbt)) then
|
||||
|
||||
local items = Milo:getMatches(filterItem, entry)
|
||||
local _, item = next(items)
|
||||
@@ -81,8 +80,7 @@ function ExportTask:cycle(context)
|
||||
for i = 1, node.adapter.__size do
|
||||
local slot = slots[i]
|
||||
if (not slot or slot.name == item.name and
|
||||
(entry.ignoreDamage or slot.damage == item.damage) and
|
||||
(entry.ignoreNbtHash or slot.nbtHash == item.nbtHash) and
|
||||
(entry.ignoreNbt or slot.nbt == item.nbt) and
|
||||
slot.count < item.maxCount) then
|
||||
|
||||
return true
|
||||
|
||||
@@ -21,7 +21,7 @@ function ImportTask:cycle(context)
|
||||
for _, entry in pairs(node.imports) do
|
||||
|
||||
local function itemMatchesFilter(item)
|
||||
if not entry.ignoreDamage and not entry.ignoreNbtHash then
|
||||
if not entry.ignoreNbt then
|
||||
local key = itemDB:makeKey(item)
|
||||
return entry.filter[key]
|
||||
end
|
||||
@@ -29,8 +29,7 @@ function ImportTask:cycle(context)
|
||||
for key in pairs(entry.filter) do
|
||||
local v = itemDB:splitKey(key)
|
||||
if item.name == v.name and
|
||||
(entry.ignoreDamage or item.damage == v.damage) and
|
||||
(entry.ignoreNbtHash or item.nbtHash == v.nbtHash) then
|
||||
(entry.ignoreNbt or item.nbt == v.nbt) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
@@ -52,7 +51,7 @@ function ImportTask:cycle(context)
|
||||
|
||||
local function importSlot(slotNo)
|
||||
local item = itemDB:get(list[slotNo], function()
|
||||
return node.adapter.getItemMeta(slotNo)
|
||||
return node.adapter.getItemDetail(slotNo)
|
||||
end)
|
||||
if item and matchesFilter(item) then
|
||||
if context.storage:import(node, slotNo, item.count, item) ~= item.count then
|
||||
|
||||
@@ -20,16 +20,18 @@ function infoTab:draw()
|
||||
Ansi.orange, item.displayName, Ansi.reset,
|
||||
item.name)
|
||||
|
||||
if item.nbtHash then
|
||||
value = value .. item.nbtHash .. '\n'
|
||||
if item.nbt then
|
||||
value = value .. item.nbt .. '\n'
|
||||
end
|
||||
|
||||
value = value .. string.format('\n%sCount:%s %s',
|
||||
Ansi.yellow, Ansi.reset, item.count)
|
||||
|
||||
value = value .. string.format('\n%sDamage:%s %s',
|
||||
Ansi.yellow, Ansi.reset, item.damage)
|
||||
|
||||
if item.durability then
|
||||
value = value .. string.format('\n%Durability:%s %s',
|
||||
Ansi.yellow, Ansi.reset, item.durability)
|
||||
end
|
||||
|
||||
if item.maxDamage and item.maxDamage > 0 then
|
||||
value = value .. string.format(' (max: %s)', item.maxDamage)
|
||||
end
|
||||
|
||||
@@ -29,11 +29,7 @@ local manageTab = UI.Tab {
|
||||
transform = 'number',
|
||||
},
|
||||
[4] = UI.Checkbox {
|
||||
formLabel = 'Ignore Dmg', formKey = 'ignoreDamage',
|
||||
help = 'Ignore damage of item',
|
||||
},
|
||||
[5] = UI.Checkbox {
|
||||
formLabel = 'Ignore NBT', formKey = 'ignoreNbtHash',
|
||||
formLabel = 'Ignore NBT', formKey = 'ignoreNbt',
|
||||
help = 'Ignore NBT of item',
|
||||
},
|
||||
},
|
||||
@@ -44,8 +40,6 @@ function manageTab:setItem(item)
|
||||
self.res = Util.shallowCopy(context.resources[item.key] or { })
|
||||
self.res.displayName = self.item.displayName
|
||||
self.form:setValues(self.res)
|
||||
|
||||
-- TODO: ignore damage should not be active if there is not a maxDamage value
|
||||
end
|
||||
|
||||
function manageTab:eventHandler(event)
|
||||
@@ -76,8 +70,7 @@ function manageTab:eventHandler(event)
|
||||
|
||||
local newKey = {
|
||||
name = self.item.name,
|
||||
damage = self.res.ignoreDamage and 0 or self.item.damage,
|
||||
nbtHash = not self.res.ignoreNbtHash and self.item.nbtHash or nil,
|
||||
nbt = not self.res.ignoreNbt and self.item.nbt or nil,
|
||||
}
|
||||
|
||||
context.resources[self.item.key] = nil
|
||||
|
||||
@@ -42,7 +42,7 @@ function recipeTab:setItem(item)
|
||||
})
|
||||
end
|
||||
local key = itemDB:splitKey(self.recipe.result)
|
||||
self.ignoreResultNBT.inactive = not key.nbtHash
|
||||
self.ignoreResultNBT.inactive = not key.nbt
|
||||
end
|
||||
self.grid:setValues(t)
|
||||
end
|
||||
@@ -53,7 +53,7 @@ function recipeTab:eventHandler(event)
|
||||
Milo:updateRecipe(self.recipe.result)
|
||||
|
||||
local item = itemDB:splitKey(self.recipe.result)
|
||||
item.nbtHash = nil
|
||||
item.nbt = nil
|
||||
self.recipe.result = itemDB:makeKey(item)
|
||||
|
||||
-- add updated entry
|
||||
@@ -64,13 +64,13 @@ function recipeTab:eventHandler(event)
|
||||
|
||||
elseif event.type == 'grid_focus_row' then
|
||||
local key = itemDB:splitKey(event.selected.key)
|
||||
self.ignoreNBT.inactive = not key.nbtHash
|
||||
self.ignoreNBT.inactive = not key.nbt
|
||||
self.ignoreNBT:draw()
|
||||
|
||||
elseif event.type == 'ignore_nbt' then
|
||||
local selected = self.grid:getSelected()
|
||||
local item = itemDB:splitKey(selected.key)
|
||||
item.nbtHash = nil
|
||||
item.nbt = nil
|
||||
selected.key = itemDB:makeKey(item)
|
||||
self.grid:draw()
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ local function client(socket)
|
||||
local function deposit()
|
||||
local node = makeNode(data.source or 'inventory')
|
||||
if node then
|
||||
local slot = node.adapter.getItemMeta(data.slot)
|
||||
local slot = node.adapter.getItemDetail(data.slot)
|
||||
if slot then
|
||||
if context.storage:import(node, data.slot, slot.count, slot) > 0 then
|
||||
local item = Milo:getItem(slot)
|
||||
|
||||
@@ -15,14 +15,13 @@ function ReplenishTask:cycle(context)
|
||||
local _, count = Milo:getMatches(item, res)
|
||||
|
||||
if count < res.low then
|
||||
local nbtHash
|
||||
if not res.ignoreNbtHash then
|
||||
nbtHash = item.nbtHash
|
||||
local nbt
|
||||
if not res.ignoreNbt then
|
||||
nbt = item.nbt
|
||||
end
|
||||
Milo:requestCrafting({
|
||||
name = item.name,
|
||||
damage = res.ignoreDamage and 0 or item.damage,
|
||||
nbtHash = nbtHash,
|
||||
nbt = nbt,
|
||||
requested = res.low - count,
|
||||
count = count,
|
||||
replenish = true,
|
||||
|
||||
@@ -12,7 +12,7 @@ local jua = require("swshop.jua")
|
||||
|
||||
local await = jua.await
|
||||
local device = _G.device
|
||||
local json = _G.json
|
||||
local json = { encode=textutils.serializeJSON, decode = textutils.unserialiseJSON }
|
||||
local rs = _G.rs
|
||||
local textutils = _G.textutils
|
||||
|
||||
|
||||
Reference in New Issue
Block a user