Checkpoint.

This commit is contained in:
Mike Stump 2023-12-30 15:52:43 -08:00
parent f5aee436d0
commit 9235b7bad1
18 changed files with 3829 additions and 736 deletions

134
api.lua

@ -3,66 +3,66 @@ local BASENAME = "microexpansion"
-- [function] Register Recipe -- [function] Register Recipe
function microexpansion.register_recipe(output, recipe) function microexpansion.register_recipe(output, recipe)
-- Check if disabled -- Check if disabled
if recipe.disabled == true then if recipe.disabled == true then
return return
end end
for _,r in ipairs(recipe) do for _,r in ipairs(recipe) do
local def = { local def = {
type = type(r[2]) == "string" and r[2] or nil, type = type(r[2]) == "string" and r[2] or nil,
output = output.." "..(r[1] or 1), output = output.." "..(r[1] or 1),
recipe = r[3] or r[2] recipe = r[3] or r[2]
} }
minetest.register_craft(def) minetest.register_craft(def)
end end
end end
-- [function] Register oredef -- [function] Register oredef
function microexpansion.register_oredef(ore, defs) function microexpansion.register_oredef(ore, defs)
-- Check if disabled -- Check if disabled
if defs.disabled == true then if defs.disabled == true then
return return
end end
for _,d in ipairs(defs) do for _,d in ipairs(defs) do
d.ore = "microexpansion:"..ore d.ore = "microexpansion:"..ore
minetest.register_ore(d) minetest.register_ore(d)
end end
end end
-- [local function] Choose description colour -- [local function] Choose description colour
local function desc_colour(status, desc) local function desc_colour(status, desc)
if status == "unstable" then if status == "unstable" then
return minetest.colorize("orange", desc) return minetest.colorize("orange", desc)
elseif status == "no" then elseif status == "no" then
return minetest.colorize("red", desc) return minetest.colorize("red", desc)
else else
return minetest.colorize("white", desc) return minetest.colorize("white", desc)
end end
end end
-- [function] Register Item -- [function] Register Item
function microexpansion.register_item(itemstring, def) function microexpansion.register_item(itemstring, def)
-- Check if disabled -- Check if disabled
if def.disabled == true then if def.disabled == true then
return return
end end
-- Set usedfor -- Set usedfor
if def.usedfor then if def.usedfor then
def.description = def.description .. "\n"..minetest.colorize("grey", def.usedfor) def.description = def.description .. "\n"..minetest.colorize("grey", def.usedfor)
end end
-- Update inventory image -- Update inventory image
if def.inventory_image then if def.inventory_image then
def.inventory_image = BASENAME.."_"..def.inventory_image..".png" def.inventory_image = BASENAME.."_"..def.inventory_image..".png"
else else
def.inventory_image = BASENAME.."_"..itemstring..".png" def.inventory_image = BASENAME.."_"..itemstring..".png"
end end
-- Colour description -- Colour description
def.description = desc_colour(def.status, def.description) def.description = desc_colour(def.status, def.description)
-- Register craftitem -- Register craftitem
minetest.register_craftitem(BASENAME..":"..itemstring, def) minetest.register_craftitem(BASENAME..":"..itemstring, def)
-- if recipe, Register recipe -- if recipe, Register recipe
if def.recipe then if def.recipe then
@ -93,43 +93,43 @@ function microexpansion.register_node(itemstring, def)
end end
end end
end end
-- Colour description -- Colour description
def.description = desc_colour(def.status, def.description) def.description = desc_colour(def.status, def.description)
-- Update connect_sides -- Update connect_sides
if def.connect_sides == "nobottom" then if def.connect_sides == "nobottom" then
def.connect_sides = { "top", "front", "left", "back", "right" } def.connect_sides = { "top", "front", "left", "back", "right" }
elseif def.connect_sides == "machine" then elseif def.connect_sides == "machine" then
def.connect_sides = { "top", "bottom", "left", "back", "right" } def.connect_sides = { "top", "bottom", "left", "back", "right" }
end end
-- Register node -- Register node
minetest.register_node(BASENAME..":"..itemstring, def) minetest.register_node(BASENAME..":"..itemstring, def)
-- if recipe, Register recipe -- if recipe, Register recipe
if def.recipe then if def.recipe then
microexpansion.register_recipe(BASENAME..":"..itemstring, def.recipe) microexpansion.register_recipe(BASENAME..":"..itemstring, def.recipe)
end end
-- if oredef, Register oredef -- if oredef, Register oredef
if def.oredef then if def.oredef then
microexpansion.register_oredef(BASENAME..":"..itemstring, def.oredef) microexpansion.register_oredef(BASENAME..":"..itemstring, def.oredef)
end end
end end
-- get a node, if nessecary load it -- get a node, if necessary load it
function microexpansion.get_node(pos) function microexpansion.get_node(pos)
local node = minetest.get_node_or_nil(pos) local node = minetest.get_node_or_nil(pos)
if node then return node end if node then return node end
local vm = VoxelManip() local vm = VoxelManip()
vm:read_from_map(pos, pos) vm:read_from_map(pos, pos)
return minetest.get_node(pos) return minetest.get_node(pos)
end end
function microexpansion.update_node(pos,event) function microexpansion.update_node(pos,event)
local node = microexpansion.get_node(pos) local node = microexpansion.get_node(pos)
local def = minetest.registered_nodes[node.name] local def = minetest.registered_nodes[node.name]
local ev = event or {type = "n/a"} local ev = event or {type = "n/a"}
if def.me_update then if def.me_update then
def.me_update(pos,node,ev) def.me_update(pos,node,ev)
end end
end end

@ -1,4 +1,4 @@
name = microexpansion name = microexpansion
description = A storage managing solution to get an overview over all your items. description = A storage managing solution to get an overview over all your items.
depends = default depends = default
optional_depends = pipeworks optional_depends = pipeworks, drawers, technic_plus

@ -0,0 +1,477 @@
local me = microexpansion
local pipeworks_craft_time = 1
function me.autocraft_next_start(net)
if net.pending then
-- start subsequent autocrafting jobs sequentially.
-- We really only need zero/not zero for build to queue actions or not
return net.pending.time[net.pending.max_index]
end
return 0
end
function me.start_crafting(pos, step_time)
local meta = minetest.get_meta(pos)
local timer = minetest.get_node_timer(pos)
timer:set(step_time, 0)
end
local function round(v)
return math.floor(v + 0.5)
end
-- technic_plus doesn't export machine speed. We
-- use this to know exactly how long a machine will take to process
-- anything, after that time, we know it is done and we can grab the
-- outputs, no polling. We do this for efficiency.
me.speed = {
["default:furnace"] = 1,
}
-- Use to wire in how long a machine takes to process something.
function me.set_speed(name, speed)
me.speed[name] = speed
end
-- Sometimes the poor autocrafter doesn't have infinite input and output room
-- for a craft, break large ones up to fit.
-- Also machine inputs/outputs don't fit.
me.maximums = {
}
-- Allow a maximum craft to be defined to avoid overrunning machine
-- inputs and outputs and the autocrafter inputs and output..
function me.register_max(name, count)
me.maximums[name] = count
end
local function build(net, cpos, inv, name, count, stack, sink, time)
-- The autocrafters nor the machines can take really large amounts
-- of things, help them out.
local max = me.maximums[name]
if not max then
-- If no explicit max, assume this is a pipeworks autocrafter and
-- it only has 12 outputs.
max = stack:get_stack_max()*12
end
if max and count > max then
local next_time = time
local built = true
while count > 1 and built do
local substack = ItemStack(stack)
max = math.min(max, count)
substack:set_count(max)
local step_time
built, step_time = build(net, cpos, inv, name, max, substack, sink, next_time)
if not built then
-- we are done, can't craft, so stop
else
next_time = math.max(next_time, next_time + step_time)
end
count = count - max
end
local step_time = next_time - time
return built, step_time
end
me.log("BUILD: count is "..count.." and stack size is "..stack:get_count(), "error")
local dat = {}
local second_output = nil
if net.process and net.process[name] then
dat.apos, dat.ipos = next(net.process[name])
dat.rinv = minetest.get_meta(dat.apos):get_inventory()
me.log("INT: looking up output "..name, "error")
local inputs = me.find_by_output(name)
local machine_name = minetest.get_node(dat.apos).name
local typename = me.block_to_typename_map[machine_name]
dat.recip = me.get_recipe(typename, inputs)
me.log("MACHINE: "..machine_name.." typename is "..typename.." and time is "..tostring(dat.recip.time), "error")
dat.recip.input = inputs
-- freezer can produce two outputs, we only care about the first.
if dat.recip.output[1] then
second_output = dat.recip.output[2]
dat.recip.output = dat.recip.output[1]
end
dat.ostack = ItemStack(dat.recip.output)
-- me.log("SEE: "..machine_name.." "..minetest.serialize(technic.recipes))
local speed = me.speed[machine_name]
local craft_count = dat.ostack:get_count()
local total = math.ceil(count/craft_count)
-- crafting 4 carbon plates misses taking 1 carbin plate on output, make this bigger
-- we'll try 1 for now, figure out right formula. 1 looks perfect
dat.recip.time = round((total+1)*dat.recip.time/speed)
if second_output then
second_output = ItemStack(second_output)
second_output:set_count(second_output:get_count()*total)
end
me.log("MACHINE: "..machine_name.." is speed "..speed.." and final time is "..dat.recip.time, "error")
elseif net.autocrafters[name] then
-- fill all recipe slots, wait, grab all output slots
-- "src" "dst" "recipe" "output"
dat.apos, dat.ipos = next(net.autocrafters[name])
-- TODO: If we set up pipeworks ac, then we remove interface for it and craft
-- it goes to ac, and dies here. Flush net.autocrafters for all the
-- attached inventories during interface removal.
dat.rinv = minetest.get_meta(dat.apos):get_inventory()
if dat.rinv == nil then
me.log("no inventory", "error")
return
end
dat.ostack = dat.rinv:get_stack("output", 1)
if dat.ostack:get_name() ~= name then
-- invalidate it
net.autocrafters[name][dat.apos] = nil
-- me.log("invalidating autocrafter", "error")
return
end
else
me.log("can't craft a "..name, "error")
return
end
dat.isink = function(sstack)
me.log("TIMER: prep inputs, moving "..sstack:get_count().." "..sstack:get_name(), "error")
return dat.rinv:add_item("src", sstack)
end
local craft_count = dat.ostack:get_count()
-- These will be returned to the me system
local extra = ItemStack(name)
local total = math.ceil(count/craft_count)
extra:set_count(total*craft_count - count)
me.log("AC: count "..count.." craft_count "..craft_count.." extra "..extra:get_count(), "error");
-- we craft a minimum of count, to the multiple of the crafting count
count = total
me.log("AC: numcount "..count, "error");
local consume = {}
if net.process and net.process[name] then
for i = 1, #dat.recip.input do
local inp = dat.recip.input[i]
me.log("MID: consuming "..inp:get_name().." count: "..count.." inp:getcount: "..inp:get_count(), "error")
consume[inp:get_name()] = (consume[inp:get_name()] or 0) + count*inp:get_count()
end
else
for i = 1, 9 do
local inp = dat.rinv:get_stack("recipe", i)
if inp and not inp:is_empty() then
consume[inp:get_name()] = (consume[inp:get_name()] or 0) + count*inp:get_count()
end
end
end
local replace = true
local next_time = time
me.log("PREP: pre count is "..count, "error")
-- prepwork
me.log("PREP: count is "..count, "error")
local prepworkbits = {}
local previous_ac_size = inv:get_size("ac")
me.log("PREP: ac size at top is "..previous_ac_size, "error")
for name, count in pairs(consume) do
local istack = ItemStack(name)
if count >= math.pow(2,16) then
replace = false
break
end
-- Don't consume the last item by autocrafting
istack:set_count(count+1)
local hasit = inv:contains_item("main", istack)
istack:set_count(count)
me.log("ac checking "..name, "error")
if hasit then
me.log("ac grabbing "..name, "error")
local grabbed = me.remove_item(net, inv, "main", istack)
if grabbed then
me.log("ac grabbed "..name, "error")
local slot = inv:get_size("ac")+1
inv:set_size("ac", slot)
inv:set_stack("ac", slot, grabbed)
-- and later we do this:
prepworkbits[function()
me.log("PREP: about to move "..name, "error")
local stack = inv:get_stack("ac", slot)
me.log("PREP: before move actual content of slot "..slot.." is "..stack:get_count().." "..stack:get_name(), "error")
local leftovers = dat.rinv:add_item("src", stack)
me.log("PREP: post move into real inventory "..leftovers:get_count().." leftovers", "error")
me.log("PREP: did move "..name, "error")
inv:set_stack("ac", slot, leftovers)
end] = name
-- and then something moves the size of ac back to before we started
end
else
-- Try and autocraft it
me.log("AC: recursive crafting "..count.." "..istack:get_count(), "error")
local built, step_time = build(net, cpos, inv, name, count, istack, dat.isink, time)
if built then
hasit = true
next_time = math.max(next_time, time + step_time)
else
me.log("can't craft "..istack:get_count().." "..istack:get_name(), "error")
end
end
replace = replace and hasit
end
local prepwork = function ()
-- Do all the little bits of prepwork
for func, name in pairs(prepworkbits) do
me.log("PREPing: before "..name, "error")
func()
me.log("PREPing: done "..name, "error")
end
end
-- end of prepwork
if not replace then
-- If we don't have everything, and can't craft it, we're stuck,
-- do as much as we can, then nothing else
me.log("missing items", "error")
-- Existing items are already loaded.
return
end
local main_action_time = count * pipeworks_craft_time + 1
if net.process and net.process[name] then
main_action_time = dat.recip.time + 1
end
local main_action = function()
me.log("ACTION: prep for "..stack:get_name(), "error")
prepwork()
-- and once we are done with all the postponed work, we can reduce "ac"
-- lifetimes are more complex than you can imagine.
-- We use a simple rule. When all done, there is nothing left. At that point,
-- we can put any leftovers back into the main inventory.
-- Even this might be too soon, if we have multiple independent crafts going, we
-- need the last one.
if previous_ac_size == 0 then
for i = 1,inv:get_size("ac") do
local stack = inv:get_stack("ac", i)
if stack:get_count() ~= 0 then
me.log("AC: putting "..stack:get_count().." "..stack:get_name().." back into main inventory", "error")
local leftovers = me.insert_item(stack, net, inv, "main")
if leftovers:get_count() > 0 then
-- drop on floor, todo: play sound
minetest.add_item(cpos, leftovers)
end
end
end
me.log("PREP: ac size is now down to "..previous_ac_size, "error")
inv:set_size("ac", previous_ac_size)
end
me.log("ACTION: main for "..stack:get_name(), "error")
local rmeta = minetest.get_meta(dat.apos)
-- Let's start up the crafter since we loaded it up to run
if (net.process and net.process[name]) or rmeta:get_int("enabled") == 1 then
local timer = minetest.get_node_timer(dat.apos)
if not timer:is_started() then
me.log("TIMER: starting ac now for "..stack:get_name(), "error")
timer:start(pipeworks_craft_time)
end
me.log("TIMER: registering timer for "..stack:get_name(), "error")
local action_time_step = count * pipeworks_craft_time + 1
if net.process and net.process[name] then
action_time_step = dat.recip.time + 1
end
local action = function(net)
me.log("ACTION: post craft for "..stack:get_name(), "error")
me.log("TIMER: moving "..stack:get_count().." "..stack:get_name(), "error")
-- deal with output and replacements
local dst_stack = dat.rinv:remove_item("dst", stack)
if dst_stack:get_count() ~= stack:get_count() then
me.log("wow, missing items that should have been crafted "..stack:get_name(), "error")
-- me.log("saw "..dst_stack:get_count().." items instead of "..stack:get_count().." items", "error")
end
if not dst_stack:is_empty() then
me.log("TIMER: inserting "..dst_stack:get_count().." "..dst_stack:get_name(), "error")
local leftovers = sink(dst_stack)
if leftovers and not leftovers:is_empty() then
me.log("autocrafter overflow, backpressuring", "error")
-- If any don't fit, back pressure on the crafter, we don't
-- mean to do this, and want to chunk the crafting items smaller
dat.rinv:add_item("dst", leftovers)
end
end
if not extra:is_empty() then
dst_stack = dat.rinv:remove_item("dst", extra)
if dst_stack:get_count() ~= extra:get_count() then
me.log("wow, missing items that should have been crafted "..stack:get_name(), "error")
me.log("saw "..dst_stack:get_count().." items instead of "..extra:get_count().." items", "error")
end
if not dst_stack:is_empty() then
local leftovers = me.insert_item(dst_stack, net, inv, "main")
net:set_storage_space(true)
if leftovers and not leftovers:is_empty() then
me.log("autocrafter overflow, backpressuring", "error")
-- If any don't fit, back pressure on the crafter, we don't
-- mean to do this, and want to chunk the crafting items smaller
dat.rinv:add_item("dst", leftovers)
end
end
end
if second_output then
local second = dat.rinv:remove_item("dst", second_output)
if second and not second:is_empty() then
local leftovers = sink(second)
if leftovers and not leftovers:is_empty() then
me.log("autocrafter overflow, backpressuring", "error")
-- If any don't fit, back pressure on the crafter, we don't
-- mean to do this, and want to chunk the crafting items smaller
dat.rinv:add_item("dst", leftovers)
end
end
end
me.log("ACTION: done post craft for "..stack:get_name(), "error")
end
local net,cpos = me.get_connected_network(dat.ipos)
me.later(net, cpos, action, next_time + action_time_step)
end
me.log("ACTION: main done for "..stack:get_name(), "error")
end
local net,cpos = me.get_connected_network(dat.ipos)
-- queue main action for later
me.log("LATER: main action for "..stack:get_name().." in "..next_time.." seconds", "error")
me.later(net, cpos, main_action, next_time)
-- The step time is the prep time and the main_action_time
local step_time = next_time - time + main_action_time
return true, step_time
end
-- time is absolute, starting from 0 from the front of a craft or
-- non-zero if a previous craft was running.
function me.later(net, cpos, action, time)
if not net.pending then
net.pending = {}
net.pending.time = {}
end
local i = (net.pending.max_index or 0) + 1
net.pending.max_index = i
net.pending[i] = action
net.pending.time[i] = time
if not net.pending.index then
net.pending.index = 1
end
if i == 1 then
me.log("TIMER: starting timer to fire at "..time.." seconds", "error")
me.start_crafting(cpos, time+0.1)
else
-- me.log("TIMER: did not start timer for later, index "..i.." at time "..time, "error")
-- bubble sort the entry back to the right spot
while i > 1 do
-- me.log("TIME ds: "..i.." "..net.pending.time[i].." "..net.pending.time[i-1], "error")
if net.pending.time[i] < net.pending.time[i-1] then
-- if out of order, swap. This works as previously the list was sorted
local t = net.pending.time[i-1]
net.pending.time[i-1] = net.pending.time[i]
net.pending.time[i] = t
t = net.pending[i-1]
net.pending[i-1] = net.pending[i]
net.pending[i] = t
if i == 2 then
me.start_crafting(cpos, net.pending.time[1]+0.1)
end
else
break
end
i = i - 1
end
end
end
function me.autocraft(autocrafterCache, cpos, net, linv, inv, count)
local ostack = linv:get_stack("output", 1)
local name = ostack:get_name()
me.log("crafting "..name.." "..tostring(count), "error")
local stack = ItemStack(name)
local craft_count = ostack:get_count()
me.log("auto: craft_count "..craft_count.." count "..count, "error")
-- we craft a minimum of count, to the multiple of the crafting count
count = math.ceil(count/craft_count)
me.log("auto: count is now "..count, "error")
stack:set_count(count*craft_count)
me.log("auto: stack size is now "..stack:get_count(), "error")
me.log("auto: and build count is "..(count*craft_count), "error")
-- me.log("autocrafters: "..minetest.serialize(net.autocrafters), "error")
if not net.process then
-- rewalk the interfaces on the network to rebuild the machines.
net:reload_network()
end
if net.autocrafters[name] or net.process[name] then
me.log("using pipeworks autocrafter", "error")
local sink = function(stack)
local leftovers = me.insert_item(stack, net, inv, "main")
net:set_storage_space(true)
return leftovers
end
local start_time = me.autocraft_next_start(net)
local built, step_time = build(net, cpos, inv, name, count*craft_count, stack, sink, start_time)
if built then
me.log("crafting "..stack:get_count().." "..stack:get_name().." in "..step_time.." seconds", "error")
else
me.log("can't craft "..stack:get_count().." "..stack:get_name(), "error")
end
return
end
me.log("using microexpansion autocrafter", "error")
local consume = {}
for i = 1, 9 do
local inp = linv:get_stack("recipe", i)
if inp and inp:get_name() ~= "" then
consume[inp:get_name()] = (consume[inp:get_name()] or 0) + count*inp:get_count()
end
end
local replace = true
for name, count in pairs(consume) do
local stack = ItemStack(name)
if count >= math.pow(2,16) then
replace = false
break
end
-- Don't consume the last item by autocrafting
stack:set_count(count+1)
replace = replace and inv:contains_item("main", stack)
end
if replace then
for name, count in pairs(consume) do
local stack = ItemStack(name)
stack:set_count(count)
me.log("REMOVE: "..count.." "..stack:get_name(), "error")
if not inv:contains_item("main", stack) then
fixme1()
end
local ret = me.remove_item(net, inv, "main", stack)
if ret:get_count() ~= stack:get_count() then
me.log("AUTO: found "..(ret:get_count()).." "..(stack:get_name()).." but wanted "..stack:get_count(), "error")
-- fixme2()
end
end
local leftovers = me.insert_item(stack, net, inv, "main")
if leftovers:get_count() > 0 then
-- Ick, no room, just drop on the floor. Maybe player inventory?
minetest.add_item(cpos, leftovers)
end
net:set_storage_space(true)
-- deal with replacements
local hash = minetest.hash_node_position(cpos)
local craft = autocrafterCache[hash] or me.get_craft(cpos, linv, hash)
for i = 1, 9 do
if (craft.decremented_input.items[i]:get_count() ~= linv:get_stack("recipe", i):get_count()
or craft.decremented_input.items[i]:get_name() ~= linv:get_stack("recipe", i):get_name())
and not craft.decremented_input.items[i]:is_empty() then
local leftovers = me.insert_item(craft.decremented_input.items[i], net, inv, "main")
net:set_storage_space(true)
if leftovers:get_count() > 0 then
-- Ick, no room, just drop on the floor. Maybe player inventory?
minetest.add_item(cpos, leftovers)
end
end
if replace then
linv:set_stack("output", 1, craft.output.item)
else
linv:set_list("output", {})
end
end
end
end

