forked from mtcontrib/pipeworks
use nodetimers instead of abm's to run the autocrafters; only run autocrafters when needed
autocrafters will stop() when theres no valid recipe, no dst space or enough src material it will resume again on inventory or recipe changes
This commit is contained in:
parent
2ccce52976
commit
44bafb844a
155
autocrafter.lua
155
autocrafter.lua
@ -2,48 +2,106 @@ local autocrafterCache = {} -- caches some recipe data to avoid to call the slo
|
|||||||
|
|
||||||
local function count_index(invlist)
|
local function count_index(invlist)
|
||||||
local index = {}
|
local index = {}
|
||||||
for _, stack in ipairs(invlist) do
|
for _, stack in pairs(invlist) do
|
||||||
|
if not stack:is_empty() then
|
||||||
local stack_name = stack:get_name()
|
local stack_name = stack:get_name()
|
||||||
index[stack_name] = (index[stack_name] or 0) + stack:get_count()
|
index[stack_name] = (index[stack_name] or 0) + stack:get_count()
|
||||||
end
|
end
|
||||||
|
end
|
||||||
return index
|
return index
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_cached_craft(pos)
|
local function get_craft(pos, inventory, hash)
|
||||||
local hash = minetest.hash_node_position(pos)
|
local hash = hash or minetest.hash_node_position(pos)
|
||||||
return hash, autocrafterCache[hash]
|
local craft = autocrafterCache[hash]
|
||||||
|
if not craft then
|
||||||
|
if inventory:is_empty("recipe") then return nil end
|
||||||
|
local recipe = inventory:get_list("recipe")
|
||||||
|
local output, decremented_input = minetest.get_craft_result({method = "normal", width = 3, items = recipe})
|
||||||
|
craft = {recipe = recipe, consumption=count_index(recipe), output = output, decremented_input = decremented_input}
|
||||||
|
autocrafterCache[hash] = craft
|
||||||
|
|
||||||
|
end
|
||||||
|
-- only return crafts that have an actual result
|
||||||
|
if not craft.output.item:is_empty() then
|
||||||
|
return craft
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- note, that this function assumes allready being updated to virtual items
|
-- note, that this function assumes allready being updated to virtual items
|
||||||
-- and doesn't handle recipes with stacksizes > 1
|
-- and doesn't handle recipes with stacksizes > 1
|
||||||
local function on_recipe_change(pos, inventory)
|
local function on_recipe_change(pos, inventory)
|
||||||
if not inventory then return end
|
-- if we emptied the grid, there's no point in keeping it running or cached
|
||||||
local recipe = inventory:get_list("recipe")
|
if inventory:is_empty("recipe") then
|
||||||
if not recipe then return end
|
minetest.get_node_timer(pos):stop()
|
||||||
|
autocrafterCache[minetest.hash_node_position(pos)] = nil
|
||||||
|
return
|
||||||
|
end
|
||||||
local recipe_changed = false
|
local recipe_changed = false
|
||||||
local hash, craft = get_cached_craft(pos)
|
local recipe = inventory:get_list("recipe")
|
||||||
|
|
||||||
if not craft then
|
local hash = minetest.hash_node_position(pos)
|
||||||
recipe_changed = true
|
local craft = autocrafterCache[hash]
|
||||||
else
|
|
||||||
|
if craft then
|
||||||
-- check if it changed
|
-- check if it changed
|
||||||
local cached_recipe = craft.recipe
|
local cached_recipe = craft.recipe
|
||||||
for i = 1, 9 do
|
for i = 1, 9 do
|
||||||
if recipe[i]:get_name() ~= cached_recipe[i]:get_name() then
|
if recipe[i]:get_name() ~= cached_recipe[i]:get_name() then
|
||||||
recipe_changed = true
|
autocrafterCache[hash] = nil -- invalidate recipe
|
||||||
|
craft = nil
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if recipe_changed then
|
local timer = minetest.get_node_timer(pos)
|
||||||
local output, decremented_input = minetest.get_craft_result({method = "normal", width = 3, items = recipe})
|
if not timer:is_started() then
|
||||||
craft = {recipe = recipe, consumption=count_index(recipe), output = output, decremented_input = decremented_input}
|
timer:start(1)
|
||||||
autocrafterCache[hash] = craft
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function on_inventory_change(pos, inventory)
|
||||||
|
local timer = minetest.get_node_timer(pos)
|
||||||
|
if not timer:is_started() then
|
||||||
|
timer:start(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function autocraft(inventory, craft)
|
||||||
|
if not craft then return false end
|
||||||
|
local output_item = craft.output.item
|
||||||
|
|
||||||
|
-- check if we have enough room in dst
|
||||||
|
if not inventory:room_for_item("dst", output_item) then return false end
|
||||||
|
local consumption = craft.consumption
|
||||||
|
local inv_index = count_index(inventory:get_list("src"))
|
||||||
|
-- check if we have enough material available
|
||||||
|
for itemname, number in pairs(consumption) do
|
||||||
|
if (not inv_index[itemname]) or inv_index[itemname] < number then return false end
|
||||||
|
end
|
||||||
|
-- consume material
|
||||||
|
for itemname, number in pairs(consumption) do
|
||||||
|
for i = 1, number do -- We have to do that since remove_item does not work if count > stack_max
|
||||||
|
inventory:remove_item("src", ItemStack(itemname))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return craft
|
-- craft the result into the dst inventory and add any "replacements" as well
|
||||||
|
inventory:add_item("dst", output_item)
|
||||||
|
for i = 1, 9 do
|
||||||
|
inventory:add_item("dst", craft.decremented_input.items[i])
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- returns false to stop the timer, true to continue running
|
||||||
|
-- is started only from start_autocrafter(pos) after sanity checks and cached recipe
|
||||||
|
local function run_autocrafter(pos, elapsed)
|
||||||
|
local meta = minetest.get_meta(pos)
|
||||||
|
local inventory = meta:get_inventory()
|
||||||
|
local craft = get_craft(pos, inventory)
|
||||||
|
return autocraft(inventory, craft)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function update_autocrafter(pos)
|
local function update_autocrafter(pos)
|
||||||
@ -61,51 +119,6 @@ local function update_autocrafter(pos)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function autocraft(inventory, craft)
|
|
||||||
local output_item = craft.output.item
|
|
||||||
-- check if we have enough room in dst
|
|
||||||
if not inventory:room_for_item("dst", output_item) then return false end
|
|
||||||
|
|
||||||
local consumption = craft.consumption
|
|
||||||
local inv_index = count_index(inventory:get_list("src"))
|
|
||||||
-- check if we have enough materials available
|
|
||||||
for itemname, number in pairs(consumption) do
|
|
||||||
if (not inv_index[itemname]) or inv_index[itemname] < number then return false end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- consume materials
|
|
||||||
for itemname, number in pairs(consumption) do
|
|
||||||
for i = 1, number do -- We have to do that since remove_item does not work if count > stack_max
|
|
||||||
inventory:remove_item("src", ItemStack(itemname))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- craft the result into the dst inventory and add any "replacements" as well
|
|
||||||
inventory:add_item("dst", output_item)
|
|
||||||
for i = 1, 9 do
|
|
||||||
inventory:add_item("dst", craft.decremented_input.items[i])
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
local function run_autocrafter(inventory, pos)
|
|
||||||
if not inventory then return end
|
|
||||||
local recipe = inventory:get_list("recipe")
|
|
||||||
if not recipe then return end
|
|
||||||
|
|
||||||
local _, craft = get_cached_craft(pos)
|
|
||||||
if craft == nil then
|
|
||||||
update_autocrafter(pos) -- only does some unnecessary calls for "old" autocrafters
|
|
||||||
craft = on_recipe_change(pos, inventory)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- skip crafts that have no output
|
|
||||||
local output_item = craft.output.item
|
|
||||||
if output_item:is_empty() then return end
|
|
||||||
|
|
||||||
autocraft(inventory, craft)
|
|
||||||
end
|
|
||||||
|
|
||||||
minetest.register_node("pipeworks:autocrafter", {
|
minetest.register_node("pipeworks:autocrafter", {
|
||||||
description = "Autocrafter",
|
description = "Autocrafter",
|
||||||
drawtype = "normal",
|
drawtype = "normal",
|
||||||
@ -114,7 +127,9 @@ minetest.register_node("pipeworks:autocrafter", {
|
|||||||
tube = {insert_object = function(pos, node, stack, direction)
|
tube = {insert_object = function(pos, node, stack, direction)
|
||||||
local meta = minetest.get_meta(pos)
|
local meta = minetest.get_meta(pos)
|
||||||
local inv = meta:get_inventory()
|
local inv = meta:get_inventory()
|
||||||
return inv:add_item("src", stack)
|
local added = inv:add_item("src", stack)
|
||||||
|
on_inventory_change(pos, inv)
|
||||||
|
return added
|
||||||
end,
|
end,
|
||||||
can_insert = function(pos, node, stack, direction)
|
can_insert = function(pos, node, stack, direction)
|
||||||
local meta = minetest.get_meta(pos)
|
local meta = minetest.get_meta(pos)
|
||||||
@ -162,6 +177,7 @@ minetest.register_node("pipeworks:autocrafter", {
|
|||||||
on_recipe_change(pos, inv)
|
on_recipe_change(pos, inv)
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
|
on_inventory_change(pos, inv)
|
||||||
return stack:get_count()
|
return stack:get_count()
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
@ -173,6 +189,7 @@ minetest.register_node("pipeworks:autocrafter", {
|
|||||||
on_recipe_change(pos, inv)
|
on_recipe_change(pos, inv)
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
|
on_inventory_change(pos, inv)
|
||||||
return stack:get_count()
|
return stack:get_count()
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
@ -192,15 +209,9 @@ minetest.register_node("pipeworks:autocrafter", {
|
|||||||
on_recipe_change(pos, inv)
|
on_recipe_change(pos, inv)
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
|
on_inventory_change(pos, inv)
|
||||||
return stack:get_count()
|
return stack:get_count()
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
})
|
on_timer = run_autocrafter
|
||||||
|
|
||||||
minetest.register_abm({nodenames = {"pipeworks:autocrafter"}, interval = 1, chance = 1,
|
|
||||||
action = function(pos, node)
|
|
||||||
local meta = minetest.get_meta(pos)
|
|
||||||
local inv = meta:get_inventory()
|
|
||||||
run_autocrafter(inv, pos)
|
|
||||||
end
|
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user