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:
Kan18
2022-12-24 15:33:01 +04:00
parent 2461d060e0
commit 2f67fb2ef8
16 changed files with 101 additions and 100 deletions

View File

@@ -2,6 +2,7 @@ local Event = require('opus.event')
local Milo = require('milo') local Milo = require('milo')
local Sound = require('opus.sound') local Sound = require('opus.sound')
local Storage = require('milo.storage') local Storage = require('milo.storage')
local TurtleInv = require('milo.turtleInv')
local UI = require('opus.ui') local UI = require('opus.ui')
local Util = require('opus.util') local Util = require('opus.util')
@@ -18,7 +19,6 @@ multishell.setTitle(multishell.getCurrent(), 'Milo')
local function Syntax(msg) local function Syntax(msg)
print([[ print([[
Turtle must be provided with: Turtle must be provided with:
* Introspection module (never bound)
* Workbench * Workbench
Turtle must be connected to: Turtle must be connected to:
@@ -46,10 +46,6 @@ if not modem.getNameLocal() then
Syntax('Wired modem is not active') Syntax('Wired modem is not active')
end 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 if not device.workbench then
turtle.equip('right', 'minecraft:crafting_table:0') turtle.equip('right', 'minecraft:crafting_table:0')
if not device.workbench then if not device.workbench then
@@ -58,6 +54,7 @@ if not device.workbench then
end end
local localName = modem.getNameLocal() local localName = modem.getNameLocal()
TurtleInv.setLocalName(localName)
local context = { local context = {
resources = Util.readTable(Milo.RESOURCE_FILE) or { }, resources = Util.readTable(Milo.RESOURCE_FILE) or { },
@@ -76,7 +73,7 @@ local context = {
turtleInventory = { turtleInventory = {
name = localName, name = localName,
mtype = 'hidden', mtype = 'hidden',
adapter = introspection.getInventory(), adapter = TurtleInv,
} }
} }

View File

@@ -283,7 +283,7 @@ local function splitKey(key)
local t = Util.split(key, '(.-):') local t = Util.split(key, '(.-):')
local item = { } local item = { }
if #t[#t] > 8 then if #t[#t] > 8 then
item.nbtHash = table.remove(t) item.nbt = table.remove(t)
end end
item.damage = tonumber(table.remove(t)) item.damage = tonumber(table.remove(t))
item.name = table.concat(t, ':') item.name = table.concat(t, ':')

View File

@@ -20,10 +20,11 @@ local Craft = {
local function splitKey(key) local function splitKey(key)
local t = Util.split(key, '(.-):') local t = Util.split(key, '(.-):')
local item = { } local item = { }
if #t[#t] > 8 then
item.nbtHash = table.remove(t) if t[3] then
item.nbt = t[3]
end end
item.damage = tonumber(table.remove(t))
item.name = table.concat(t, ':') item.name = table.concat(t, ':')
return item return item
end end
@@ -32,7 +33,7 @@ local function makeRecipeKey(item)
if type(item) == 'string' then if type(item) == 'string' then
item = splitKey(item) item = splitKey(item)
end end
return table.concat({ item.name, item.damage or 0, item.nbtHash }, ':') return table.concat({ item.name, item.nbt }, ':')
end end
local function convert(ingredient) local function convert(ingredient)
@@ -47,8 +48,7 @@ local function getCraftingTool(storage, item)
for _,v in pairs(items) do for _,v in pairs(items) do
if item.name == v.name and if item.name == v.name and
(not item.damage or item.damage == v.damage) and (not item.nbt or item.nbt == v.nbt) then
(not item.nbtHash or item.nbtHash == v.nbtHash) then
return v return v
end end
end end
@@ -93,11 +93,7 @@ function Craft.getItemCount(items, item)
local count = 0 local count = 0
for _,v in pairs(items) do for _,v in pairs(items) do
if v.name == item.name and if v.name == item.name and
(not item.damage or v.damage == item.damage) and v.nbt == item.nbt then
v.nbtHash == item.nbtHash then
if item.damage then
return v.count
end
count = count + v.count count = count + v.count
end end
end end
@@ -387,18 +383,7 @@ function Craft.findRecipe(key)
end end
local item = itemDB:splitKey(key) local item = itemDB:splitKey(key)
if item.damage then return Craft.recipes[makeRecipeKey(item)]
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
end end
-- determine the full list of ingredients needed to craft -- determine the full list of ingredients needed to craft

View File

@@ -93,7 +93,7 @@ function Milo:getMatches(item, flags)
local count = 0 local count = 0
local items = self:listItems() 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 key = item.key or itemDB:makeKey(item)
local v = items[key] local v = items[key]
if v then if v then
@@ -104,8 +104,7 @@ function Milo:getMatches(item, flags)
else else
for key,v in pairs(items) do for key,v in pairs(items) do
if item.name == v.name and if item.name == v.name and
(flags.ignoreDamage or item.damage == v.damage) and (flags.ignoreNbt or item.nbt == v.nbt) then
(flags.ignoreNbtHash or item.nbtHash == v.nbtHash) then
t[key] = Util.shallowCopy(v) t[key] = Util.shallowCopy(v)
count = count + v.count count = count + v.count
@@ -125,7 +124,7 @@ function Milo:getTurtleInventory()
for i, v in pairs(self.context.turtleInventory.adapter.list()) do for i, v in pairs(self.context.turtleInventory.adapter.list()) do
list[i] = itemDB:get(v, function() list[i] = itemDB:get(v, function()
return self.context.turtleInventory.adapter.getItemMeta(i) return self.context.turtleInventory.adapter.getItemDetail(i)
end) end)
end end
@@ -277,22 +276,15 @@ function Milo:learnRecipe()
for _,v1 in pairs(results) do for _,v1 in pairs(results) do
for _,v2 in pairs(ingredients) do for _,v2 in pairs(ingredients) do
if v1.name == v2.name and if v1.name == v2.name and
v1.nbtHash == v2.nbtHash and v1.nbt == v2.nbt then
(v1.damage == v2.damage or
(v1.maxDamage > 0 and v2.maxDamage > 0 and
v1.damage ~= v2.damage)) then
if not newRecipe.crafingTools then if not newRecipe.crafingTools then
newRecipe.craftingTools = { } newRecipe.craftingTools = { }
end end
local tool = Util.shallowCopy(v2) 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 :( Turtles can only craft one item at a time using a tool :(
]]-- ]] -- Todo: check if this still applies
maxCount = 1 maxCount = 1
newRecipe.craftingTools[itemDB:makeKey(tool)] = true newRecipe.craftingTools[itemDB:makeKey(tool)] = true

View File

@@ -21,13 +21,13 @@ function Adapter:listItems(throttle)
for k,v in pairs(self.list()) do for k,v in pairs(self.list()) do
if v.count > 0 then 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] local entry = cache[key]
if entry then if entry then
entry.count = entry.count + v.count entry.count = entry.count + v.count
else else
cache[key] = itemDB:get(v, function() return self.getItemMeta(k) end) cache[key] = itemDB:get(v, function() return self.getItemDetail(k) end)
end end
throttle() throttle()
end end

View File

@@ -321,7 +321,7 @@ function Storage:listItemsRaw(throttle)
local items = chest.list() local items = chest.list()
for slot, item in pairs(items) do 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 end
res[v.name] = items res[v.name] = items
@@ -340,7 +340,7 @@ function Storage:listProviders(throttle)
for chest, items in pairs(rawItems) do for chest, items in pairs(rawItems) do
for slot, item in pairs(items) 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 if not res[key] then
res[key] = {} res[key] = {}
end end
@@ -443,7 +443,7 @@ function Storage:updateCache(adapter, item, count)
return return
end 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] local entry = adapter.cache[key]
if not entry then if not entry then
@@ -490,14 +490,14 @@ end
local function isValidTransfer(adapter, target) local function isValidTransfer(adapter, target)
-- lazily cache transfer locations -- lazily cache transfer locations
if not adapter.transferLocations then --[[if not adapter.transferLocations then
adapter.transferLocations = adapter.getTransferLocations() adapter.transferLocations = adapter.getTransferLocations()
end end
for _,v in pairs(adapter.transferLocations) do for _,v in pairs(adapter.transferLocations) do]]
if v == target then -- if v == target then
return true return true
end -- end
end --end
end end
local function rawExport(source, target, item, qty, slot) local function rawExport(source, target, item, qty, slot)
@@ -529,8 +529,7 @@ local function rawExport(source, target, item, qty, slot)
local stacks = source.list() local stacks = source.list()
for key,stack in Util.rpairs(stacks) do for key,stack in Util.rpairs(stacks) do
if stack.name == item.name and if stack.name == item.name and
stack.damage == item.damage and stack.nbt == item.nbt then
stack.nbtHash == item.nbtHash then
local amount = math.min(qty, stack.count) local amount = math.min(qty, stack.count)
if amount > 0 then if amount > 0 then
amount = transfer(key, amount, slot) amount = transfer(key, amount, slot)
@@ -560,7 +559,7 @@ end
function Storage:export(target, slot, count, item) function Storage:export(target, slot, count, item)
local timer = Util.timer() local timer = Util.timer()
local total = 0 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) local function provide(adapter, pcount)
-- update cache before export to allow for simultaneous calls -- 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 timer = Util.timer()
local total = 0 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 if not self.cache then
self:listItems() self:listItems()
@@ -664,7 +663,7 @@ function Storage:import(source, slot, count, item)
entry = itemDB:add(item) entry = itemDB:add(item)
else else
-- get the metadata from the device and add to db -- 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 end
itemDB:flush() itemDB:flush()
end end

41
milo/apis/turtleInv.lua Normal file
View 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

View File

@@ -312,14 +312,10 @@ The settings will take effect immediately!]],
margin = 1, margin = 1,
manualControls = true, manualControls = true,
[1] = UI.Checkbox { [1] = UI.Checkbox {
formLabel = 'Ignore Dmg', formKey = 'ignoreDamage', formLabel = 'Ignore NBT', formKey = 'ignoreNbt',
help = 'Ignore damage of item',
},
[2] = UI.Checkbox {
formLabel = 'Ignore NBT', formKey = 'ignoreNbtHash',
help = 'Ignore NBT of item', help = 'Ignore NBT of item',
}, },
[3] = UI.Chooser { [2] = UI.Chooser {
width = 13, width = 13,
formLabel = 'Mode', formKey = 'blacklist', formLabel = 'Mode', formKey = 'blacklist',
nochoice = 'whitelist', nochoice = 'whitelist',
@@ -327,7 +323,7 @@ The settings will take effect immediately!]],
{ name = 'whitelist', value = false }, { name = 'whitelist', value = false },
{ name = 'blacklist', value = true }, { name = 'blacklist', value = true },
}, },
help = 'Ignore damage of item' help = 'Accept item by default or deny by default'
}, },
scan = UI.Button { scan = UI.Button {
x = -11, y = 1, x = -11, y = 1,
@@ -349,7 +345,7 @@ The settings will take effect immediately!]],
self.form:setValues(entry) self.form:setValues(entry)
self:resetGrid() self:resetGrid()
self.form[3].inactive = whitelistOnly self.form[2].inactive = whitelistOnly
UI.SlideOut.show(self) UI.SlideOut.show(self)
self:setFocus(self.form.scan) self:setFocus(self.form.scan)

