WIP: Try to update Milo for 1.19 #63
@@ -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
Only includes the NBT hash with the 'true' second arg. At least in my testing in 1.18.2; may be different in 1.19
Instead of fixing this, I could just wait for cc-tweaked/cc-tweaked#1306 😴