1
0
mirror of https://github.com/mt-mods/pipeworks.git synced 2025-05-11 21:30:22 +02:00

use a fake inventory method instead of detached inv

thanks to @fluxionary for pointing me to his fake inventory class.

This way of checking was what I was trying to avoid, but it will
hopefully prove to have been worth the effort.
It requires a lot more code than the detached inv method did.
This commit is contained in:
Luke aka SwissalpS 2023-06-13 02:58:58 +02:00
parent bb88c54a55
commit 802c1e73c6

View File

@ -1,6 +1,5 @@
local S = minetest.get_translator("pipeworks") local S = minetest.get_translator("pipeworks")
local autocrafterCache = {} -- caches some recipe data to avoid to call the slow function minetest.get_craft_result() every second local autocrafterCache = {} -- caches some recipe data to avoid to call the slow function minetest.get_craft_result() every second
local sandbox_inv_name = "pipeworks:autocrafter_sandbox"
local craft_time = 1 local craft_time = 1
local function count_index(invlist) local function count_index(invlist)
@ -33,14 +32,96 @@ local function get_craft(pos, inventory, hash)
return craft return craft
end end
-- Check if items from list_src fit into list_dst.
-- Items in list_src are expected to be ItemStack objects and have legal size.
-- Items in list_dst may be any type ItemStack constructor accepts.
-- based on: https://github.com/fluxionary/minetest-futil/blob/5f895d7be879054ea24bf6ce65eb60347bdd9338/minetest/fake_inventory.lua
local function has_room_for_in(list_src, list_dst)
local i = #list_dst
if i == 0 then return false end -- sanity check jic
-- copy destination list indexing list
local dst = {}
local stack, name
local indexes = { __empty__ = {} }
repeat
stack = ItemStack(list_dst[i])
name = stack:get_name()
if stack:is_empty() then name = "__empty__" end
if not indexes[name] then indexes[name] = {} end
indexes[name][#indexes[name] + 1] = i
dst[i] = stack
i = i - 1
until i == 0
-- purge empty slots from source list
local src = {}
i = #list_src
if i > 0 then -- jic
repeat
if not list_src[i]:is_empty() then
src[#src + 1] = list_src[i]
end
i = i - 1
until i == 0
end
-- Shortcut check is safe as we can expect all stacks in list_src
-- to have valid stack sizes.
-- If there are plenty empty slots, confirm to have space.
if #indexes.__empty__ >= #src then return true end
-- try adding each stack to slots until stacks are empty
local j, done
i = #src
if i == 0 then return true end -- sanity check jic
repeat
done = false
stack = ItemStack(src[i])
name = stack:get_name()
-- first try stacks with same item name
if indexes[name] then
j = #indexes[name]
repeat
stack = dst[indexes[name][j]]:add_item(stack)
if stack:is_empty() then
done = true
break
end
j = j - 1
until j == 0
end
-- do we have 'empty' slots to test?
j = #indexes.__empty__
if not done and j == 0 then return false end
if not done then
-- try adding stack to empty slots until stack is empty
-- note: slots may no longer be empty (as we don't update indexes here)
repeat
stack = dst[indexes.__empty__[j]]:add_item(stack)
if stack:is_empty() then
done = true
break
end
j = j - 1
until j == 0
-- we've tried all sensible slots, if stack still contains items
-- there is not enough space, no need to try any others
if not done then return false end
end
-- this item fit, try next one
i = i - 1
until i == 0
-- all stacks from source list fit into destination list
return true
end
local function autocraft(inventory, craft) local function autocraft(inventory, craft)
if not craft then return false end if not craft then return false end
local output_item = craft.output.item
-- check if we have enough room in dst for output item(s)
-- we'll check replacements later
if not inventory:room_for_item("dst", output_item) then return false end
local consumption = craft.consumption local consumption = craft.consumption
local inv_index = count_index(inventory:get_list("src")) local inv_index = count_index(inventory:get_list("src"))
-- check if we have enough material available -- check if we have enough material available
@ -48,31 +129,39 @@ local function autocraft(inventory, craft)
if (not inv_index[itemname]) or inv_index[itemname] < number then return false end if (not inv_index[itemname]) or inv_index[itemname] < number then return false end
end end
-- since there is no inv:room_for_items() method, we use a sandbox inventory -- make a list of output stacks
-- see: https://github.com/mt-mods/pipeworks/issues/61 local list_out = { craft.output.item }
local sandbox_inv = minetest.create_detached_inventory(sandbox_inv_name) local i = #craft.decremented_input.items
sandbox_inv:set_size("", inventory:get_size("dst")) repeat
sandbox_inv:set_list("", inventory:get_list("dst")) -- purge empty stacks
-- we checked if this fits, so go ahead if not craft.decremented_input.items[i]:is_empty() then
sandbox_inv:add_item("", output_item) list_out[#list_out + 1] = craft.decremented_input.items[i]
-- now try to add replacements
local replacement
for i = 1, 9 do
replacement = craft.decremented_input.items[i]
if not replacement:is_empty() then
if not sandbox_inv:room_for_item("", replacement) then
-- cleanup and leave
minetest.remove_detached_inventory(sandbox_inv_name)
return false
end
sandbox_inv:add_item("", replacement)
end end
i = i - 1
until i == 0
-- since there is no inv:room_for_items() method, we use our own
-- see: https://github.com/mt-mods/pipeworks/issues/61
if not has_room_for_in(list_out, inventory:get_list("dst")) then
return false
end end
-- success, so apply to actual output inventory -- success, so safely add items to output inventory
inventory:set_list("dst", sandbox_inv:get_list("")) local leftover
-- destroy sandbox inv i = #list_out
minetest.remove_detached_inventory(sandbox_inv_name) repeat
leftover = inventory:add_item("dst", list_out[i])
if not leftover:is_empty() then
local location = inventory:get_location()
local error_text = "[pipeworks] autocrafter failed to predict space for output items."
if location.type == "node" then
error_text = error_text .. " At " .. minetest.pos_to_string(location.pos)
end
minetest.log("warning", error_text)
end
i = i - 1
until i == 0
-- consume materials -- consume materials
for itemname, number in pairs(consumption) do for itemname, number in pairs(consumption) do
for _ = 1, number do -- We have to do that since remove_item does not work if count > stack_max for _ = 1, number do -- We have to do that since remove_item does not work if count > stack_max