View File

@@ -30,7 +30,7 @@ function ExportTask:cycle(context)
end end
local function exportSingleSlot() 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 if slot and slot.count == slot.maxCount then
return return
@@ -41,8 +41,7 @@ function ExportTask:cycle(context)
for key in pairs(entry.filter) do for key in pairs(entry.filter) do
local filterItem = itemDB:splitKey(key) local filterItem = itemDB:splitKey(key)
if (slot.name == filterItem.name and if (slot.name == filterItem.name and
(entry.ignoreDamage or slot.damage == filterItem.damage) and (entry.ignoreNbt or slot.nbt == filterItem.nbt)) then
(entry.ignoreNbtHash or slot.nbtHash == filterItem.nbtHash)) then
local items = Milo:getMatches(filterItem, entry) local items = Milo:getMatches(filterItem, entry)
local _, item = next(items) local _, item = next(items)
@@ -81,8 +80,7 @@ function ExportTask:cycle(context)
for i = 1, node.adapter.__size do for i = 1, node.adapter.__size do
local slot = slots[i] local slot = slots[i]
if (not slot or slot.name == item.name and if (not slot or slot.name == item.name and
(entry.ignoreDamage or slot.damage == item.damage) and (entry.ignoreNbt or slot.nbt == item.nbt) and
(entry.ignoreNbtHash or slot.nbtHash == item.nbtHash) and
slot.count < item.maxCount) then slot.count < item.maxCount) then
return true return true