@ -7,50 +7,50 @@ local network = me.network
-- [register node] Controller -- [register node] Controller
me.register_node("ctrl", { me.register_node("ctrl", {
description = "ME Controller", description = "ME Controller",
tiles = { tiles = {
"ctrl_sides", "ctrl_sides",
"ctrl_bottom", "ctrl_bottom",
"ctrl_sides", "ctrl_sides",
"ctrl_sides", "ctrl_sides",
"ctrl_sides", "ctrl_sides",
"ctrl_sides" "ctrl_sides"
}, },
recipe = { recipe = {
{ 1, { { 1, {
{"default:steel_ingot", "microexpansion:steel_infused_obsidian_ingot", "default:steel_ingot"}, {"default:steel_ingot", "microexpansion:steel_infused_obsidian_ingot", "default:steel_ingot"},
{"default:steel_ingot", "microexpansion:machine_casing", "default:steel_ingot"}, {"default:steel_ingot", "microexpansion:machine_casing", "default:steel_ingot"},
{"default:steel_ingot", "microexpansion:cable", "default:steel_ingot"}, {"default:steel_ingot", "microexpansion:cable", "default:steel_ingot"},
}, },
} }
}, },
drawtype = "nodebox", drawtype = "nodebox",
paramtype = "light", paramtype = "light",
node_box = { node_box = {
type = "fixed", type = "fixed",
fixed = { fixed = {
{-0.375, -0.375, -0.375, 0.375, 0.375, 0.375}, -- Core {-0.375, -0.375, -0.375, 0.375, 0.375, 0.375}, -- Core
{0.1875, -0.5, -0.5, 0.5, 0.5, -0.1875}, -- Corner1 {0.1875, -0.5, -0.5, 0.5, 0.5, -0.1875}, -- Corner1
{-0.5, -0.5, -0.5, -0.1875, 0.5, -0.1875}, -- Corner2 {-0.5, -0.5, -0.5, -0.1875, 0.5, -0.1875}, -- Corner2
{-0.5, -0.5, 0.1875, -0.1875, 0.5, 0.5}, -- Corner3 {-0.5, -0.5, 0.1875, -0.1875, 0.5, 0.5}, -- Corner3
{0.1875, -0.5, 0.1875, 0.5, 0.5, 0.5}, -- Corner4 {0.1875, -0.5, 0.1875, 0.5, 0.5, 0.5}, -- Corner4
{-0.5, -0.4375, -0.5, 0.5, -0.1875, 0.5}, -- Bottom {-0.5, -0.4375, -0.5, 0.5, -0.1875, 0.5}, -- Bottom
{-0.5, 0.1875, -0.5, 0.5, 0.5, -0.1875}, -- Top1 {-0.5, 0.1875, -0.5, 0.5, 0.5, -0.1875}, -- Top1
{0.1875, 0.1875, -0.5, 0.5, 0.5, 0.5}, -- Top2 {0.1875, 0.1875, -0.5, 0.5, 0.5, 0.5}, -- Top2
{-0.5, 0.1875, -0.5, -0.1875, 0.5, 0.5}, -- Top3 {-0.5, 0.1875, -0.5, -0.1875, 0.5, 0.5}, -- Top3
{-0.5, 0.1875, 0.1875, 0.5, 0.5, 0.5}, -- Top4 {-0.5, 0.1875, 0.1875, 0.5, 0.5, 0.5}, -- Top4
{-0.1875, -0.5, -0.1875, 0.1875, -0.25, 0.1875}, -- Bottom2 {-0.1875, -0.5, -0.1875, 0.1875, -0.25, 0.1875}, -- Bottom2
}, },
}, },
groups = { cracky = 1, me_connect = 1, }, groups = { cracky = 1, me_connect = 1, },
connect_sides = "nobottom", connect_sides = "nobottom",
me_update = function(pos,_,ev) me_update = function(pos,_,ev)
local cnet = me.get_network(pos) local net = me.get_network(pos)
if cnet == nil then if net == nil then
microexpansion.log("no network for ctrl at pos "..minetest.pos_to_string(pos),"error") me.log("no network for ctrl at pos "..minetest.pos_to_string(pos),"error")
return return
end end
cnet:update() net:update()
end, end,
on_construct = function(pos) on_construct = function(pos)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
@ -60,13 +60,13 @@ me.register_node("ctrl", {
meta:set_string("infotext", "Network Controller") meta:set_string("infotext", "Network Controller")
end, end,
after_place_node = function(pos, player) after_place_node = function(pos, player)
local name = player:get_player_name() local name = player:get_player_name()
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
meta:set_string("infotext", "Network Controller (owned by "..name..")") meta:set_string("infotext", "Network Controller (owned by "..name..")")
meta:set_string("owner", name) meta:set_string("owner", name)
end, end,
on_destruct = function(pos) on_destruct = function(pos)
local net,idx = me.get_network(pos) local net,idx = me.get_network(pos)
--disconnect all those who need the network --disconnect all those who need the network
me.send_event(pos,"disconnect",{net=net}) me.send_event(pos,"disconnect",{net=net})
@ -78,61 +78,93 @@ me.register_node("ctrl", {
end end
--disconnect all those that haven't realized the network is gone --disconnect all those that haven't realized the network is gone
me.send_event(pos,"disconnect") me.send_event(pos,"disconnect")
end, end,
after_destruct = function(pos) after_destruct = function(pos)
--disconnect all those that haven't realized the controller was disconnected --disconnect all those that haven't realized the controller was disconnected
me.send_event(pos,"disconnect") me.send_event(pos,"disconnect")
end, end,
machine = { machine = {
type = "controller", type = "controller",
}, },
on_timer = function(pos, elapsed)
::top::
me.log("TIMER: starting service", "error")
local net = me.get_network(pos)
if not net then return false end
if not net.pending then return false end
local i = net.pending.index
local action = net.pending[i]
-- me.log("TIMER: doing service", "error")
if not action then
net.pending = nil
return false
end
local prev_time = net.pending.time[i]
action(net)
net.pending[i] = nil
net.pending.time[i] = nil
net.pending.index = i + 1
if net.pending[i+1] then
local next_action_time = net.pending.time[i+1]
local step_time = next_action_time - prev_time
me.log("TIMER: starting next timer for "..step_time.." seconds", "error")
if step_time == 0 then
goto top
end
me.start_crafting(pos, step_time)
else
-- me.log("TIMER: ending service", "error")
net.pending = nil
end
return false
end,
}) })
-- [register node] Cable -- [register node] Cable
me.register_machine("cable", { me.register_machine("cable", {
description = "ME Cable", description = "ME Cable",
tiles = { tiles = {
"cable", "cable",
}, },
recipe = { recipe = {
{ 12, "shapeless", { { 12, "shapeless", {
"microexpansion:steel_infused_obsidian_ingot", "microexpansion:machine_casing" "microexpansion:steel_infused_obsidian_ingot", "microexpansion:machine_casing"
}, },
} }
}, },
drawtype = "nodebox", drawtype = "nodebox",
node_box = { node_box = {
type = "connected", type = "connected",
fixed = {-0.25, -0.25, -0.25, 0.25, 0.25, 0.25}, fixed = {-0.25, -0.25, -0.25, 0.25, 0.25, 0.25},
connect_top = {-0.25, -0.25, -0.25, 0.25, 0.5, 0.25}, -- y+ connect_top = {-0.25, -0.25, -0.25, 0.25, 0.5, 0.25}, -- y+
connect_bottom = {-0.25, -0.5, -0.25, 0.25, 0.25, 0.25}, -- y- connect_bottom = {-0.25, -0.5, -0.25, 0.25, 0.25, 0.25}, -- y-
connect_front = {-0.25, -0.25, -0.5, 0.25, 0.25, 0.25}, -- z- connect_front = {-0.25, -0.25, -0.5, 0.25, 0.25, 0.25}, -- z-
connect_back = {-0.25, -0.25, 0.25, 0.25, 0.25, 0.5 }, -- z+ connect_back = {-0.25, -0.25, 0.25, 0.25, 0.25, 0.5 }, -- z+
connect_left = {-0.5, -0.25, -0.25, 0.25, 0.25, 0.25}, -- x- connect_left = {-0.5, -0.25, -0.25, 0.25, 0.25, 0.25}, -- x-
connect_right = {-0.25, -0.25, -0.25, 0.5, 0.25, 0.25}, -- x+ connect_right = {-0.25, -0.25, -0.25, 0.5, 0.25, 0.25}, -- x+
}, },
paramtype = "light", paramtype = "light",
groups = { crumbly = 1, }, groups = { crumbly = 1, },
--TODO: move these functions into the registration --TODO: move these functions into the registration
on_construct = function(pos) on_construct = function(pos)
me.send_event(pos,"connect") me.send_event(pos,"connect")
end, end,
after_destruct = function(pos) after_destruct = function(pos)
me.send_event(pos,"disconnect") me.send_event(pos,"disconnect")
end, end,
me_update = function(pos,_,ev) me_update = function(pos,_,ev)
if ev then if ev then
if ev.type ~= "disconnect" then return end if ev.type ~= "disconnect" then return end
end end
--maybe this shouldn't be called on every update --maybe this shouldn't be called on every update
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
if me.get_connected_network(pos) then if me.get_connected_network(pos) then
meta:set_string("infotext", "Network connected") meta:set_string("infotext", "Network connected")
else else
meta:set_string("infotext", "No Network") meta:set_string("infotext", "No Network")
end end
end, end,
machine = { machine = {
type = "conductor", type = "conductor",
}, },
}) })

@ -1,7 +1,7 @@
local me = microexpansion local me = microexpansion
me.networks = {} me.networks = {}
local networks = me.networks local networks = me.networks
local path = microexpansion.get_module_path("network") local path = me.get_module_path("network")
--deprecated: use ItemStack(x) instead --deprecated: use ItemStack(x) instead
--[[ --[[
@ -30,98 +30,397 @@ local function split_stack_values(stack)
end end
--]] --]]
function me.insert_item(stack, inv, listname) local annotate_large_stack = function(stack, count)
if me.settings.huge_stacks == false then local description = minetest.registered_items[stack:get_name()]
return inv:add_item(listname, stack) if description then
end -- steel is an alias and won't be found in here, skip it
local to_insert = type(stack) == "userdata" and stack or ItemStack(stack) description = description.description
local found = false --stack:set_count(1)
for i = 0, inv:get_size(listname) do --This screw up everything, autocrafting, item removal and more
local inside = inv:get_stack(listname, i) --stack:get_meta():set_string("description", description.." "..count)
if inside:get_name() == to_insert:get_name() and inside:get_wear() == to_insert:get_wear() then stack:get_meta():set_string("description", "")
if inside:get_meta():equals(to_insert:get_meta()) then
local total_count = inside:get_count() + to_insert:get_count()
-- bigger item count is not possible, we only have unsigned 16 bit
if total_count <= math.pow(2,16) then
if not inside:set_count(total_count) then
microexpansion.log("adding items to stack in microexpansion network failed","error")
print("stack is now " .. inside:to_string())
end
inv:set_stack(listname, i, inside)
found = true
break;
end
end
end
end
if not found then
return inv:add_item(listname, stack)
end end
end end
function me.insert_item(stack, net, inv, listname, bias)
if stack == nil then
foobar()
return ItemStack(), 0
end
stack = type(stack) == "userdata" and stack or ItemStack(stack)
if stack:get_name() == "" then
-- foobar() -- TODO: can trip, ignore for now
return ItemStack(), 0
end
local slot
assert(net, not stack:is_empty())
local found = false
if not net.byname then
net.byname = {}
end
if not net.byname[listname] then
net.byname[listname] = {}
end
if not net.byname[listname][stack:get_name()] then
net.byname[listname][stack:get_name()] = {}
end
--if not net.byname[listname][stack:get_name()][stack:get_wear()] then
-- net.byname[listname][stack:get_name()][stack:get_wear()] = {}
--end
local meta = stack:get_meta()
-- slot = net.byname[listname][stack:get_name()][stack:get_wear()][meta]
-- assert(net, minetest.serialize(meta))
::was_empty::
slot = net.byname[listname][stack:get_name()][stack:get_wear()]
-- me.log("checking "..listname.." slot "..tostring(slot).." has "..stack:get_name().." in it", "error")
if not slot then
local ret = inv:add_item(listname, stack) -- TODO: fix to use set_stack, careful capacity
slot = inv:get_size(listname)
-- me.log(listname.." slot "..tostring(slot).." should now have "..stack:get_name().." in it", "error")
-- me.log(listname.." slot "..tostring(slot).." has "..inv:get_stack(listname, slot):get_name().." in it", "error")
if inv:get_stack(listname, slot):get_name() ~= stack:get_name() then
return ret, 0
--net:sync_main(inv)
-- TODO: infinite loop on full?
--goto was_empty
end
-- net.byname[listname][stack:get_name()][stack:get_wear()][meta] = slot
net.byname[listname][stack:get_name()][stack:get_wear()] = slot
-- me.log("byname is "..minetest.serialize(net.byname), "error")
if bias then
if not net.bias then
net.bias = {}
end
if not net.bias[listname] then
net.bias[listname] = {}
end
if not net.bias[listname][stack:get_name()] then
net.bias[listname][stack:get_name()] = 0
end
net.bias[listname][stack:get_name()] = net.bias[listname][stack:get_name()] + bias
me.log("LARGE: init insert_item bias "..bias.." for "..stack:get_name(), "error")
local mstack = inv:get_stack(listname, slot)
annotate_large_stack(mstack, mstack:get_count()+net.bias[listname][stack:get_name()])
inv:set_stack(listname, slot, mstack)
end
return ret, slot
end
local mstack = inv:get_stack(listname, slot)
-- me.log(listname.." slot "..tostring(slot).." has "..mstack:get_name().." in it", "error")
if mstack:is_empty() then
-- me.log("adding items to stack in microexpansion network was going to fail, fixing", "error")
net.byname[listname][stack:get_name()][stack:get_wear()] = nil
goto was_empty
end
-- me.log("init insert_item "..stack:get_name(), "error")
-- me.log("init insert_item "..mstack:get_name(), "error")
if mstack:get_name() ~= stack:get_name() then
-- me.log("adding items to stack in microexpansion network was going to fail, wrong item, fixing", "error")
net.byname[listname][stack:get_name()][stack:get_wear()] = nil
goto was_empty
end
local mbias = (net.bias and net.bias[listname] and net.bias[listname][stack:get_name()]) or 0
local total_count = mstack:get_count() + mbias + stack:get_count() + (bias or 0)
-- me.log("insert_item "..mstack:get_name().." "..tostring(total_count), "error")
-- bigger item count is not possible, we only have unsigned 16 bit
if total_count > math.pow(2,15) then
-- We handle overflows by storing the excess stores into a bias number for the slot.
-- This give us lua max int (2^48 or so) for the actual count.
if not net.bias then
net.bias = {}
end
if not net.bias[listname] then
net.bias[listname] = {}
end
if not net.bias[listname][stack:get_name()] then
net.bias[listname][stack:get_name()] = 0
end
net.bias[listname][stack:get_name()] = total_count - math.pow(2,15)
me.log("LARGE: overflow bias "..stack:get_count().." for "..stack:get_name(), "error")
total_count = math.pow(2,15)
end
local addition_real_loaned = total_count - mstack:get_count()
mstack:set_count(total_count)
inv:set_stack(listname, slot, mstack)
-- if there is one or more loans for the item, add stack:get_count()
-- + (bias or 0) to them and update those inventories, if they have room
local remaining = stack:get_count() + (bias or 0)
local loan_slot = net:find_loan(inv, stack)
if loan_slot then
me.loan.bump_loan(net, inv, loan_slot, remaining, addition_real_loaned)
end
return ItemStack(), slot
end
function me.add_capacity(ipos, count)
local int_meta = minetest.get_meta(ipos)
local prev = int_meta:get_int("capacity") or 0
int_meta:set_int("capacity", prev + count)
end
function me.remove_item(net, inv, listname, stack)
if not stack then return ItemStack() end
if stack == "" then return ItemStack() end
if type(stack) == string then
me.log("REMOVE: converting "..stack, "error")
stack = ItemStack(stack)
end
-- stack = ItemStack(stack):set_count(stack:get_count())
-- this can dump...
me.log("type is "..type(stack), "error")
-- me.log("serial is "..minetest.serialize(stack), "error")
if stack:get_count() == 0 then return ItemStack() end
me.log("me.remove_item "..listname, "error")
me.log("me.remove_item "..listname.." "..stack:get_count().." "..stack:get_name(), "error")
-- me.log("me.remove_item "..listname.." "..stack:get_wear(), "error")
if listname ~= "main" then
foobar()
return inv:remove_item(listname, stack)
end
if not net.byname[listname] or not net.byname[listname][stack:get_name()] then
return ItemStack()
end
local slot = net.byname[listname][stack:get_name()][stack:get_wear()]
if not slot then
me.log("wanted to remove "..stack:get_name().." from "..listname..", but didn't find anything", "error")
return ItemStack()
end
local mstack = inv:get_stack(listname, slot)
me.log("init remove item "..tostring(stack:get_count()).." "..stack:get_name(), "error")
if stack:get_name() ~= mstack:get_name()
or stack:get_wear() ~= mstack:get_wear()
or not stack:get_meta():equals(mstack:get_meta()) then
me.log("wanted to remove "..stack:get_name().." from "..listname..", but had "..mstack:get_name().." in slot "..slot, "error")
-- foobar()
net.byname[listname][stack:get_name()][stack:get_wear()] = nil
return ItemStack()
end
local mbias = 0
if net.bias and net.bias[listname] then
mbias = net.bias[listname][stack:get_name()] or 0
end
local on_loan = net.counts[stack:get_name()]
local remaining = mstack:get_count() + mbias - stack:get_count()
me.log("init me.remove_item has "..(on_loan or 0).." on loan and "..remaining.." remaining", "error")
if on_loan and remaining < on_loan then
me.log("init me.remove_item has "..on_loan.." on loan and "..remaining.." remaining", "error")
local loan_slot = net:find_loan(inv, stack)
-- find_loan can update main, refetch
mstack = inv:get_stack(listname, slot)
if not loan_slot then
-- someone updated the real inventories, there are no more of these
return ItemStack()
end
local lstack = me.loan.get_stack(net, inv, loan_slot)
if lstack:is_empty() then
return ItemStack()
end
local ref = me.network.get_ref(lstack)
local real_count = nil
if ref.drawer then
local c = drawers.drawer_get_content(ref.pos, ref.slot)
c.count = math.max(c.count-1,0) -- Poor man's locking
local count = math.min(stack:get_count(), c.count)
me.log("init removing "..count.." items from drawer", "error")
stack:set_count(count)
local take = stack
drawers.drawer_take_large_item(ref.pos, take)
c.count = c.count - count
real_count = c.count
if real_count > math.pow(2,15) then
real_count = math.pow(2,15)
end
-- me.log("CAPACITY: drawer "..-count, "error")
-- me.add_capacity(ref.ipos, -count)
else
local rinv = minetest.get_meta(ref.pos):get_inventory()
local real_stack = rinv:get_stack(ref.invname, ref.slot)
local count = math.min(stack:get_count(), real_stack:get_count())
me.log("init removing "..count.." items from chest", "error")
stack:set_count(count)
real_count = real_stack:get_count()-count
real_stack:set_count(real_count)
rinv:set_stack(ref.invname, ref.slot, real_stack)
-- me.log("CAPACITY: chest "..-count, "error")
-- me.add_capacity(ref.ipos, -count)
end
me.log("init now down to "..real_count.." items", "error")
local lcount = lstack:get_count() -- it was mc 1 excess 0 lcount 1 rc 1
local excess = real_count - lcount -- it was mc 1 excess -1 lcount 2 rc 1
me.log("it was "..mstack:get_count().." "..excess.." "..lcount.." "..real_count, "error") -- fails with: 1 -1
if real_count == 0 then
me.log("init me.remove_item before remove_loan chest", "error")
net:remove_loan(ref.pos, inv, lstack, loan_slot, ref)
elseif excess ~= 0 then
-- update loan and main with new excess
lstack:set_count(lcount + excess)
me.log("LOAN: me.remove_item loan to "..lstack:get_count(), "error")
me.loan.set_stack(net, inv, loan_slot, lstack)
mstack:set_count(mstack:get_count() + excess)
inv:set_stack(listname, slot, mstack)
if mstack:is_empty() then
me.maybemove(net, inv, listname, slot, stack)
end
-- TODO: Think this is wrong, only adjust by excess no stack:get_count()
--net.counts[lstack:get_name()] = net.counts[lstack:get_name()] - stack:get_count()
--me.add_capacity(ref.ipos, -stack:get_count())
me.log("CAPACITY: is "..excess, "error")
net.counts[lstack:get_name()] = net.counts[lstack:get_name()] + excess
me.add_capacity(ref.ipos, excess)
end
return stack
end
if remaining > 0 then
if remaining > math.pow(2,15) then
if not net.bias then
net.bias = {}
end
if not net.bias[listname] then
net.bias[listname] = {}
end
me.log("LARGE: total count "..remaining.." for "..stack:get_name(), "error")
annotate_large_stack(mstack, remaining)
net.bias[listname][stack:get_name()] = remaining - math.pow(2,15)
remaining = math.pow(2,15)
end
mstack:set_count(remaining)
inv:set_stack(listname, slot, mstack)
return stack
end
if remaining < 0 then
me.log("init wow, missing "..tostring(-remaining).." "..stack:get_name().." during removal, taking less", "error")
-- take fewer
stack:set_count(mstack:get_count() + mbias)
remaining = 0
end
mstack:set_count(remaining)
inv:set_stack(listname, slot, mstack)
me.log("init me.remove_item bottom before", "error")
me.maybemove(net, inv, listname, slot, stack)
me.log("init me.remove_item bottom after", "error")
return stack
end
function me.maybemove(net, inv, listname, slot, stack)
if stack == nil then
-- me.log("init nil stack", "error")
return
end
if net == nil then
-- me.log("init nil net", "error")
return
end
if inv == nil then
-- me.log("init nil inv", "error")
return
end
-- me.log("init maybemove "..stack:get_name(), "error")
if not net.byname or not net.byname[listname] or not net.byname[listname][stack:get_name()] then
me.log("byname on "..listname.." is already nil", "error")
else
-- slot has been completely removed, deindex it
net.byname[listname][stack:get_name()][stack:get_wear()] = nil
end
if net.bias and net.bias[listname] then
net.bias[listname][stack:get_name()] = nil
end
-- Move the last or the second to the last if the last is empty,
-- back to the hole left by the removed items
local main_size = inv:get_size(listname)
-- me.log("CALC main_size"..main_size.." slot "..slot, "error")
if slot < main_size and main_size > 1 then
local orig_slot = main_size
local mstack = inv:get_stack(listname, orig_slot)
-- me.log("CALC count "..mstack:get_count().." orig_slot-1 "..orig_slot-1, "error")
if mstack:is_empty() and slot < orig_slot-1 and orig_slot-1 > 1 then
orig_slot = orig_slot-1
mstack = inv:get_stack(listname, orig_slot)
end
if not mstack:is_empty() then
inv:set_stack(listname, orig_slot, ItemStack())
inv:set_stack(listname, slot, mstack)
-- [meta]
-- me.log("CALC not empty, old "..stack:get_name().." new "..mstack:get_name(), "error")
if net.byname and net.byname[listname] and net.byname[listname][stack:get_name()] then
net.byname[listname][stack:get_name()][stack:get_wear()] = nil
end
net.byname[listname][mstack:get_name()][mstack:get_wear()] = slot
else
inv:set_stack(listname, slot, mstack)
-- me.log("CALC empty, old "..stack:get_name(), "error")
if net.byname and net.byname[listname] and net.byname[listname][stack:get_name()] then
net.byname[listname][stack:get_name()][stack:get_wear()] = nil
end
end
end
inv:set_size(listname, main_size-1)
end
dofile(path.."/loan.lua") -- Loan Management
dofile(path.."/network.lua") -- Network Management dofile(path.."/network.lua") -- Network Management
dofile(path.."/autocraft.lua") -- Autocrafting
-- generate iterator to find all connected nodes -- generate iterator to find all connected nodes
function me.connected_nodes(start_pos,include_ctrl) function me.connected_nodes(start_pos,include_ctrl)
-- nodes to be checked -- nodes to be checked
local open_list = {{pos = start_pos}} local open_list = {{pos = start_pos}}
-- nodes that were checked -- nodes that were checked
local closed_set = {} local closed_set = {}
-- local connected nodes function to reduce table lookups -- local connected nodes function to reduce table lookups
local adjacent_connected_nodes = me.network.adjacent_connected_nodes local adjacent_connected_nodes = me.network.adjacent_connected_nodes
-- return the generated iterator -- return the generated iterator
return function () return function ()
-- start looking for next pos -- start looking for next pos
local open = false local open = false
-- pos to be checked -- pos to be checked
local current local current
-- find next unclosed -- find next unclosed
while not open do while not open do
-- get unchecked pos -- get unchecked pos
current = table.remove(open_list) current = table.remove(open_list)
-- none are left -- none are left
if current == nil then return end if current == nil then return end
-- assume it's open -- assume it's open
open = true open = true
-- check the closed positions -- check the closed positions
for _,closed in pairs(closed_set) do for _,closed in pairs(closed_set) do
-- if current is unclosed -- if current is unclosed
if vector.equals(closed,current.pos) then if vector.equals(closed,current.pos) then
--found one was closed --found one was closed
open = false open = false
end
end
end
-- get all connected nodes
local nodes = adjacent_connected_nodes(current.pos,include_ctrl)
-- iterate through them
for _,n in pairs(nodes) do
-- mark position to be checked
table.insert(open_list,n)
end
-- add this one to the closed set
table.insert(closed_set,current.pos)
-- return the one to be checked
return current.pos,current.name
end end
end
end
-- get all connected nodes
local nodes = adjacent_connected_nodes(current.pos,include_ctrl)
-- iterate through them
for _,n in pairs(nodes) do
-- mark position to be checked
table.insert(open_list,n)
end
-- add this one to the closed set
table.insert(closed_set,current.pos)
-- return the one to be checked
return current.pos,current.name
end
end end
-- get network connected to position -- get network connected to position
function me.get_connected_network(start_pos) function me.get_connected_network(start_pos)
for npos,nn in me.connected_nodes(start_pos,true) do for npos,nn in me.connected_nodes(start_pos,true) do
if nn == "microexpansion:ctrl" then if nn == "microexpansion:ctrl" then
local network = me.get_network(npos) local net = me.get_network(npos)
if network then if net then
return network,npos return net,npos
end end
end end
end end
end end
function me.update_connected_machines(start_pos,event,include_start) function me.update_connected_machines(start_pos,event,include_start)
microexpansion.log("updating connected machines","action") me.log("updating connected machines", "action")
local ev = event or {type = "n/a"} local ev = event or {type = "n/a"}
local sn = microexpansion.get_node(start_pos) local sn = me.get_node(start_pos)
local sd = minetest.registered_nodes[sn.name] local sd = minetest.registered_nodes[sn.name]
local sm = sd.machine or {} local sm = sd.machine or {}
ev.origin = { ev.origin = {
@ -134,7 +433,7 @@ function me.update_connected_machines(start_pos,event,include_start)
if include_start or not vector.equals(npos,start_pos) then if include_start or not vector.equals(npos,start_pos) then
me.update_node(npos,ev) me.update_node(npos,ev)
end end
end end
end end
function me.send_event(spos,type,data) function me.send_event(spos,type,data)
@ -148,31 +447,31 @@ function me.send_event(spos,type,data)
end end
function me.get_network(pos) function me.get_network(pos)
for i,net in pairs(networks) do for i,net in pairs(networks) do
if net.controller_pos then if net.controller_pos then
if vector.equals(pos, net.controller_pos) then if vector.equals(pos, net.controller_pos) then
return net,i return net,i
end end
end end
end end
end end
dofile(path.."/ctrl.lua") -- Controller/wires dofile(path.."/ctrl.lua") -- Controller/wires
-- load networks -- load networks
function me.load() function me.load()
local f = io.open(me.worldpath.."/microexpansion_networks", "r") local f = io.open(me.worldpath.."/microexpansion_networks", "r")
if f then if f then
local res = minetest.deserialize(f:read("*all")) local res = minetest.deserialize(f:read("*all"))
f:close() f:close()
if type(res) == "table" then if type(res) == "table" then
for _,n in pairs(res) do for _,n in pairs(res) do
local net = me.network.new(n) local net = me.network.new(n)
net:load() net:load()
table.insert(me.networks,net) table.insert(me.networks,net)
end end
end end
end end
end end
-- load now -- load now
@ -181,12 +480,17 @@ me.load()
-- save networks -- save networks
function me.save() function me.save()
local data = {} local data = {}
for _,v in pairs(me.networks) do for _,net in pairs(me.networks) do
table.insert(data,v:serialize()) -- We rebuild this data by walking as they contain non-serializable content.
-- All other data is serialized and survives.
net.autocrafters = nil
net.autocrafters_by_pos = nil
net.process = nil
table.insert(data,net:serialize())
end end
local f = io.open(me.worldpath.."/microexpansion_networks", "w") local f = io.open(me.worldpath.."/microexpansion_networks", "w")
f:write(minetest.serialize(data)) f:write(minetest.serialize(data))
f:close() f:close()
end end
-- save on server shutdown -- save on server shutdown

108
modules/network/loan.lua Normal file

@ -0,0 +1,108 @@
local me = microexpansion
local loan = {
}
me.loan = loan
function me.loan.get_stack(net, inv, loan_slot)
local lstack = inv:get_stack("loan", loan_slot)
return lstack
end
function me.loan.set_stack(net, inv, loan_slot, lstack)
inv:set_stack("loan", loan_slot, lstack)
end
function me.loan.get_size(net, inv)
return inv:get_size("loan")
end
function me.loan.set_size(net, inv, size)
return inv:set_size("loan", size)
end
function me.loan.bump_loan(net, inv, loan_slot, remaining, addition_real_loaned)
local lstack = me.loan.get_stack(net, inv, loan_slot)
local ref
local real_count
if lstack:is_empty() then
-- TODO: should never happen, verify and remove
return
end
ref = me.network.get_ref(lstack)
real_count = nil
local prev_lstack_count = lstack:get_count()
if ref.drawer then
local stack = ItemStack(lstack)
-- the drawer api only allow up to 2^16-1, ask them for a better api, 2^48 max
local count = math.pow(2,16)-1
-- ensure that lstack:get_count() + spare below is at most 2^16-1
count = math.min(count, math.pow(2,16)-1 - lstack:get_count())
-- ensure at most 2^16-1 as the stack api doesn't allow more
count = math.min(count, remaining)
-- if the loan size is already maximal, then we can't update the loan any
if count > 0 then
stack:set_count(count)
-- bump up the actual inventory, spare is how many fit
local excess = drawers.drawer_insert_object(ref.pos, stack, ref.slot)
local spare = count - excess:get_count()
-- bump loan by spare
lstack:set_count(lstack:get_count() + spare)
me.log("LOAN: bump_loan to "..lstack:get_count(), "error")
me.loan.set_stack(net, inv, loan_slot, lstack)
net.counts[lstack:get_name()] = net.counts[lstack:get_name()] + spare
me.log("COUNT: loan now to "..net.counts[lstack:get_name()].." "..lstack:get_name()..", "..spare.." more", "error")
me.log("LOAN: adril "..addition_real_loaned.." loan "..prev_lstack_count.." and adjustment(spare) "..spare, "error")
addition_real_loaned = math.min(addition_real_loaned, spare)
me.add_capacity(ref.ipos, addition_real_loaned)
-- reduce remaining by spare
remaining = remaining - spare
end
else
local rinv = minetest.get_meta(ref.pos):get_inventory()
local real_stack = rinv:get_stack(ref.invname, ref.slot)
local max = real_stack:get_stack_max()
if real_stack:get_count() < max then
local spare = max - real_stack:get_count()
spare = math.min(spare, remaining)
-- me.log("bumping "..lstack:get_name().." by "..spare, "error")
-- bump up the actual inventory by spare
real_stack:set_count(real_stack:get_count() + spare)
rinv:set_stack(ref.invname, ref.slot, real_stack)
-- bump loan by spare
lstack:set_count(lstack:get_count() + spare)
-- me.log("bumping "..lstack:get_name().." to "..lstack:get_count(), "error")
me.log("LOAN: bump_loan to "..lstack:get_count(), "error")
me.loan.set_stack(net, inv, loan_slot, lstack)
-- me.log("COUNTS: "..minetest.serialize(net.counts), "error")
net.counts[lstack:get_name()] = net.counts[lstack:get_name()] + spare
me.log("COUNT: bump_loan loan now to "..net.counts[lstack:get_name()].." "..lstack:get_name()..", "..spare.." more", "error")
me.log("LOAN: adril "..addition_real_loaned.." loan "..prev_lstack_count.." and adjustment(spare) "..spare, "error")
addition_real_loaned = math.min(addition_real_loaned, spare)
me.add_capacity(ref.ipos, addition_real_loaned)
-- reduce remaining by spare
remaining = remaining - spare
end
end
-- This code is misguided. We've already added them as real, we can
-- only add them to a loan if those items are added to that
-- inventory being loaded. This is only possible if there is room.
if false and remaining > 0 then
if not net.bias then
net.bias = {}
end
if not net.bias["loan"] then
net.bias["loan"] = {}
end
if not net.bias["loan"][loan_slot] then
net.bias["loan"][loan_slot] = 0
end
net.bias["loan"][loan_slot] = net.bias["loan"][loan_slot] + remaining
me.log("LARGE: bump_loan bias is "..net.bias["loan"][loan_slot].." "..lstack:get_name()..", "..remaining.." more", "error")
end
end

File diff suppressed because it is too large Load Diff

@ -48,7 +48,7 @@ function microexpansion.int_to_pagenum(int)
end end
-- [function] Move items from inv to inv -- [function] Move items from inv to inv
function microexpansion.move_inv(inv1, inv2, max) function microexpansion.move_inv(net, inv1, inv2, max)
if max <= 0 then return end if max <= 0 then return end
local finv, tinv = inv1.inv, inv2.inv local finv, tinv = inv1.inv, inv2.inv
local fname, tname = inv1.name, inv2.name local fname, tname = inv1.name, inv2.name
@ -66,14 +66,14 @@ function microexpansion.move_inv(inv1, inv2, max)
end end
if tinv and tinv:room_for_item(tname, v) then if tinv and tinv:room_for_item(tname, v) then
if huge then if huge then
microexpansion.insert_item(v, tinv, tname) microexpansion.insert_item(v, net, tinv, tname)
finv:remove_item(fname, v) finv:remove_item(fname, v)
else else
local leftover = tinv:add_item(tname, v) local leftovers = tinv:add_item(tname, v)
finv:remove_item(fname, v) finv:remove_item(fname, v)
if leftover and not(leftover:is_empty()) then if leftovers and not leftovers:is_empty() then
microexpansion.log("leftover items when transferring inventory","warning") microexpansion.log("leftover items when transferring inventory", "warning")
finv:add_item(fname, leftover) finv:add_item(fname, leftovers)
end end
end end
inserted = inserted + v:get_count() inserted = inserted + v:get_count()

@ -0,0 +1,756 @@
-- crafting terminal
-- microexpansion/cterminal.lua
-- TODO: Bugs, can't craft sticks, oil extract by using the
-- output. Does work when updating the recipe. Groups are hanky. We
-- only use the last recipe registered. Would be nice to be able to
-- cycle trough them. We handle this by merely requiring the user to
-- select the input recipe they want.
-- TODO: Bugs in original, if you remove controller, this wipes all drives
-- Spacing sucks.
-- The search list doesn't update when main updates or when autocrafting updates.
local autocrafterCache = {} -- caches some recipe data to avoid to call the slow function minetest.get_craft_result() every second
local me = microexpansion
local pipeworks_enabled = minetest.get_modpath("pipeworks") and true or false
-- [me chest] Get formspec
local function chest_formspec(pos, start_id, listname, page_max, q)
local list
local page_number = ""
local buttons = ""
local query = q or ""
local net,cpos = me.get_connected_network(pos)
if cpos then
local inv = net:get_inventory()
if listname and (inv:get_size(listname) > 0 or net:get_item_capacity() > 0) then
local ctrlinvname = net:get_inventory_name()
if listname == "main" then
list = "list[detached:"..ctrlinvname..";"
.. listname .. ";0,0.3;8,4;" .. (start_id - 1) .. "]"
else
list = "list[context;" .. listname .. ";0,0.3;8,4;" .. (start_id - 1) .. "]"
end
if minetest.get_modpath("i3") then
list = list .. [[
list[current_player;main;0,8.5;9,4;]
]]
else
list = list .. [[
list[current_player;main;0,8.5;8,1;]
list[current_player;main;0,9.73;8,3;8]
]]
end
list = list .. [[
list[context;recipe;0.22,5.22;3,3;]
list[context;output;4,6.22;1,1;]
]]
list = list .. [[
listring[current_player;main]
listring[detached:]]..ctrlinvname..[[;main]
listring[current_player;main]
listring[context;recipe]
listring[current_player;main]
listring[context;output]
listring[current_player;main]
]]
buttons = [[
button[3.56,4.35;1.8,0.9;tochest;To Drive]
tooltip[tochest;Move everything from your inventory to the ME network.]
button[5.4,4.35;0.8,0.9;prev;<]
button[7.25,4.35;0.8,0.9;next;>]
tooltip[prev;Previous]
tooltip[next;Next]
field[0.29,4.6;2.2,1;filter;;]]..query..[[]
button[2.1,4.5;0.8,0.5;search;?]
button[2.75,4.5;0.8,0.5;clear;X]
tooltip[search;Search]
tooltip[clear;Reset]
field[6,5.42;2,1;autocraft;;1]
tooltip[autocraft;Number of items to Craft]
]]
else
list = "label[3,2;" .. minetest.colorize("red", "No connected storage!") .. "]"
end
else
list = "label[3,2;" .. minetest.colorize("red", "No connected network!") .. "]"
end
if page_max then
page_number = "label[6.15,4.5;" .. math.floor((start_id / 32)) + 1 ..
"/" .. page_max .."]"
end
return [[
size[9,12.5]
]]..
microexpansion.gui_bg ..
microexpansion.gui_slots ..
list ..
[[
label[0,-0.23;ME Crafting Terminal]
field_close_on_enter[filter;false]
field_close_on_enter[autocraft;false]
]]..
page_number ..
buttons
end
local function update_chest(pos,_,ev)
--for now all events matter
local net = me.get_connected_network(pos)
local meta = minetest.get_meta(pos)
if net == nil then
meta:set_int("page", 1)
meta:set_string("formspec", chest_formspec(pos, 1))
return
end
local size = net:get_item_capacity()
local page_max = me.int_to_pagenum(size) + 1
meta:set_string("inv_name", "main")
meta:set_string("formspec", chest_formspec(pos, 1, "main", page_max))
end
-- From pipeworks/autocrafter.lua
local function count_index(invlist)
local index = {}
for _, stack in pairs(invlist) do
if not stack:is_empty() then
local stack_name = stack:get_name()
index[stack_name] = (index[stack_name] or 0) + stack:get_count()
end
end
return index
end
-- From pipeworks/autocrafter.lua
function me.get_craft(pos, inventory, hash)
local hash = hash or minetest.hash_node_position(pos)
local craft = autocrafterCache[hash]
if not craft then
local recipe = inventory:get_list("recipe")
for i = 1, 9 do
if recipe[i]:get_count() > 1 then
recipe[i] = ItemStack(recipe[i]:get_name())
end
end
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
return craft
end
-- From pipeworks/autocrafter.lua
-- note, that this function assumes allready being updated to virtual items
-- and doesn't handle recipes with stacksizes > 1
local function after_recipe_change(pos, inventory)
local meta = minetest.get_meta(pos)
-- if we emptied the grid, there's no point in keeping it running or cached
if inventory:is_empty("recipe") then
autocrafterCache[minetest.hash_node_position(pos)] = nil
inventory:set_stack("output", 1, "")
return
end
local recipe = inventory:get_list("recipe")
local hash = minetest.hash_node_position(pos)
local craft = autocrafterCache[hash]
if craft then
-- check if it changed
local cached_recipe = craft.recipe
for i = 1, 9 do
if recipe[i]:get_name() ~= cached_recipe[i]:get_name() then
autocrafterCache[hash] = nil -- invalidate recipe
craft = nil
break
end
end
end
craft = craft or me.get_craft(pos, inventory, hash)
local output_item = craft.output.item
inventory:set_stack("output", 1, output_item)
end
-- From pipeworks/autocrafter.lua
-- clean out unknown items and groups, which would be handled like unknown items in the crafting grid
-- if minetest supports query by group one day, this might replace them
-- with a canonical version instead
local function normalize(item_list)
for i = 1, #item_list do
local name = item_list[i]
if not minetest.registered_items[name] then
item_list[i] = ""
if name == "group:stick" then
item_list[i] = "default:stick"
elseif name == "group:glass" then
item_list[i] = "default:glass"
elseif name == "group:wood" then
name = "moretrees:oak_planks"
if minetest.registered_items[name] then
item_list[i] = name
else
item_list[i] = "default:wood"
end
elseif name == "group:wood" then
item_list[i] = "moretrees:oak_trunk"
elseif name == "group:wool" then
item_list[i] = "wool:white"
elseif name == "group:sand" then
item_list[i] = "default:sand"
elseif name == "group:stone" then
item_list[i] = "default:cobble"
elseif name == "group:leaves" then
name = "moretrees:sequoia_leaves"
if minetest.registered_items[name] then
item_list[i] = name
else
item_list[i] = "default:leaves"
end
elseif name == "group:coal" then
item_list[i] = "default:coal"
elseif name == "group:tree" then
item_list[i] = "default:tree"
end
end
end
return item_list
end
-- 0 to 34
function me.uranium_dust(p)
return "technic:uranium"..(p == 7 and "" or p).."_dust"
end
for pa = 0, 34 do
-- uranium_dust(pa)
-- me.uranium_dust(pa-1).." 2"
-- No, this would require a billion uranium to do this. :-(
-- Make a uranium centrifuge controller and have it be smart.
--me.register_output_by_typename("separating", "")
end
me.output_by_typename = {
-- aka me.register_output_by_typename("cooking", "default:stone")
["cooking"] = { "default:stone" }
}
-- Used to register what machine types (typename) produce which outputs.
-- Used to figure out what machine to use to create the given output.
-- If multiple outputs are produced, only use the main output, not the
-- incidental output.
function me.register_output_by_typename(typename, output)
if not me.output_by_typename[typename] then
me.output_by_typename[typename] = {}
end
table.insert(me.output_by_typename[typename], output)
end
function me.register_output_to_inputs(output, inputs)
me.log("REG: output "..output.." from inputs "..dump(inputs))
me.map_output_to_inputs[output] = inputs
end
me.map_output_to_inputs = {
-- furnace ("cooking")
["default:stone"] = { ItemStack("default:cobble") },
}
function me.find_by_output(name)
-- TODO: we'd love to be able to look this stuff up in the recipes.
-- technic.recipes["technic:doped_silicon_wafer"].alloy.recipes[4].input[1]
return me.map_output_to_inputs[name]
end
function me.register_inventory(name, func)
-- me.log("INVENTORY: registering "..name, "error")
if not me.registered_inventory then
me.registered_inventory = {}
end
me.registered_inventory[name] = func
end
-- Allow any type of machine process to be registered. For example,
-- "alloy" for an alloy furnace for example. These must be done
-- before specific me.register_inventory calls, as that one needs to
-- override this call.
function me.register_typename(name, typename)
me.block_to_typename_map[name] = typename
me.register_inventory(name, function() end)
end
me.block_to_typename_map = {
}
-- default wiring
me.register_typename("default:furnace", "cooking")
function me.get_recipe(typename, inputs)
return technic.get_recipe(typename, inputs)
end
-- TODO: Removing an output when the recipe is empty that is in
-- net.autocrafters should not be allowed as the output in that case
-- is virtual. It can be removed if rinv:"output" has the item iff
-- that item is removed from the autocrafter's output.
local function on_output_change(pos, linv, stack)
local name = stack:get_name()
-- me.log("PROCESS: "..name.." was found0", "error")
local net = me.get_connected_network(pos)
local inv = net:get_inventory()
local has_enough = true
local function clear_recipe()
local has_enough = true
for i = 1, 9 do
local prev = linv:get_stack("recipe", i)
if prev and prev:get_name() ~= "" and not inv:room_for_item("main", prev) then
-- full, no room to remove
has_enough = false
elseif prev and prev:get_name() ~= "" and me.insert_item(prev, net, inv, "main"):get_count() > 0 then
net:set_storage_space(true)
-- full, no room to remove
-- push into player inventory?
-- Don't have to worry about this happening until minetest is fully multithreaded
has_enough = false
else
net:set_storage_space(true)
linv:set_stack("recipe", i, ItemStack(""))
end
end
return has_enough
end
if not net.process then
-- rewalk the interfaces on the network to rebuild loans and machines.
net:reload_network()
end
if net and net.process[name] then
-- me.log("PROCESS: "..name.." was found1", "error")
has_enough = clear_recipe()
local pos,ipos = next(net.process[name])
if has_enough and pos then
-- me.log("PROCESS: "..name.." was found2", "error")
local inputs = me.find_by_output(name)
-- me.log("PROCESS: inputs are "..dump(inputs), "error")
local machine_name = minetest.get_node(pos).name
local typename = me.block_to_typename_map[machine_name]
local recip = typename and me.get_recipe(typename, inputs)
if recip and recip.output then
recip.intput = inputs
-- me.log("PROCESS: "..name.." was found for "..typename.." on a "..machine_name, "error")
-- freezer can produce two outputs, we only care about the first.
if recip.output[1] then
recip.output = recip.output[1]
end
stack = ItemStack(recip.output)
linv:set_stack("output", 1, stack)
-- me.log("PROCESS: and the output is "..minetest.serialize(recip.output), "error")
-- me.log("PROCESS: and the output is "..stack:get_name(), "error")
else
me.log("PROCESS: "..name.." was missing from recipe on a "..machine_name, "error")
linv:set_stack("output", 1, ItemStack())
end
end
return 0
elseif net and net.autocrafters[name] then
has_enough = clear_recipe()
if has_enough then
local pos,ipos = next(net.autocrafters[name])
if pos then
local rinv = minetest.get_meta(pos):get_inventory()
stack = ItemStack(rinv:get_stack("output", 1))
linv:set_stack("output", 1, stack)
else
-- me.log("pos in autocrafters was missing", "error")
linv:set_stack("output", 1, ItemStack())
end
else
linv:set_stack("output", 1, ItemStack())
end
return 0
end
local input = minetest.get_craft_recipe(name)
if not input.items or input.type ~= "normal" then return 0 end
local items, width = normalize(input.items), input.width
local item_idx, width_idx = 1, 1
for i = 1, 9 do
local prev = linv:get_stack("recipe", i)
if prev and prev:get_name() ~= "" and not inv:room_for_item("main", prev) then
-- full, no room to remove
has_enough = false
if width_idx <= width then
item_idx = item_idx + 1
end
elseif prev and prev:get_name() ~= "" and me.insert_item(prev, net, inv, "main"):get_count() > 0 then
net:set_storage_space(true)
-- full, no room to remove
-- push into player inventory?
-- Don't have to worry about this happening until minetest is fully multithreaded
has_enough = false
if width_idx <= width then
item_idx = item_idx + 1
end
elseif width_idx <= width then
net:set_storage_space(true)
if inv:contains_item("main", items[item_idx]) then
me.remove_item(net, inv, "main", ItemStack(items[item_idx]))
linv:set_stack("recipe", i, items[item_idx])
else
has_enough = false
linv:set_stack("recipe", i, ItemStack(""))
end
item_idx = item_idx + 1
else
linv:set_stack("recipe", i, ItemStack(""))
end
width_idx = (width_idx < 3) and (width_idx + 1) or 1
end
-- we'll set the output slot in after_recipe_change to the actual result of the new recipe
after_recipe_change(pos, linv)
return 0
end
-- [me cterminal] Register node
me.register_node("cterminal", {
description = "ME Crafting Terminal",
usedfor = "Can interact with storage cells in ME networks",
tiles = {
"chest_top",
"chest_top",
"chest_side",
"chest_side",
"chest_side",
"chest_front",
},
recipe = {
{ 1, {
{"microexpansion:term", "default:chest"},
},
}
},
is_ground_content = false,
groups = { cracky = 1, me_connect = 1, tubedevice = 1, tubedevice_receiver = 1 },
paramtype = "light",
paramtype2 = "facedir",
me_update = update_chest,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("formspec", chest_formspec(pos, 1))
meta:set_string("inv_name", "none")
meta:set_int("page", 1)
local own_inv = meta:get_inventory()
own_inv:set_size("src", 1)
own_inv:set_size("recipe", 3*3)
own_inv:set_size("output", 1)
local net = me.get_connected_network(pos)
me.send_event(pos,"connect",{net=net})
if net then
update_chest(pos)
end
end,
after_destruct = function(pos)
me.send_event(pos,"disconnect")
end,
can_dig = function(pos, player)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv:is_empty("recipe")
end,
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
me.log("Allow a move from "..from_list.." to "..to_list, "error")
local meta = minetest.get_meta(pos)
if to_list == "search" then
local net = me.get_connected_network(pos)
local linv = minetest.get_meta(pos):get_inventory()
local inv = net:get_inventory()
local stack = linv:get_stack(from_list, from_index)
stack:set_count(count)
-- local meta = minetest.get_meta(pos)
-- meta:set_string("infotext", "allow moving: "..stack:get_name())
-- TODO: Check capacity? Test.
me.insert_item(stack, net, inv, "main")
return count
end
if to_list == "recipe" and from_list == "search" then
local net = me.get_connected_network(pos)
local linv = minetest.get_meta(pos):get_inventory()
local inv = net:get_inventory()
local stack = linv:get_stack(from_list, from_index)
local meta = minetest.get_meta(pos)
count = math.min(count, stack:get_stack_max())
stack:set_count(count)
me.remove_item(net, inv, "main", stack)
return count
end
if to_list == "output" then
local linv = minetest.get_meta(pos):get_inventory()
local stack = linv:get_stack(from_list, from_index)
return on_output_change(pos, linv, stack)
end
return count
end,
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
-- This is used for removing items from "search", "recipe" and "output".
--me.log("Allow a take from "..listname, "error")
local count = stack:get_count()
if listname == "search" or listname == "recipe" then
count = math.min(count, stack:get_stack_max())
end
--[[if listname == "main" then
-- This should be unused, we don't have a local inventory called main.
local net = me.get_connected_network(pos)
local inv = net:get_inventory()
local ret = me.remove_item(net, inv, "main", stack)
me.log("REMOVE: after remove count is "..ret:get_count(), "error")
return ret:get_count()
end
]]
return count
end,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
if listname == "output" then
local linv = minetest.get_meta(pos):get_inventory()
return on_output_change(pos, linv, stack)
elseif listname == "search" or listname == "main" then
local net = me.get_connected_network(pos)
local inv = net:get_inventory()
-- TODO: Check full inv, should be fixed now, confirm.
local leftovers = me.insert_item(stack, net, inv, "main")
return stack:get_count() - leftovers:get_count()
end
return stack:get_count()
end,
on_metadata_inventory_put = function(pos, listname, _, stack)
if listname == "recipe" then
local linv = minetest.get_meta(pos):get_inventory()
after_recipe_change(pos, linv)
elseif listname == "search" or listname == "main" then
-- done above in allow, nothing left to do here
elseif listname == output then
-- done above
else
local net = me.get_connected_network(pos)
local inv = net:get_inventory()
local leftovers = me.insert_item(stack, net, inv, "main")
if leftovers:get_count() > 0 then
fixme()
end
net:set_storage_space(true)
end
end,
on_metadata_inventory_take = function(pos, listname, index, stack)
me.log("A taking of "..stack:get_name().." from "..listname, "error")
if listname == "output" then
local linv = minetest.get_meta(pos):get_inventory()
local num_left = linv:get_stack("output", 1):get_count()
-- We only need to consume the recipe if there are no more items
-- if num_left ~= 0 then return end
if num_left > 1 then return end
local net = me.get_connected_network(pos)
local inv = net:get_inventory()
local replace = true
-- This assumes that all inputs are only just 1 item, always true?
for i = 1, 9 do
local inp = linv:get_stack("recipe", i)
if inp and inp:get_name() ~= "" then
local consume = ItemStack(inp:get_name())
replace = replace and (inp:get_count() > 1 or inv:contains_item("main", consume))
end
end
for i = 1, 9 do
local inp = linv:get_stack("recipe", i)
if inp and inp:get_name() ~= "" then
if inp:get_count() == 1 then
if inv:contains_item("main", inp) then
local r = me.remove_item(net, inv, "main", inp)
if r:get_count() ~= 1 then
linv:set_stack("recipe", i, ItemStack(""))
replace = false
end
else
linv:set_stack("recipe", i, ItemStack(""))
replace = false
end
else
local stack_copy = ItemStack(inp)
stack_copy:set_count(inp:get_count()-1)
linv:set_stack("recipe", i, stack_copy)
end
end
end
-- deal with replacements
local hash = minetest.hash_node_position(pos)
local craft = autocrafterCache[hash] or me.get_craft(pos, linv, hash)
for i = 1, 9 do
if (craft.decremented_input.items[i]:get_count() ~= linv:get_stack("recipe", i):get_count()
or craft.decremented_input.items[i]:get_name() ~= linv:get_stack("recipe", i):get_name())
and not craft.decremented_input.items[i]:is_empty() then
local leftovers = me.insert_item(craft.decremented_input.items[i], net, inv, "main")
if leftovers:get_count() > 0 then
-- Ick, no room, just drop on the floor. Maybe player inventory?
minetest.add_item(pos, leftovers)
end
end
if replace then
linv:set_stack("output", 1, craft.output.item)
else
linv:set_list("output", {})
end
end
elseif listname == "recipe" then
local linv = minetest.get_meta(pos):get_inventory()
after_recipe_change(pos, linv)
elseif listname ~= "main" then
local net = me.get_connected_network(pos)
local inv = net:get_inventory()
me.remove_item(net, inv, "main", stack)
end
end,
on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
me.log("A move from "..from_list.." to "..to_list, "error")
if to_list == "recipe" or from_list == "recipe" then
local inv = minetest.get_meta(pos):get_inventory()
after_recipe_change(pos, inv)
end
end,
tube = {
can_insert = function(pos, _, stack) --pos, node, stack, direction
-- TODO: update to use capacity_cache?
local net = me.get_connected_network(pos)
local inv = net:get_inventory()
local max_slots = inv:get_size("main")
local max_items = net.capacity_cache
local slots, items = 0, 0
-- Get amount of items in drive
for i = 1, max_slots do
local dstack = inv:get_stack("main", i)
if dstack:get_name() ~= "" then
slots = slots + 1
local num = dstack:get_count()
if num == 0 then num = 1 end
items = items + num
end
end
items = items + stack:get_count()
return max_items > items
end,
insert_object = function(pos, _, stack)
local net = me.get_connected_network(pos)
local inv = net:get_inventory()
local leftovers = me.insert_item(stack, net, inv, "main")
net:set_storage_space(true)
return leftovers
end,
connect_sides = {left=1, right=1, front=1, back=1, top=1, bottom=1},
},
after_place_node = pipeworks_enabled and pipeworks.after_place,
after_dig_node = pipeworks_enabled and pipeworks.after_dig,
on_receive_fields = function(pos, _, fields, sender)
local net,cpos = me.get_connected_network(pos)
if net then
if cpos then
me.log("network and ctrl_pos","info")
else
me.log("network but no ctrl_pos","warning")
end
else
if cpos then
me.log("no network but ctrl_pos","warning")
else
me.log("no network and no ctrl_pos","info")
end
end
local meta = minetest.get_meta(pos)
local page = meta:get_int("page")
local inv_name = meta:get_string("inv_name")
local own_inv = meta:get_inventory()
local ctrl_inv
if cpos then
ctrl_inv = net:get_inventory()
else
me.log("no network connected","warning")
return
end
local inv
if inv_name == "main" then
inv = ctrl_inv
assert(inv,"no control inv")
else
inv = own_inv
assert(inv,"no own inv")
end
local page_max = math.floor(inv:get_size(inv_name) / 32) + 1
if inv_name == "none" then
return
end
if fields.next then
if page + 32 > inv:get_size(inv_name) then
return
end
meta:set_int("page", page + 32)
meta:set_string("formspec", chest_formspec(pos, page + 32, inv_name, page_max))
elseif fields.prev then
if page - 32 < 1 then
return
end
meta:set_int("page", page - 32)
meta:set_string("formspec", chest_formspec(pos, page - 32, inv_name, page_max))
elseif fields.search or fields.key_enter_field == "filter" then
own_inv:set_size("search", 0)
if fields.filter == "" then
meta:set_int("page", 1)
meta:set_string("inv_name", "main")
meta:set_string("formspec", chest_formspec(pos, 1, "main", page_max))
else
local tab = {}
for i = 1, ctrl_inv:get_size("main") do
local match = ctrl_inv:get_stack("main", i):get_name():find(fields.filter)
if match then
tab[#tab + 1] = ctrl_inv:get_stack("main", i)
end
end
own_inv:set_list("search", tab)
meta:set_int("page", 1)
meta:set_string("inv_name", "search")
meta:set_string("formspec", chest_formspec(pos, 1, "search", page_max, fields.filter))
end
elseif fields.clear then
own_inv:set_size("search", 0)
meta:set_int("page", 1)
meta:set_string("inv_name", "main")
meta:set_string("formspec", chest_formspec(pos, 1, "main", page_max))
elseif fields.tochest then
local pinv = minetest.get_inventory({type="player", name=sender:get_player_name()})
-- TODO: test and fix, net:set_storage_space(pinv:get_size("main"))
local space = net:get_item_capacity()
local contents = ctrl_inv:get_list("main") or {}
for _,s in pairs(contents) do
if not s:is_empty() then
space = space - s:get_count()
end
end
me.move_inv(net, { inv=pinv, name="main" }, { inv=ctrl_inv, name="main", huge=true }, space)
net:set_storage_space(true)
elseif fields.autocraft or fields.key_enter_field == "autocraft" then
if fields.autocraft ~= "" and tonumber(fields.autocraft) ~= nil then
local count = tonumber(fields.autocraft)
fields.autocraft = nil
if not own_inv:get_stack("output", 1):is_empty() and count < math.pow(2,16) then
me.autocraft(autocrafterCache, pos, net, own_inv, ctrl_inv, count)
end
end
end
end,
})

@ -0,0 +1,42 @@
--[[
Minetest Mod Storage Drawers - A Mod adding storage drawers
Copyright (C) 2017-2020 Linus Jahn <lnj@kaidan.im>
Copyright (C) 2016 Mango Tango <mtango688@gmail.com>
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
function drawers.drawer_take_large_item(pos, itemstack)
local drawer_visuals = drawers.drawer_visuals[core.hash_node_position(pos)]
if not drawer_visuals then
return ItemStack("")
end
for _, visual in pairs(drawer_visuals) do
if visual.itemName == itemstack:get_name() then
return visual:take_items(itemstack:get_count())
end
end
return ItemStack()
end

@ -0,0 +1,39 @@
-- Interoperability file for drawers support.
local me = microexpansion
me.register_inventory("drawers:wood1", function(net, ctrl_inv, int_meta, n, pos, doinventories)
if not doinventories then return end
local c = drawers.drawer_get_content(n.pos, "")
if c.name ~= "" and c.count > 1 then
-- A poor man's locking system will have us never remove the last item from a drawer.
c.count = c.count-1
local stack = ItemStack(c.name)
local bias = nil
if c.count > math.pow(2,15) then -- assumes me.settings.huge_stacks == true
bias = c.count - math.pow(2,15)
c.count = math.pow(2,15)
end
stack:set_count(c.count)
net:create_loan(stack, {pos=n.pos, drawer=true, slot="", ipos=pos}, ctrl_inv, int_meta, bias)
end
-- local rest = drawers.drawer_insert_object(n.pos, ItemStack("default:stone"), "")
-- meta:set_int("count", meta:get_int("count")+1)
-- drawers.remove_visuals(n.pos)
-- drawers.spawn_visuals(n.pos)
end)
me.register_inventory("drawers:wood2", function(net, ctrl_inv, int_meta, n, pos, doinventories)
if not doinventories then return end
-- local c = drawers.drawer_get_content(n.pos, "")
-- local rest = drawers.drawer_insert_object(n.pos, ItemStack("default:stone"), "")
end)
me.register_inventory("drawers:wood4", function(net, ctrl_inv, int_meta, n, pos, doinventories)
if not doinventories then return end
-- local c = drawers.drawer_get_content(n.pos, "")
-- local rest = drawers.drawer_insert_object(n.pos, ItemStack("default:stone"), "")
end)
me.register_inventory("drawers:controller", function(net, ctrl_inv, int_meta, n, pos)
-- inv:add_item("src", ItemStack("default:stone"))
end)

@ -39,7 +39,7 @@ local function get_drive_controller(pos)
for i,d in pairs(netdrives) do for i,d in pairs(netdrives) do
if d.dpos then if d.dpos then
if vector.equals(pos, d.dpos) then if vector.equals(pos, d.dpos) then
return d,i return d,i
end end
end end
end end
@ -51,10 +51,10 @@ local function set_drive_controller(dpos,setd,cpos,i)
local dt = netdrives[i] local dt = netdrives[i]
if dt then if dt then
if setd then if setd then
dt.dpos = dpos dt.dpos = dpos
end end
if cpos ~= nil then if cpos ~= nil then
dt.cpos = cpos dt.cpos = cpos
end end
else else
netdrives[i] = {dpos = dpos, cpos = cpos} netdrives[i] = {dpos = dpos, cpos = cpos}
@ -63,10 +63,10 @@ local function set_drive_controller(dpos,setd,cpos,i)
local dt = get_drive_controller(dpos) local dt = get_drive_controller(dpos)
if dt then if dt then
if setd then if setd then
dt.dpos = dpos dt.dpos = dpos
end end
if cpos ~= nil then if cpos ~= nil then
dt.cpos = cpos dt.cpos = cpos
end end
else else
table.insert(netdrives,{dpos = dpos, cpos = cpos}) table.insert(netdrives,{dpos = dpos, cpos = cpos})
@ -75,26 +75,26 @@ local function set_drive_controller(dpos,setd,cpos,i)
end end
local function write_to_cell(cell, items, item_count) local function write_to_cell(cell, items, item_count)
local size = microexpansion.get_cell_size(cell:get_name()) local size = microexpansion.get_cell_size(cell:get_name())
local item_meta = cell:get_meta() local item_meta = cell:get_meta()
--print(dump2(items,"cell_items")) --print(dump2(items,"cell_items"))
item_meta:set_string("items", minetest.serialize(items)) item_meta:set_string("items", minetest.serialize(items))
local base_desc = minetest.registered_craftitems[cell:get_name()].microexpansion.base_desc local base_desc = minetest.registered_craftitems[cell:get_name()].microexpansion.base_desc
-- Calculate Percentage -- Calculate Percentage
local percent = math.floor(item_count / size * 100) local percent = math.floor(item_count / size * 100)
-- Update description -- Update description
item_meta:set_string("description", base_desc.."\n".. item_meta:set_string("description", base_desc.."\n"..
minetest.colorize("grey", tostring(item_count).."/"..tostring(size).." Items ("..tostring(percent).."%)")) minetest.colorize("grey", tostring(item_count).."/"..tostring(size).." Items ("..tostring(percent).."%)"))
return cell return cell
end end
local function write_drive_cells(pos,network) local function write_drive_cells(pos, net)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local own_inv = meta:get_inventory() local own_inv = meta:get_inventory()
if network == nil then if net == nil then
return false return false
end end
local ctrl_inv = network:get_inventory() local ctrl_inv = net:get_inventory()
local cells = {} local cells = {}
for i = 1, own_inv:get_size("main") do for i = 1, own_inv:get_size("main") do
local cell = own_inv:get_stack("main", i) local cell = own_inv:get_stack("main", i)
@ -111,40 +111,83 @@ local function write_drive_cells(pos,network)
local items_in_cell_count = 0 local items_in_cell_count = 0
local cell_items = {} local cell_items = {}
net:update_counts()
if not net.counts then
net.counts = {}
end
for i = 1, ctrl_inv:get_size("main") do for i = 1, ctrl_inv:get_size("main") do
local stack_inside = ctrl_inv:get_stack("main", i) local stack = ctrl_inv:get_stack("main", i)
local item_string = stack_inside:to_string() local item_string = stack:to_string()
if item_string ~= "" then if item_string ~= "" then
item_string = item_string:split(" ") item_string = item_string:split(" ")
local item_count = stack_inside:get_count() lbias = (net.counts and net.counts[stack:get_name()]) or 0
local mbias = (net.bias and net.bias["main"] and net.bias["main"][stack:get_name()]) or 0
if mbias > lbias and lbias > 0 then
mbias = mbias - lbias
lbias = 0
net.bias["main"][stack:get_name()] = mbias
net.counts[stack:get_name()] = nil
elseif lbias > mbias and mbias > 0 then
lbias = lbias - mbias
mbias = 0
net.counts[stack:get_name()] = lbias
net.bias["main"][stack:get_name()] = nil
elseif mbias == lbias and mbias > 0 then
net.bias["main"][stack:get_name()] = nil
net.counts[stack:get_name()] = nil
end
local item_count = stack:get_count() + mbias
-- TODO: on_loan includes the >32k bias in it, so, therefore the
-- stack:get_count() + mbias should have it in it.
local on_loan = net.counts[stack:get_name()] or 0
item_count = item_count - on_loan
if item_count < 0 then
me.log("LOAN: drive "..item_count.." "..stack:get_name().." "..on_loan.." on loan", "error")
-- TODO: we need to update the count faster and we need to update counts from actual inventories
-- and not allow taking unless there is an actual item there, mostly done now
-- TODO: In theory this should be impossible now, but we have
-- bugs with cell removal and insert, see wow
me.log("wow, free items "..stack:get_name().." during drive write, "..tostring(-item_count).." extra, with a loan of "..on_loan, "error")
item_count = 0
if mbias > 0 then
net.bias["main"][stack:get_name()] = nil
end
end
if item_count > 1 and item_string[2] ~= tostring(item_count) then if item_count > 1 and item_string[2] ~= tostring(item_count) then
microexpansion.log("stack count differs from second field of the item string","warning") me.log("stack count differs from second field of the item string","warning")
end end
while item_count ~= 0 and cell_idx ~= nil do while item_count ~= 0 and cell_idx ~= nil do
--print(("stack to store: %q"):format(table.concat(item_string," "))) --print(("stack to store: %q"):format(table.concat(item_string," ")))
if size < items_in_cell_count + item_count then
local space = size - items_in_cell_count
item_string[2] = tostring(space)
table.insert(cell_items,table.concat(item_string," "))
items_in_cell_count = items_in_cell_count + space
own_inv:set_stack("main", cell_idx, write_to_cell(cells[cell_idx],cell_items,items_in_cell_count)) -- TODO: This should fail if we write 64k items onto a 64k
cell_idx = next(cells, cell_idx) -- drive (or larger). Fix by writting the number first, that's
if cell_idx == nil then -- the bias and then the name. This requires that no node in
--there may be other drives within the network -- the storage system starts with a number. Then in read,
microexpansion.log("too many items to store in drive","info") -- support that.
break
end if size < items_in_cell_count + item_count then
size = microexpansion.get_cell_size(cells[cell_idx]:get_name()) local space = size - items_in_cell_count
items_in_cell_count = 0 item_string[2] = tostring(space)
cell_items = {} table.insert(cell_items,table.concat(item_string," "))
item_count = item_count - space items_in_cell_count = items_in_cell_count + space
else
items_in_cell_count = items_in_cell_count + item_count own_inv:set_stack("main", cell_idx, write_to_cell(cells[cell_idx],cell_items,items_in_cell_count))
item_string[2] = tostring(item_count) cell_idx = next(cells, cell_idx)
table.insert(cell_items,table.concat(item_string," ")) if cell_idx == nil then
item_count = 0 --there may be other drives within the network
end me.log("too many items to store in drive","info")
break
end
size = microexpansion.get_cell_size(cells[cell_idx]:get_name())
items_in_cell_count = 0
cell_items = {}
item_count = item_count - space
else
items_in_cell_count = items_in_cell_count + item_count
item_string[2] = tostring(item_count)
table.insert(cell_items,table.concat(item_string," "))
item_count = 0
end
end end
end end
if cell_idx == nil then if cell_idx == nil then
@ -162,6 +205,7 @@ local function write_drive_cells(pos,network)
end end
local function take_all(pos,net) local function take_all(pos,net)
-- me.log("take_all", "error")
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local own_inv = meta:get_inventory() local own_inv = meta:get_inventory()
local ctrl_inv = net:get_inventory() local ctrl_inv = net:get_inventory()
@ -172,21 +216,25 @@ local function take_all(pos,net)
if name ~= "" then if name ~= "" then
local its = minetest.deserialize(stack:get_meta():get_string("items")) local its = minetest.deserialize(stack:get_meta():get_string("items"))
for _,s in pairs(its) do for _,s in pairs(its) do
table.insert(items,s) table.insert(items,s)
end end
end end
end end
for _,ostack in pairs(items) do for _,ostack in pairs(items) do
--this returns 99 (max count) even if it removes more --this returns 99 (max count) even if it removes more
ctrl_inv:remove_item("main", ostack) --ctrl_inv:remove_item("main", ostack)
print(ostack) local postack = ItemStack(ostack)
-- me.log("drive take_all remove_item "..minetest.serialize(ostack), "error")
me.log("DRIVE: take_all remove_item "..tostring(postack:get_count()).." "..postack:get_name(), "error")
me.remove_item(net, ctrl_inv, "main", postack)
end end
net:update() net:update()
me.send_event(pos,"items") me.send_event(pos,"items")
end end
local function add_all(pos,net) local function add_all(pos,net)
-- me.log("add_all", "error")
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local own_inv = meta:get_inventory() local own_inv = meta:get_inventory()
local ctrl_inv = net:get_inventory() local ctrl_inv = net:get_inventory()
@ -197,23 +245,22 @@ local function add_all(pos,net)
if name ~= "" then if name ~= "" then
local its = minetest.deserialize(stack:get_meta():get_string("items")) local its = minetest.deserialize(stack:get_meta():get_string("items"))
if its then if its then
for _,s in pairs(its) do for _,s in pairs(its) do
table.insert(items,s) table.insert(items,s)
end end
end end
end end
end
for _,ostack in pairs(items) do
me.insert_item(ostack, ctrl_inv, "main")
print(ostack)
end end
for _,ostack in pairs(items) do
me.insert_item(ostack, net, ctrl_inv, "main")
end
net:update() net:update()
me.send_event(pos,"items",{net = net}) me.send_event(pos,"items",{net = net})
end end
function me.disconnect_drive(pos,ncpos) function me.disconnect_drive(pos,ncpos)
microexpansion.log("disconnecting drive at "..minetest.pos_to_string(pos),"action") me.log("disconnecting drive at "..minetest.pos_to_string(pos),"action")
local fc,i = get_drive_controller(pos) local fc,i = get_drive_controller(pos)
if not fc.cpos then if not fc.cpos then
return return
@ -228,7 +275,7 @@ function me.disconnect_drive(pos,ncpos)
if fnet then if fnet then
take_all(pos,fnet) take_all(pos,fnet)
else else
microexpansion.log("drive couldn't take items from its former network","warning") me.log("drive couldn't take items from its former network","warning")
end end
end end
@ -240,141 +287,146 @@ local function update_drive(pos,_,ev)
local cnet = ev.net or me.get_connected_network(pos) local cnet = ev.net or me.get_connected_network(pos)
if cnet then if cnet then
if not fc then if not fc then
microexpansion.log("connecting drive at "..minetest.pos_to_string(pos),"action") me.log("connecting drive at "..minetest.pos_to_string(pos), "action")
set_drive_controller(pos,true,cnet.controller_pos,i) set_drive_controller(pos,true,cnet.controller_pos,i)
add_all(pos,cnet) add_all(pos,cnet)
elseif not fc.cpos then elseif not fc.cpos then
microexpansion.log("connecting drive at "..minetest.pos_to_string(pos),"action") me.log("connecting drive at "..minetest.pos_to_string(pos), "action")
set_drive_controller(pos,false,cnet.controller_pos,i) set_drive_controller(pos,false,cnet.controller_pos,i)
add_all(pos,cnet) add_all(pos,cnet)
elseif not vector.equals(fc.cpos,cnet.controller_pos) then elseif not vector.equals(fc.cpos,cnet.controller_pos) then
microexpansion.log("reconnecting drive at "..minetest.pos_to_string(pos),"action") me.log("reconnecting drive at "..minetest.pos_to_string(pos), "action")
write_drive_cells(pos,me.get_network(fc.cpos)) write_drive_cells(pos,me.get_network(fc.cpos))
set_drive_controller(pos,false,cnet.controller_pos,i) set_drive_controller(pos,false,cnet.controller_pos,i)
add_all(pos,cnet) add_all(pos,cnet)
me.disconnect_drive(pos,cnet.controller_pos) me.disconnect_drive(pos,cnet.controller_pos)
else else
if ev.origin.name == "microexpansion:ctrl" then if ev.origin.name == "microexpansion:ctrl" then
me.disconnect_drive(pos,false) me.disconnect_drive(pos,false)
end end
end end
else elseif fc then
if fc then if fc.cpos then
if fc.cpos then me.disconnect_drive(pos,false)
me.disconnect_drive(pos,false)
end
end end
end end
end end
-- [me chest] Register node -- [me chest] Register node
microexpansion.register_node("drive", { microexpansion.register_node("drive", {
description = "ME Drive", description = "ME Drive",
usedfor = "Stores items into ME storage cells", usedfor = "Stores items into ME storage cells",
tiles = { tiles = {
"chest_top", "chest_top",
"chest_top", "chest_top",
"chest_side", "chest_side",
"chest_side", "chest_side",
"chest_side", "chest_side",
"drive_full", "drive_full",
}, },
recipe = { recipe = {
{ 1, { { 1, {
{"default:steel_ingot", "default:chest", "default:steel_ingot" }, {"default:steel_ingot", "default:chest", "default:steel_ingot" },
{"default:steel_ingot", "microexpansion:machine_casing", "default:steel_ingot" }, {"default:steel_ingot", "microexpansion:machine_casing", "default:steel_ingot" },
{"default:steel_ingot", "default:chest", "default:steel_ingot" }, {"default:steel_ingot", "default:chest", "default:steel_ingot" },
}, },
} }
}, },
is_ground_content = false, is_ground_content = false,
groups = { cracky = 1, me_connect = 1 }, groups = { cracky = 1, me_connect = 1 },
paramtype = "light", paramtype = "light",
paramtype2 = "facedir", paramtype2 = "facedir",
me_update = update_drive, me_update = update_drive,
on_construct = function(pos) on_construct = function(pos)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
meta:set_string("formspec", meta:set_string("formspec",
"size[9,9.5]".. "size[9,7.5]"..
microexpansion.gui_bg .. microexpansion.gui_bg ..
microexpansion.gui_slots .. microexpansion.gui_slots ..
[[ [[
label[0,-0.23;ME Drive] label[0,-0.23;ME Drive]
list[context;main;0,0.3;8,4] list[context;main;0,0.3;5,2]
list[current_player;main;0,5.5;8,1;] list[current_player;main;0,3.5;8,1;]
list[current_player;main;0,6.73;8,3;8] list[current_player;main;0,4.73;8,3;8]
listring[current_name;main] listring[current_name;main]
listring[current_player;main] listring[current_player;main]
field_close_on_enter[filter;false] field_close_on_enter[filter;false]
]]) ]])
local inv = meta:get_inventory() local inv = meta:get_inventory()
inv:set_size("main", 10) inv:set_size("main", 10)
me.send_event(pos,"connect") me.send_event(pos,"connect")
end, end,
can_dig = function(pos, player) can_dig = function(pos, player)
if minetest.is_protected(pos, player) then if minetest.is_protected(pos, player) then
return false return false
end end
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local inv = meta:get_inventory() local inv = meta:get_inventory()
return inv:is_empty("main") return inv:is_empty("main")
end, end,
after_destruct = function(pos) after_destruct = function(pos)
me.send_event(pos,"disconnect") me.send_event(pos,"disconnect")
end, end,
allow_metadata_inventory_put = function(_, _, _, stack) allow_metadata_inventory_put = function(pos, _, _, stack, player)
if minetest.is_protected(pos, player) or minetest.get_item_group(stack:get_name(), "microexpansion_cell") == 0 then if minetest.is_protected(pos, player)
return 0 or minetest.get_item_group(stack:get_name(), "microexpansion_cell") == 0 then
else return 0
return 1 end
end return 1
end, end,
on_metadata_inventory_put = function(pos, _, _, stack) on_metadata_inventory_put = function(pos, _, _, stack)
me.send_event(pos,"item_cap") me.send_event(pos,"item_cap")
local network = me.get_connected_network(pos) local network = me.get_connected_network(pos)
if network == nil then if network == nil then
return return
end end
local ctrl_inv = network:get_inventory() local ctrl_inv = network:get_inventory()
local items = minetest.deserialize(stack:get_meta():get_string("items")) local items = minetest.deserialize(stack:get_meta():get_string("items"))
if items == nil then if items == nil then
print("no items") print("no items")
me.send_event(pos,"items",{net=network}) me.send_event(pos,"items",{net=network})
return return
end end
network:set_storage_space(#items) -- network:set_storage_space(#items)
for _,s in pairs(items) do for _,stack in pairs(items) do
me.insert_item(s, ctrl_inv, "main") network:set_storage_space(true)
end me.insert_item(stack, network, ctrl_inv, "main")
me.send_event(pos,"items",{net=network}) end
end, network:set_storage_space(true)
allow_metadata_inventory_take = function(pos,_,_,stack, player) --args: pos, listname, index, stack, player me.send_event(pos,"items",{net=network})
if minetest.is_protected(pos, player) then end,
return 0 allow_metadata_inventory_take = function(pos,_,_,stack, player) --args: pos, listname, index, stack, player
end if minetest.is_protected(pos, player) then
local network = me.get_connected_network(pos) return 0
write_drive_cells(pos,network) end
return stack:get_count() local network = me.get_connected_network(pos)
end, write_drive_cells(pos,network)
on_metadata_inventory_take = function(pos, _, _, stack) return stack:get_count()
local network = me.get_connected_network(pos) end,
if network == nil then on_metadata_inventory_take = function(pos, _, _, stack)
return local network = me.get_connected_network(pos)
end if network == nil then
me.send_event(pos,"item_cap",{net=network}) return
local ctrl_inv = network:get_inventory() end
local items = minetest.deserialize(stack:get_meta():get_string("items")) me.send_event(pos,"item_cap",{net=network})
if items == nil then local ctrl_inv = network:get_inventory()
network:update() local items = minetest.deserialize(stack:get_meta():get_string("items"))
return if items == nil then
end network:update()
for _,ostack in pairs(items) do return
--this returns 99 (max count) even if it removes more end
ctrl_inv:remove_item("main", ostack) for _,ostack in pairs(items) do
end local postack = ItemStack(ostack)
--print(stack:to_string()) -- me.log("drive meta_inv_take remove_item "..tostring(postack:get_count()).." "..postack:get_name(), "error")
-- TODO: this was here, but did nothing? me.remove_item(network, ctrl_inv, "main", postack)
-- No, this is the main item removal on cell removal from drive
me.remove_item(network, ctrl_inv, "main", postack)
--this returns 99 (max count) even if it removes more
--ctrl_inv:remove_item("main", ostack)
end
--print(stack:to_string())
network:update() network:update()
me.send_event(pos,"items",{net=network}) me.send_event(pos,"items",{net=network})
end, end,
}) })

@ -13,3 +13,18 @@ dofile(module_path.."/storage.lua")
-- Load machines -- Load machines
dofile(module_path.."/drive.lua") dofile(module_path.."/drive.lua")
dofile(module_path.."/terminal.lua") dofile(module_path.."/terminal.lua")
dofile(module_path.."/cterminal.lua")
dofile(module_path.."/interface.lua")
local drawers_enabled = minetest.get_modpath("drawers") and true or false
if drawers_enabled then
dofile(module_path.."/drawer-api.lua") -- Extra Drawer api
dofile(module_path.."/drawer-interop.lua")
end
local technic_enabled = minetest.get_modpath("technic") and true or false
if technic_enabled then
dofile(module_path.."/technic-interop.lua")
end
local pipeworks_enabled = minetest.get_modpath("pipeworks") and true or false
if pipeworks_enabled then
dofile(module_path.."/pipeworks-interop.lua")
end

@ -0,0 +1,313 @@
-- interface
-- microexpansion/interface.lua
local me = microexpansion
local pipeworks_enabled = minetest.get_modpath("pipeworks") and true or false
-- Interfaces work by walking all connected blocks. We walk machines and inventories.
-- This explains which nodes are connected.
-- Missing from technic_plus, the generators, might be nice to be able
-- to feed them wood when the power is low.
-- Odd machines like the reactor or the force field aren't supported.
-- We'd have to figure out what we'd want to do with them.
local function can_connect(name)
if me.registered_inventory[name] then
return true
end
return false
end
function me.walk_connected(pos)
local nodes = {}
local visited = {}
visited[pos.x] = {}
visited[pos.x][pos.y] = {}
visited[pos.x][pos.y][pos.z] = true
local to_visit = {pos}
while #to_visit > 0 do
local pos = table.remove(to_visit)
local adjacent = {
{x=pos.x+1, y=pos.y, z=pos.z},
{x=pos.x-1, y=pos.y, z=pos.z},
{x=pos.x, y=pos.y+1, z=pos.z},
{x=pos.x, y=pos.y-1, z=pos.z},
{x=pos.x, y=pos.y, z=pos.z+1},
{x=pos.x, y=pos.y, z=pos.z-1},
}
for _,apos in pairs(adjacent) do
if not visited[apos.x] then
visited[apos.x] = {}
end
if not visited[apos.x][apos.y] then
visited[apos.x][apos.y] = {}
end
if visited[apos.x][apos.y][apos.z] ~= true then
visited[apos.x][apos.y][apos.z] = true
local napos = minetest.get_node(apos)
local nn = napos.name
if can_connect(nn) then
table.insert(nodes, {pos=apos, name=nn})
table.insert(to_visit, apos)
end
end
end
end
return nodes
end
local function update(pos,_,ev)
if ev.type == "connect" then
-- net.update_counts()
elseif ev.type == "disconnect" then
--
end
end
function me.reload_inventory(name, net, ctrl_inv, int_meta, n, pos, doinventories)
local func = me.registered_inventory and me.registered_inventory[name]
if func then
func(net, ctrl_inv, int_meta, n, pos, doinventories)
end
end
function me.chest_reload(net, ctrl_inv, int_meta, n, pos, doinventories)
if not doinventories then return end
local meta = minetest.get_meta(n.pos)
local inv = meta:get_inventory()
for i = 1, inv:get_size("main") do
local stack = inv:get_stack("main", i)
if not stack:is_empty() then
net:create_loan(stack, {pos=n.pos, invname="main", slot=i, ipos=pos}, ctrl_inv, int_meta)
end
end
end
me.register_inventory("default:chest", me.chest_reload)
-- This never rewalks connected machines. To do that add a gui
-- rewalk and/or remove, replace.
function me.reload_interface(net, pos, doinventories)
if not net then return end
local ctrl_inv = net:get_inventory()
local int_meta = minetest.get_meta(pos)
local inv = int_meta:get_inventory()
local inventories = minetest.deserialize(int_meta:get_string("connected"))
-- not appropriate
-- me.send_event(pos,"connect")
int_meta:set_string("infotext", "chests: "..#inventories)
if not net.counts then
net.counts = {}
end
if not net.autocrafters then
net.autocrafters = {}
end
if not net.autocrafters_by_pos then
net.autocrafters_by_pos = {}
end
if not net.process then
net.process = {}
end
for _, n in pairs(inventories) do
local node = minetest.get_node(n.pos)
local name = node.name
-- me.log("INT: found a "..name, "error")
local outputs = me.output_by_typename[me.block_to_typename_map[name]]
if outputs then
for _, name in pairs(outputs) do
if not net.process[name] then
net.process[name] = {}
end
-- me.log("INT: registering "..name.." for the "..node.name, "error")
net.process[name][n.pos] = pos
end
else
me.reload_inventory(name, net, ctrl_inv, int_meta, n, pos, doinventories)
end
end
-- me.send_event(pos,...
end
-- [me chest] Register node
me.register_node("interface", {
description = "ME Interface",
usedfor = "Interface for ME system",
tiles = {
"chest_top",
"chest_top",
"chest_side",
"chest_side",
"chest_side",
"chest_side", -- TODO: Maybe customize it?
},
recipe = {
{ 1, {
{"default:steel_ingot", "microexpansion:machine_casing", "default:steel_ingot" },
{"default:steel_ingot", "microexpansion:machine_casing", "default:steel_ingot" },
{"default:steel_ingot", "default:chest", "default:steel_ingot" },
},
}
},
is_ground_content = false,
groups = { cracky = 1, me_connect = 1, tubedevice = 1, tubedevice_receiver = 1 },
paramtype = "light",
paramtype2 = "facedir",
me_update = update,
on_construct = function(pos)
local int_meta = minetest.get_meta(pos)
int_meta:set_string("formspec",
"size[9,7.5]"..
microexpansion.gui_bg ..
microexpansion.gui_slots ..
[[
label[0,-0.23;ME Interface]
list[context;import;0,0.3;9,1]
list[context;export;0,0.3;9,1]
list[current_player;main;0,3.5;8,1;]
list[current_player;main;0,4.73;8,3;8]
listring[current_name;import]
listring[current_player;main]
field_close_on_enter[filter;false]
]])
local inv = int_meta:get_inventory()
inv:set_size("export", 3)
inv:set_size("import", 3)
local inventories = me.walk_connected(pos)
int_meta:set_string("connected", minetest.serialize(inventories))
local net = me.get_connected_network(pos)
if net == nil then
int_meta:set_string("infotext", "No Network")
return
end
me.reload_interface(net, pos, true)
end,
can_dig = function(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv:is_empty("import")
end,
on_destruct = function(pos)
local net = me.get_connected_network(pos)
if net == nil then return end
local inv = net:get_inventory()
local int_meta = minetest.get_meta(pos)
local inventories = minetest.deserialize(int_meta:get_string("connected"))
int_meta:set_string("infotext", "")
local to_remove = {}
for _, n in pairs(inventories) do
local pos = n.pos
if not to_remove[pos.x] then
to_remove[pos.x] = {}
end
if not to_remove[pos.x][pos.y] then
to_remove[pos.x][pos.y] = {}
end
if not to_remove[pos.x][pos.y][pos.z] then
to_remove[pos.x][pos.y][pos.z] = true
end
end
net:update_counts()
local loan_slot = me.loan.get_size(net, inv)
local ref
while loan_slot > 0 do
local lstack = me.loan.get_stack(net, inv, loan_slot)
if lstack:is_empty() then
-- TODO: Don't think this can happen now, update_counts
-- me.log("interface empty loan at "..loan_slot, "error")
goto continue
foobar()
end
-- me.log("interface removing loan at "..loan_slot, "error")
ref = me.network.get_ref(lstack)
if ref and to_remove[ref.pos.x] and to_remove[ref.pos.x][ref.pos.y] and to_remove[ref.pos.x][ref.pos.y][ref.pos.z] then
net:remove_loan(ref.pos, inv, lstack, loan_slot, ref)
end
::continue::
loan_slot = loan_slot - 1
end
net:update_counts()
-- pos is a table and does not have value semantics.
if net.autocrafters_by_pos then
for k, v in pairs(net.autocrafters_by_pos) do
if k.x == pos.x and k.y == pos.y and k.z == pos.z then
pos = k
break
end
end
if net.autocrafters_by_pos[pos] then
for name, apos in pairs(net.autocrafters_by_pos[pos]) do
-- deindex these upon removal of the interface controlling them
net.autocrafters_by_pos[pos][name] = nil
net.autocrafters[name][apos] = nil
end
end
end
end,
after_destruct = function(pos)
me.send_event(pos,"disconnect")
end,
allow_metadata_inventory_put = function(pos, listname, index, stack)
return stack:get_count()
end,
on_metadata_inventory_put = function(pos, _, _, stack)
local net = me.get_connected_network(pos)
if net == nil then
return
end
local ctrl_inv = net:get_inventory()
end,
allow_metadata_inventory_take = function(pos,_,_,stack) --args: pos, listname, index, stack, player
local net = me.get_connected_network(pos)
return stack:get_count()
end,
on_metadata_inventory_take = function(pos, _, _, stack)
local net = me.get_connected_network(pos)
if net == nil then
return
end
local ctrl_inv = net:get_inventory()
end,
-- tube connection
tube = {
can_insert = function(pos, _, stack) --pos, node, stack, direction
local net = me.get_connected_network(pos)
local inv = net:get_inventory()
local max_slots = inv:get_size("main")
local max_items = net.capacity_cache
local slots, items = 0, 0
-- Get amount of items in drive
for i = 1, max_slots do
local mstack = inv:get_stack("main", i)
if mstack:get_name() ~= "" then
slots = slots + 1
local num = mstack:get_count()
if num == 0 then num = 1 end
items = items + num
end
end
items = items + stack:get_count()
return max_items > items
end,
insert_object = function(pos, _, stack)
local net = me.get_connected_network(pos)
local leftovers = stack
if net then
local inv = net:get_inventory()
leftovers = me.insert_item(stack, net, inv, "main")
net:set_storage_space(true)
end
return leftovers
end,
connect_sides = {left=1, right=1, front=1, back=1, top=1, bottom=1},
},
after_place_node = pipeworks_enabled and pipeworks.after_place,
after_dig_node = pipeworks_enabled and pipeworks.after_dig,
})

@ -0,0 +1,25 @@
-- Interoperability file for pipework support.
local me = microexpansion
me.register_inventory("pipeworks:autocrafter", function(net, ctrl_inv, int_meta, n, pos)
local meta = minetest.get_meta(n.pos)
local rinv = meta:get_inventory()
-- Autoinsert all the outputs
--for i = 1, rinv:get_size("dst")
-- local stack = rinv:get_stack("dst", i)
-- local leftovers = me.insert_item(stack, net, ctrl_inv, "main")
-- rinv:set_stack("dst", i, leftovers)
--end
-- register the crafted items so the autocrafter can use them
local craft = rinv:get_stack("output", 1)
if not craft:is_empty() then
if not net.autocrafters_by_pos[pos] then
net.autocrafters_by_pos[pos] = {}
end
net.autocrafters_by_pos[pos][craft:get_name()] = n.pos
if not net.autocrafters[craft:get_name()] then
net.autocrafters[craft:get_name()] = {}
end
net.autocrafters[craft:get_name()][n.pos] = pos
end
end)

@ -2,7 +2,7 @@
--TODO: use storagecomp for crafting --TODO: use storagecomp for crafting
-- [drive] 8k -- [drive] 1k
microexpansion.register_cell("cell_1k", { microexpansion.register_cell("cell_1k", {
description = "1k ME Storage Cell", description = "1k ME Storage Cell",
capacity = 1000, capacity = 1000,
@ -16,7 +16,7 @@ microexpansion.register_cell("cell_1k", {
}, },
}) })
-- [drive] 8k -- [drive] 2k
microexpansion.register_cell("cell_2k", { microexpansion.register_cell("cell_2k", {
description = "2k ME Storage Cell", description = "2k ME Storage Cell",
capacity = 2000, capacity = 2000,
@ -31,7 +31,7 @@ microexpansion.register_cell("cell_2k", {
}, },
}) })
-- [drive] 16k -- [drive] 4k
microexpansion.register_cell("cell_4k", { microexpansion.register_cell("cell_4k", {
description = "4k ME Storage Cell", description = "4k ME Storage Cell",
capacity = 4000, capacity = 4000,
@ -44,7 +44,7 @@ microexpansion.register_cell("cell_4k", {
}, },
}) })
-- [drive] 16k -- [drive] 8k
microexpansion.register_cell("cell_8k", { microexpansion.register_cell("cell_8k", {
description = "8k ME Storage Cell", description = "8k ME Storage Cell",
capacity = 8000, capacity = 8000,
@ -53,7 +53,7 @@ microexpansion.register_cell("cell_8k", {
}, },
}) })
-- [drive] 32k -- [drive] 16k
microexpansion.register_cell("cell_16k", { microexpansion.register_cell("cell_16k", {
description = "16k ME Storage Cell", description = "16k ME Storage Cell",
capacity = 16000, capacity = 16000,

@ -0,0 +1,234 @@
-- Interoperability file for technic and technic_plus support.
local me = microexpansion
-- technic_plus doesn't export machine speed, so wire it in here. We
-- use this to know exactly how long a machine will take to process
-- anything, after that time, we know it is done and we can grab the
-- outputs, no polling. We do this for efficiency.
-- The speeds of the various machines:
me.set_speed("technic:electric_furnace", 2)
me.set_speed("technic:mv_electric_furnace", 4)
me.set_speed("technic:hv_electric_furnace", 12)
me.set_speed("technic:lv_alloy_furnace", 1)
me.set_speed("technic:mv_alloy_furnace", 1.5)
me.set_speed("technic:lv_compressor", 1)
me.set_speed("technic:mv_compressor", 2)
me.set_speed("technic:hv_compressor", 5)
me.set_speed("technic:lv_extractor", 1)
me.set_speed("technic:mv_extractor", 2)
me.set_speed("technic:lv_grinder", 1)
me.set_speed("technic:mv_grinder", 2)
me.set_speed("technic:hv_grinder", 5)
me.set_speed("technic:mv_centrifuge", 2)
me.set_speed("technic:mv_freezer", 0.5)
-- ======================================================================== --
-- Register maximal output sizes for all the ingredients we produce.
-- We also break up deeply recursive crafts that would blow a pipeworks
-- autocrafter if it tried to make something.
-- might not be necessary, but maybe it is. It might limit
-- oversupply of inputs to batteries.
me.register_max("technic:lv_battery_box0", 3)
me.register_max("technic:battery", 12)
-- HV furnace only has 4 output slots
me.register_max("technic:cast_iron_ingot", 20)
me.register_max("mesecons_materials:glue", 380)
me.register_max("mesecons_materials:fiber", 380)
me.register_max("default:stone", 20)
me.register_max("basic_materials:plastic_sheet", 380)
me.register_max("basic_materials:paraffin", 380)
-- HV grinder only has 4 output slots
me.register_max("technic:coal_dust", 380)
me.register_max("technic:gold_dust", 380)
me.register_max("technic:sulfur_dust", 380)
me.register_max("technic:stone_dust", 380)
me.register_max("default:gravel", 380)
me.register_max("default:sand", 380)
me.register_max("default:snowblock", 380)
me.register_max("technic:rubber_tree_grindings", 380)
-- MV alloy furnace only has 4 output slots
me.register_max("technic:doped_silicon_wafer", 380)
me.register_max("technic:silicon_wafer", 380)
me.register_max("basic_materials:brass_ingot", 380)
me.register_max("default:bronze_ingot", 380)
me.register_max("technic:stainless_steel_ingot", 380)
me.register_max("technic:rubber", 380)
me.register_max("bucket:bucket_lava", 4)
me.register_max("technic:carbon_steel_ingot", 380)
-- LV extractor only has 4 output slots
me.register_max("technic:raw_latex", 380)
-- HV compressor only has 4 output slots
me.register_max("technic:composite_plate", 380)
me.register_max("technic:copper_plate", 380)
me.register_max("technic:graphite", 380)
me.register_max("technic:carbon_plate", 380)
me.register_max("technic:uranium_fuel", 380)
me.register_max("default:diamond", 380)
-- freezer only has 4 output slots
me.register_max("default:ice", 380)
-- ======================================================================== --
-- The type of machines all the machines are: We have to list these
-- before me.register_inventory.
me.register_typename("technic:electric_furnace", "cooking")
me.register_typename("technic:mv_electric_furnace", "cooking")
me.register_typename("technic:hv_electric_furnace", "cooking")
me.register_typename("technic:lv_grinder", "grinding")
me.register_typename("technic:mv_grinder", "grinding")
me.register_typename("technic:hv_grinder", "grinding")
me.register_typename("technic:coal_alloy_furnace", "alloy")
me.register_typename("technic:lv_alloy_furnace", "alloy")
me.register_typename("technic:mv_alloy_furnace", "alloy")
me.register_typename("technic:lv_extractor", "extracting")
me.register_typename("technic:mv_extractor", "extracting")
me.register_typename("technic:lv_compressor", "compressing")
me.register_typename("technic:mv_compressor", "compressing")
me.register_typename("technic:hv_compressor", "compressing")
me.register_typename("technic:mv_centrifuge", "separating")
me.register_typename("technic:mv_freezer", "freezing")
-- ======================================================================== --
-- The various blocks and how to interface to them:
me.register_inventory("technic:gold_chest", me.chest_reload)
me.register_inventory("technic:mithril_chest", me.chest_reload)
me.register_inventory("technic:quarry", function(net, ctrl_inv, int_meta, n, pos)
local meta = minetest.get_meta(n.pos)
local rinv = meta:get_inventory()
for i = 1, rinv:get_size("cache") do
local stack = rinv:get_stack("cache", i)
if not stack:is_empty() then
local leftovers = me.insert_item(stack, net, ctrl_inv, "main")
rinv:set_stack("cache", i, leftovers)
end
end
-- we can set up a timer to recheck the cache every 30 seconds and
-- clean it out for example.
end)
me.register_inventory("technic:mv_centrifuge", function(net, ctrl_inv, int_meta, n, pos)
local meta = minetest.get_meta(n.pos)
local rinv = meta:get_inventory()
for i = 1, rinv:get_size("dst") do
local stack = rinv:get_stack("dst", i)
if not stack:is_empty() then
local leftovers = me.insert_item(stack, net, ctrl_inv, "main")
rinv:set_stack("dst", i, leftovers)
end
end
for i = 1, rinv:get_size("dst2") do
local stack = rinv:get_stack("dst2", i)
if not stack:is_empty() then
local leftovers = me.insert_item(stack, net, ctrl_inv, "main")
rinv:set_stack("dst2", i, leftovers)
end
end
end)
-- ======================================================================== --
-- The various outputs the various machine types can generate:
me.register_output_by_typename("cooking", "technic:cast_iron_ingot")
me.register_output_by_typename("cooking", "mesecons_materials:glue")
me.register_output_by_typename("cooking", "mesecons_materials:fiber")
me.register_output_by_typename("cooking", "basic_materials:plastic_sheet")
me.register_output_by_typename("cooking", "basic_materials:paraffin")
me.register_output_by_typename("grinding", "technic:coal_dust")
me.register_output_by_typename("grinding", "technic:gold_dust")
me.register_output_by_typename("grinding", "technic:sulfur_dust")
me.register_output_by_typename("grinding", "technic:stone_dust")
me.register_output_by_typename("grinding", "default:gravel")
me.register_output_by_typename("grinding", "default:sand")
me.register_output_by_typename("grinding", "default:snowblock")
me.register_output_by_typename("grinding", "technic:rubber_tree_grindings")
me.register_output_by_typename("alloy", "technic:doped_silicon_wafer")
me.register_output_by_typename("alloy", "technic:silicon_wafer")
me.register_output_by_typename("alloy", "basic_materials:brass_ingot")
me.register_output_by_typename("alloy", "default:bronze_ingot")
me.register_output_by_typename("alloy", "technic:stainless_steel_ingot")
me.register_output_by_typename("alloy", "technic:rubber")
me.register_output_by_typename("alloy", "bucket:bucket_lava")
me.register_output_by_typename("alloy", "technic:carbon_steel_ingot")
me.register_output_by_typename("extracting", "technic:raw_latex")
me.register_output_by_typename("compressing", "technic:composite_plate")
me.register_output_by_typename("compressing", "technic:copper_plate")
me.register_output_by_typename("compressing", "technic:graphite")
me.register_output_by_typename("compressing", "technic:carbon_plate")
me.register_output_by_typename("compressing", "technic:uranium_fuel")
me.register_output_by_typename("compressing", "default:diamond")
-- Any of these worth doing? TODO: Uranium, sure.
--me.register_output_by_typename("separating", "")
me.register_output_by_typename("freezing", "default:ice")
-- ======================================================================== --
-- The inputs required for the given output. The inputs are exact count, the output it just
-- for 1. We'll figure out how many are actually produced later. For multiple outputs
-- only list the more interesting one.
-- furnace ("cooking")
me.register_output_to_inputs("technic:cast_iron_ingot", { ItemStack("default:steel_ingot") })
me.register_output_to_inputs("mesecons_materials:glue", { ItemStack("technic:raw_latex") })
me.register_output_to_inputs("mesecons_materials:fiber", { ItemStack("mesecons_materials:glue") })
me.register_output_to_inputs("basic_materials:plastic_sheet", { ItemStack("basic_materials:paraffin") })
me.register_output_to_inputs("basic_materials:paraffin", { ItemStack("basic_materials:oil_extract") })
-- grinder ("grinding")
me.register_output_to_inputs("technic:coal_dust", { ItemStack("default:coal_lump") })
me.register_output_to_inputs("technic:gold_dust", { ItemStack("default:gold_lump") })
me.register_output_to_inputs("technic:sulfur_dust", { ItemStack("technic:sulfur_lump") })
me.register_output_to_inputs("technic:stone_dust", { ItemStack("default:stone") })
me.register_output_to_inputs("default:gravel", { ItemStack("default:cobble") })
me.register_output_to_inputs("default:sand", { ItemStack("default:gravel") })
me.register_output_to_inputs("default:snowblock", { ItemStack("default:ice") })
-- TODO: Something about this doesn't work: recheck, think it is fixed now
me.register_output_to_inputs("technic:rubber_tree_grindings", { ItemStack("moretrees:rubber_tree_trunk") })
-- alloy_furnace ("alloy")
-- The most useful alloy recipes. We don't do the less useful ones as we don't yet have
-- a way for the user to say, no, don't do that.
me.register_output_to_inputs("technic:doped_silicon_wafer", { ItemStack("technic:gold_dust"), ItemStack("technic:silicon_wafer") })
me.register_output_to_inputs("technic:silicon_wafer", { ItemStack("default:sand 2"), ItemStack("technic:coal_dust 2") })
me.register_output_to_inputs("basic_materials:brass_ingot", { ItemStack("default:copper_ingot 2"), ItemStack("technic:zinc_ingot") })
me.register_output_to_inputs("default:bronze_ingot", { ItemStack("default:copper_ingot 7"), ItemStack("default:tin_ingot") })
me.register_output_to_inputs("technic:stainless_steel_ingot", { ItemStack("technic:carbon_steel_ingot 4"), ItemStack("technic:chromium_ingot") })
me.register_output_to_inputs("technic:rubber", { ItemStack("technic:raw_latex 4"), ItemStack("technic:coal_dust 2") })
me.register_output_to_inputs("bucket:bucket_lava", { ItemStack("default:obsidian"), ItemStack("bucket:bucket_empty") })
me.register_output_to_inputs("technic:carbon_steel_ingot", { ItemStack("default:steel_ingot 2"), ItemStack("technic:coal_dust") })
-- extractor ("extracting")
me.register_output_to_inputs("technic:raw_latex", { ItemStack("technic:rubber_tree_grindings 4") })
-- compressor ("compressing")
me.register_output_to_inputs("technic:composite_plate", { ItemStack("technic:mixed_metal_ingot") })
me.register_output_to_inputs("technic:copper_plate", { ItemStack("default:copper_ingot 5") })
me.register_output_to_inputs("technic:graphite", { ItemStack("technic:coal_dust 4") })
me.register_output_to_inputs("technic:carbon_plate", { ItemStack("technic:carbon_cloth") })
me.register_output_to_inputs("technic:uranium_fuel", { ItemStack("technic:uranium35_ingot 5") })
me.register_output_to_inputs("default:diamond", { ItemStack("technic:graphite 25") })
-- centrifuge ("separating")
-- freezer ("freezing")
me.register_output_to_inputs("default:ice", { ItemStack("bucket:bucket_water") })

@ -1,3 +1,4 @@
-- terminal
-- microexpansion/machines.lua -- microexpansion/machines.lua
local me = microexpansion local me = microexpansion
@ -5,136 +6,149 @@ local pipeworks_enabled = minetest.get_modpath("pipeworks") and true or false
-- [me chest] Get formspec -- [me chest] Get formspec
local function chest_formspec(pos, start_id, listname, page_max, q) local function chest_formspec(pos, start_id, listname, page_max, q)
local list local list
local page_number = "" local page_number = ""
local buttons = "" local buttons = ""
local query = q or "" local query = q or ""
local net,cp = me.get_connected_network(pos) local net,cp = me.get_connected_network(pos)
if cp then if cp then
if listname and net:get_item_capacity() > 0 then if listname and net:get_item_capacity() > 0 then
local ctrlinvname = net:get_inventory_name() local ctrlinvname = net:get_inventory_name()
if listname == "main" then if listname == "main" then
list = "list[detached:"..ctrlinvname..";" list = "list[detached:"..ctrlinvname..";"
.. listname .. ";0,0.3;8,4;" .. (start_id - 1) .. "]" .. listname .. ";0,0.3;8,4;" .. (start_id - 1) .. "]"
else else
list = "list[context;" .. listname .. ";0,0.3;8,4;" .. (start_id - 1) .. "]" list = "list[context;" .. listname .. ";0,0.3;8,4;" .. (start_id - 1) .. "]"
end end
list = list .. [[ if minetest.get_modpath("i3") then
list[current_player;main;0,5.5;8,1;] list = list .. [[
list[current_player;main;0,6.73;8,3;8] list[current_player;main;0,5.5;9,4;]
listring[detached:]]..ctrlinvname..[[;main] ]]
listring[current_player;main] else
]] list = list .. [[
buttons = [[ list[current_player;main;0,5.5;8,1;]
button[3.56,4.35;1.8,0.9;tochest;To Drive] list[current_player;main;0,6.73;8,3;8]
tooltip[tochest;Move everything from your inventory to the ME network.] ]]
button[5.4,4.35;0.8,0.9;prev;<] end
button[7.25,4.35;0.8,0.9;next;>] list = list .. [[
tooltip[prev;Previous] listring[detached:]]..ctrlinvname..[[;main]
tooltip[next;Next] listring[current_player;main]
field[0.29,4.6;2.2,1;filter;;]]..query..[[] ]]
button[2.1,4.5;0.8,0.5;search;?] buttons = [[
button[2.75,4.5;0.8,0.5;clear;X] button[3.56,4.35;1.8,0.9;tochest;To Drive]
tooltip[search;Search] tooltip[tochest;Move everything from your inventory to the ME network.]
tooltip[clear;Reset] button[5.4,4.35;0.8,0.9;prev;<]
]] button[7.25,4.35;0.8,0.9;next;>]
else tooltip[prev;Previous]
list = "label[3,2;" .. minetest.colorize("red", "No connected drives!") .. "]" tooltip[next;Next]
end field[0.29,4.6;2.2,1;filter;;]]..query..[[]
else button[2.1,4.5;0.8,0.5;search;?]
list = "label[3,2;" .. minetest.colorize("red", "No connected network!") .. "]" button[2.75,4.5;0.8,0.5;clear;X]
end tooltip[search;Search]
if page_max then tooltip[clear;Reset]
page_number = "label[6.15,4.5;" .. math.floor((start_id / 32)) + 1 .. ]]
"/" .. page_max .."]" else
end list = "label[3,2;" .. minetest.colorize("red", "No connected storage!") .. "]"
end
else
list = "label[3,2;" .. minetest.colorize("red", "No connected network!") .. "]"
end
if page_max then
page_number = "label[6.15,4.5;" .. math.floor((start_id / 32)) + 1 ..
"/" .. page_max .."]"
end
return [[ return [[
size[9,9.5] size[9,9.5]
]].. ]]..
microexpansion.gui_bg .. microexpansion.gui_bg ..
microexpansion.gui_slots .. microexpansion.gui_slots ..
list .. list ..
[[ [[
label[0,-0.23;ME Terminal] label[0,-0.23;ME Terminal]
field_close_on_enter[filter;false] field_close_on_enter[filter;false]
]].. ]]..
page_number .. page_number ..
buttons buttons
end end
local function update_chest(pos,_,ev) local function update_chest(pos,_,ev)
--for now all events matter --for now all events matter
local network = me.get_connected_network(pos)
local meta = minetest.get_meta(pos)
if network == nil then
meta:set_int("page", 1)
meta:set_string("formspec", chest_formspec(pos, 1))
return
end
local size = network:get_item_capacity()
local page_max = me.int_to_pagenum(size) + 1
meta:set_string("inv_name", "main") local network = me.get_connected_network(pos)
meta:set_string("formspec", chest_formspec(pos, 1, "main", page_max)) local meta = minetest.get_meta(pos)
if network == nil then
meta:set_int("page", 1)
meta:set_string("formspec", chest_formspec(pos, 1))
return
end
local size = network:get_item_capacity()
local page_max = me.int_to_pagenum(size) + 1
meta:set_string("inv_name", "main")
meta:set_string("formspec", chest_formspec(pos, 1, "main", page_max))
end end
-- [me chest] Register node -- [me chest] Register node
microexpansion.register_node("term", { me.register_node("term", {
description = "ME Terminal", description = "ME Terminal",
usedfor = "Can interact with storage cells in ME networks", usedfor = "Can interact with storage cells in ME networks",
tiles = { tiles = {
"chest_top", "chest_top",
"chest_top", "chest_top",
"chest_side", "chest_side",
"chest_side", "chest_side",
"chest_side", "chest_side",
"chest_front", "chest_front",
}, },
recipe = { recipe = {
{ 1, { { 1, {
{"default:steel_ingot", "default:chest", "default:steel_ingot"}, {"default:steel_ingot", "default:chest", "default:steel_ingot"},
{"default:steel_ingot", "microexpansion:machine_casing", "default:steel_ingot"}, {"default:steel_ingot", "microexpansion:machine_casing", "default:steel_ingot"},
{"default:steel_ingot", "microexpansion:cable", "default:steel_ingot"}, {"default:steel_ingot", "microexpansion:cable", "default:steel_ingot"},
}, },
} }
}, },
is_ground_content = false, is_ground_content = false,
groups = { cracky = 1, me_connect = 1, tubedevice = 1, tubedevice_receiver = 1 }, groups = { cracky = 1, me_connect = 1, tubedevice = 1, tubedevice_receiver = 1 },
paramtype = "light", paramtype = "light",
paramtype2 = "facedir", paramtype2 = "facedir",
me_update = update_chest, me_update = update_chest,
on_construct = function(pos) on_construct = function(pos)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
meta:set_string("formspec", chest_formspec(pos, 1)) meta:set_string("formspec", chest_formspec(pos, 1))
meta:set_string("inv_name", "none") meta:set_string("inv_name", "none")
meta:set_int("page", 1) meta:set_int("page", 1)
local own_inv = meta:get_inventory() local own_inv = meta:get_inventory()
own_inv:set_size("src", 1) own_inv:set_size("src", 1)
local net = me.get_connected_network(pos) local net = me.get_connected_network(pos)
me.send_event(pos,"connect",{net=net}) me.send_event(pos,"connect",{net=net})
if net then if net then
update_chest(pos) update_chest(pos)
end end
end, end,
after_destruct = function(pos) after_destruct = function(pos)
me.send_event(pos,"disconnect") me.send_event(pos,"disconnect")
end,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
-- TODO: Check capacity, only allow if room
return stack:get_count()
end, end,
on_metadata_inventory_put = function(pos, listname, _, stack) on_metadata_inventory_put = function(pos, listname, _, stack)
local net = me.get_connected_network(pos) local net = me.get_connected_network(pos)
local inv = net:get_inventory() local inv = net:get_inventory()
me.insert_item(stack, inv, "main") me.insert_item(stack, net, inv, "main")
net:set_storage_space(true)
end, end,
on_metadata_inventory_take = function(pos, listname, _, stack) on_metadata_inventory_take = function(pos, listname, _, stack)
local net = me.get_connected_network(pos) local net = me.get_connected_network(pos)
local inv = net:get_inventory() local inv = net:get_inventory()
inv:remove_item("main", stack) me.remove_item(net, inv, "main", stack)
end, end,
tube = { tube = {
can_insert = function(pos, _, stack) --pos, node, stack, direction can_insert = function(pos, _, stack) --pos, node, stack, direction
local net = me.get_connected_network(pos) local net = me.get_connected_network(pos)
local inv = net:get_inventory() local inv = net:get_inventory()
@ -144,13 +158,13 @@ microexpansion.register_node("term", {
local slots, items = 0, 0 local slots, items = 0, 0
-- Get amount of items in drive -- Get amount of items in drive
for i = 1, max_slots do for i = 1, max_slots do
local dstack = inv:get_stack("main", i) local dstack = inv:get_stack("main", i)
if dstack:get_name() ~= "" then if dstack:get_name() ~= "" then
slots = slots + 1 slots = slots + 1
local num = dstack:get_count() local num = dstack:get_count()
if num == 0 then num = 1 end if num == 0 then num = 1 end
items = items + num items = items + num
end end
end end
items = items + stack:get_count() items = items + stack:get_count()
return max_items > items return max_items > items
@ -158,101 +172,100 @@ microexpansion.register_node("term", {
insert_object = function(pos, _, stack) insert_object = function(pos, _, stack)
local net = me.get_connected_network(pos) local net = me.get_connected_network(pos)
local inv = net:get_inventory() local inv = net:get_inventory()
me.insert_item(stack, inv, "main") local leftovers = me.insert_item(stack, net, inv, "main")
net:set_storage_space(true) net:set_storage_space(true)
--TODO: leftover return leftovers
return ItemStack()
end, end,
connect_sides = {left=1, right=1, front=1, back=1, top=1, bottom=1}, connect_sides = {left=1, right=1, front=1, back=1, top=1, bottom=1},
}, },
after_place_node = pipeworks_enabled and pipeworks.after_place, after_place_node = pipeworks_enabled and pipeworks.after_place,
after_dig_node = pipeworks_enabled and pipeworks.after_dig, after_dig_node = pipeworks_enabled and pipeworks.after_dig,
on_receive_fields = function(pos, _, fields, sender) on_receive_fields = function(pos, _, fields, sender)
local net,cp = me.get_connected_network(pos) local net,cp = me.get_connected_network(pos)
if net then if net then
if cp then if cp then
microexpansion.log("network and ctrl_pos","info") me.log("network and ctrl_pos","info")
else else
microexpansion.log("network but no ctrl_pos","warning") me.log("network but no ctrl_pos","warning")
end end
else else
if cp then if cp then
microexpansion.log("no network but ctrl_pos","warning") me.log("no network but ctrl_pos","warning")
else else
microexpansion.log("no network and no ctrl_pos","info") me.log("no network and no ctrl_pos","info")
end end
end end
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local page = meta:get_int("page") local page = meta:get_int("page")
local inv_name = meta:get_string("inv_name") local inv_name = meta:get_string("inv_name")
local own_inv = meta:get_inventory() local own_inv = meta:get_inventory()
local ctrl_inv local ctrl_inv
if cp then if cp then
ctrl_inv = net:get_inventory() ctrl_inv = net:get_inventory()
else else
microexpansion.log("no network connected","warning") me.log("no network connected","warning")
return return
end end
local inv local inv
if inv_name == "main" then if inv_name == "main" then
inv = ctrl_inv inv = ctrl_inv
assert(inv,"no control inv") assert(inv,"no control inv")
else else
inv = own_inv inv = own_inv
assert(inv,"no own inv") assert(inv,"no own inv")
end end
local page_max = math.floor(inv:get_size(inv_name) / 32) + 1 local page_max = math.floor(inv:get_size(inv_name) / 32) + 1
if inv_name == "none" then if inv_name == "none" then
return return
end end
if fields.next then if fields.next then
if page + 32 > inv:get_size(inv_name) then if page + 32 > inv:get_size(inv_name) then
return return
end end
meta:set_int("page", page + 32) meta:set_int("page", page + 32)
meta:set_string("formspec", chest_formspec(pos, page + 32, inv_name, page_max)) meta:set_string("formspec", chest_formspec(pos, page + 32, inv_name, page_max))
elseif fields.prev then elseif fields.prev then
if page - 32 < 1 then if page - 32 < 1 then
return return
end end
meta:set_int("page", page - 32) meta:set_int("page", page - 32)
meta:set_string("formspec", chest_formspec(pos, page - 32, inv_name, page_max)) meta:set_string("formspec", chest_formspec(pos, page - 32, inv_name, page_max))
elseif fields.search or fields.key_enter_field == "filter" then elseif fields.search or fields.key_enter_field == "filter" then
own_inv:set_size("search", 0) own_inv:set_size("search", 0)
if fields.filter == "" then if fields.filter == "" then
meta:set_int("page", 1) meta:set_int("page", 1)
meta:set_string("inv_name", "main") meta:set_string("inv_name", "main")
meta:set_string("formspec", chest_formspec(pos, 1, "main", page_max)) meta:set_string("formspec", chest_formspec(pos, 1, "main", page_max))
else else
local tab = {} local tab = {}
for i = 1, ctrl_inv:get_size("main") do for i = 1, ctrl_inv:get_size("main") do
local match = ctrl_inv:get_stack("main", i):get_name():find(fields.filter) local match = ctrl_inv:get_stack("main", i):get_name():find(fields.filter)
if match then if match then
tab[#tab + 1] = ctrl_inv:get_stack("main", i) tab[#tab + 1] = ctrl_inv:get_stack("main", i)
end end
end end
own_inv:set_list("search", tab) own_inv:set_list("search", tab)
meta:set_int("page", 1) meta:set_int("page", 1)
meta:set_string("inv_name", "search") meta:set_string("inv_name", "search")
meta:set_string("formspec", chest_formspec(pos, 1, "search", page_max, fields.filter)) meta:set_string("formspec", chest_formspec(pos, 1, "search", page_max, fields.filter))
end end
elseif fields.clear then elseif fields.clear then
own_inv:set_size("search", 0) own_inv:set_size("search", 0)
meta:set_int("page", 1) meta:set_int("page", 1)
meta:set_string("inv_name", "main") meta:set_string("inv_name", "main")
meta:set_string("formspec", chest_formspec(pos, 1, "main", page_max)) meta:set_string("formspec", chest_formspec(pos, 1, "main", page_max))
elseif fields.tochest then elseif fields.tochest then
local pinv = minetest.get_inventory({type="player", name=sender:get_player_name()}) local pinv = minetest.get_inventory({type="player", name=sender:get_player_name()})
net:set_storage_space(pinv:get_size("main")) -- TODO: test and fix, net:set_storage_space(pinv:get_size("main"))
local space = net:get_item_capacity() local space = net:get_item_capacity()
local contents = ctrl_inv:get_list("main") or {} local contents = ctrl_inv:get_list("main") or {}
for _,s in pairs(contents) do for _,s in pairs(contents) do
if not s:is_empty() then if not s:is_empty() then
space = space - s:get_count() space = space - s:get_count()
end end
end end
microexpansion.move_inv({ inv=pinv, name="main" }, { inv=ctrl_inv, name="main",huge=true }, space) me.move_inv(net, { inv=pinv, name="main" }, { inv=ctrl_inv, name="main", huge=true }, space)
net:set_storage_space(true) net:set_storage_space(true)
end end
end, end,
}) })