View File

@@ -21,7 +21,7 @@ function ImportTask:cycle(context)
for _, entry in pairs(node.imports) do for _, entry in pairs(node.imports) do
local function itemMatchesFilter(item) local function itemMatchesFilter(item)
if not entry.ignoreDamage and not entry.ignoreNbtHash then if not entry.ignoreNbt then
local key = itemDB:makeKey(item) local key = itemDB:makeKey(item)
return entry.filter[key] return entry.filter[key]
end end
@@ -29,8 +29,7 @@ function ImportTask:cycle(context)
for key in pairs(entry.filter) do for key in pairs(entry.filter) do
local v = itemDB:splitKey(key) local v = itemDB:splitKey(key)
if item.name == v.name and if item.name == v.name and
(entry.ignoreDamage or item.damage == v.damage) and (entry.ignoreNbt or item.nbt == v.nbt) then
(entry.ignoreNbtHash or item.nbtHash == v.nbtHash) then
return true return true
end end
end end
@@ -52,7 +51,7 @@ function ImportTask:cycle(context)
local function importSlot(slotNo) local function importSlot(slotNo)
local item = itemDB:get(list[slotNo], function() local item = itemDB:get(list[slotNo], function()
return node.adapter.getItemMeta(slotNo) return node.adapter.getItemDetail(slotNo)
end) end)
if item and matchesFilter(item) then if item and matchesFilter(item) then
if context.storage:import(node, slotNo, item.count, item) ~= item.count then if context.storage:import(node, slotNo, item.count, item) ~= item.count then

View File

@@ -20,15 +20,17 @@ function infoTab:draw()
Ansi.orange, item.displayName, Ansi.reset, Ansi.orange, item.displayName, Ansi.reset,
item.name) item.name)
if item.nbtHash then if item.nbt then
value = value .. item.nbtHash .. '\n' value = value .. item.nbt .. '\n'
end end
value = value .. string.format('\n%sCount:%s %s', value = value .. string.format('\n%sCount:%s %s',
Ansi.yellow, Ansi.reset, item.count) Ansi.yellow, Ansi.reset, item.count)
value = value .. string.format('\n%sDamage:%s %s', if item.durability then
Ansi.yellow, Ansi.reset, item.damage) value = value .. string.format('\n%Durability:%s %s',
Ansi.yellow, Ansi.reset, item.durability)
end
if item.maxDamage and item.maxDamage > 0 then if item.maxDamage and item.maxDamage > 0 then
value = value .. string.format(' (max: %s)', item.maxDamage) value = value .. string.format(' (max: %s)', item.maxDamage)

View File

@@ -29,11 +29,7 @@ local manageTab = UI.Tab {
transform = 'number', transform = 'number',
}, },
[4] = UI.Checkbox { [4] = UI.Checkbox {
formLabel = 'Ignore Dmg', formKey = 'ignoreDamage', formLabel = 'Ignore NBT', formKey = 'ignoreNbt',
help = 'Ignore damage of item',
},
[5] = UI.Checkbox {
formLabel = 'Ignore NBT', formKey = 'ignoreNbtHash',
help = 'Ignore NBT of item', help = 'Ignore NBT of item',
}, },
}, },
@@ -44,8 +40,6 @@ function manageTab:setItem(item)
self.res = Util.shallowCopy(context.resources[item.key] or { }) self.res = Util.shallowCopy(context.resources[item.key] or { })
self.res.displayName = self.item.displayName self.res.displayName = self.item.displayName
self.form:setValues(self.res) self.form:setValues(self.res)
-- TODO: ignore damage should not be active if there is not a maxDamage value
end end
function manageTab:eventHandler(event) function manageTab:eventHandler(event)
@@ -76,8 +70,7 @@ function manageTab:eventHandler(event)
local newKey = { local newKey = {
name = self.item.name, name = self.item.name,
damage = self.res.ignoreDamage and 0 or self.item.damage, nbt = not self.res.ignoreNbt and self.item.nbt or nil,
nbtHash = not self.res.ignoreNbtHash and self.item.nbtHash or nil,
} }
context.resources[self.item.key] = nil context.resources[self.item.key] = nil

View File

@@ -42,7 +42,7 @@ function recipeTab:setItem(item)
}) })
end end
local key = itemDB:splitKey(self.recipe.result) local key = itemDB:splitKey(self.recipe.result)
self.ignoreResultNBT.inactive = not key.nbtHash self.ignoreResultNBT.inactive = not key.nbt
end end
self.grid:setValues(t) self.grid:setValues(t)
end end
@@ -53,7 +53,7 @@ function recipeTab:eventHandler(event)
Milo:updateRecipe(self.recipe.result) Milo:updateRecipe(self.recipe.result)
local item = itemDB:splitKey(self.recipe.result) local item = itemDB:splitKey(self.recipe.result)
item.nbtHash = nil item.nbt = nil
self.recipe.result = itemDB:makeKey(item) self.recipe.result = itemDB:makeKey(item)
-- add updated entry -- add updated entry
@@ -64,13 +64,13 @@ function recipeTab:eventHandler(event)
elseif event.type == 'grid_focus_row' then elseif event.type == 'grid_focus_row' then
local key = itemDB:splitKey(event.selected.key) local key = itemDB:splitKey(event.selected.key)
self.ignoreNBT.inactive = not key.nbtHash self.ignoreNBT.inactive = not key.nbt
self.ignoreNBT:draw() self.ignoreNBT:draw()
elseif event.type == 'ignore_nbt' then elseif event.type == 'ignore_nbt' then
local selected = self.grid:getSelected() local selected = self.grid:getSelected()
local item = itemDB:splitKey(selected.key) local item = itemDB:splitKey(selected.key)
item.nbtHash = nil item.nbt = nil
selected.key = itemDB:makeKey(item) selected.key = itemDB:makeKey(item)
self.grid:draw() self.grid:draw()

View File

@@ -94,7 +94,7 @@ local function client(socket)
local function deposit() local function deposit()
local node = makeNode(data.source or 'inventory') local node = makeNode(data.source or 'inventory')
if node then if node then
local slot = node.adapter.getItemMeta(data.slot) local slot = node.adapter.getItemDetail(data.slot)
if slot then if slot then
if context.storage:import(node, data.slot, slot.count, slot) > 0 then if context.storage:import(node, data.slot, slot.count, slot) > 0 then
local item = Milo:getItem(slot) local item = Milo:getItem(slot)

View File

@@ -15,14 +15,13 @@ function ReplenishTask:cycle(context)
local _, count = Milo:getMatches(item, res) local _, count = Milo:getMatches(item, res)
if count < res.low then if count < res.low then
local nbtHash local nbt
if not res.ignoreNbtHash then if not res.ignoreNbt then
nbtHash = item.nbtHash nbt = item.nbt
end end
Milo:requestCrafting({ Milo:requestCrafting({
name = item.name, name = item.name,
damage = res.ignoreDamage and 0 or item.damage, nbt = nbt,
nbtHash = nbtHash,
requested = res.low - count, requested = res.low - count,
count = count, count = count,
replenish = true, replenish = true,

View File

@@ -12,7 +12,7 @@ local jua = require("swshop.jua")
local await = jua.await local await = jua.await
local device = _G.device local device = _G.device
local json = _G.json local json = { encode=textutils.serializeJSON, decode = textutils.unserialiseJSON }
local rs = _G.rs local rs = _G.rs
local textutils = _G.textutils local textutils = _G.textutils