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

@ -1,4 +1,4 @@
name = microexpansion
description = A storage managing solution to get an overview over all your items.
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
me.register_node("ctrl", {
description = "ME Controller",
tiles = {
"ctrl_sides",
"ctrl_bottom",
"ctrl_sides",
"ctrl_sides",
"ctrl_sides",
"ctrl_sides"
},
recipe = {
description = "ME Controller",
tiles = {
"ctrl_sides",
"ctrl_bottom",
"ctrl_sides",
"ctrl_sides",
"ctrl_sides",
"ctrl_sides"
},
recipe = {
{ 1, {
{"default:steel_ingot", "microexpansion:steel_infused_obsidian_ingot", "default:steel_ingot"},
{"default:steel_ingot", "microexpansion:machine_casing", "default:steel_ingot"},
{"default:steel_ingot", "microexpansion:cable", "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:cable", "default:steel_ingot"},
},
}
},
drawtype = "nodebox",
paramtype = "light",
node_box = {
type = "fixed",
fixed = {
{-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.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.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.1875, -0.5, 0.5, 0.5, -0.1875}, -- Top1
{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.1875, 0.5, 0.5, 0.5}, -- Top4
{-0.1875, -0.5, -0.1875, 0.1875, -0.25, 0.1875}, -- Bottom2
},
},
groups = { cracky = 1, me_connect = 1, },
connect_sides = "nobottom",
me_update = function(pos,_,ev)
local cnet = me.get_network(pos)
if cnet == nil then
microexpansion.log("no network for ctrl at pos "..minetest.pos_to_string(pos),"error")
drawtype = "nodebox",
paramtype = "light",
node_box = {
type = "fixed",
fixed = {
{-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.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.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.1875, -0.5, 0.5, 0.5, -0.1875}, -- Top1
{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.1875, 0.5, 0.5, 0.5}, -- Top4
{-0.1875, -0.5, -0.1875, 0.1875, -0.25, 0.1875}, -- Bottom2
},
},
groups = { cracky = 1, me_connect = 1, },
connect_sides = "nobottom",
me_update = function(pos,_,ev)
local net = me.get_network(pos)
if net == nil then
me.log("no network for ctrl at pos "..minetest.pos_to_string(pos),"error")
return
end
cnet:update()
net:update()
end,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
@ -60,13 +60,13 @@ me.register_node("ctrl", {
meta:set_string("infotext", "Network Controller")
end,
after_place_node = function(pos, player)
local name = player:get_player_name()
local meta = minetest.get_meta(pos)
meta:set_string("infotext", "Network Controller (owned by "..name..")")
meta:set_string("owner", name)
end,
on_destruct = function(pos)
after_place_node = function(pos, player)
local name = player:get_player_name()
local meta = minetest.get_meta(pos)
meta:set_string("infotext", "Network Controller (owned by "..name..")")
meta:set_string("owner", name)
end,
on_destruct = function(pos)
local net,idx = me.get_network(pos)
--disconnect all those who need the network
me.send_event(pos,"disconnect",{net=net})
@ -78,61 +78,93 @@ me.register_node("ctrl", {
end
--disconnect all those that haven't realized the network is gone
me.send_event(pos,"disconnect")
end,
after_destruct = function(pos)
end,
after_destruct = function(pos)
--disconnect all those that haven't realized the controller was disconnected
me.send_event(pos,"disconnect")
end,
machine = {
type = "controller",
},
end,
machine = {
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
me.register_machine("cable", {
description = "ME Cable",
tiles = {
"cable",
},
recipe = {
description = "ME Cable",
tiles = {
"cable",
},
recipe = {
{ 12, "shapeless", {
"microexpansion:steel_infused_obsidian_ingot", "microexpansion:machine_casing"
"microexpansion:steel_infused_obsidian_ingot", "microexpansion:machine_casing"
},
}
},
drawtype = "nodebox",
node_box = {
type = "connected",
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_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_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_right = {-0.25, -0.25, -0.25, 0.5, 0.25, 0.25}, -- x+
},
paramtype = "light",
groups = { crumbly = 1, },
--TODO: move these functions into the registration
on_construct = function(pos)
me.send_event(pos,"connect")
end,
after_destruct = function(pos)
me.send_event(pos,"disconnect")
end,
me_update = function(pos,_,ev)
if ev then
if ev.type ~= "disconnect" then return end
end
--maybe this shouldn't be called on every update
local meta = minetest.get_meta(pos)
if me.get_connected_network(pos) then
meta:set_string("infotext", "Network connected")
else
meta:set_string("infotext", "No Network")
end
end,
machine = {
type = "conductor",
},
drawtype = "nodebox",
node_box = {
type = "connected",
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_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_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_right = {-0.25, -0.25, -0.25, 0.5, 0.25, 0.25}, -- x+
},
paramtype = "light",
groups = { crumbly = 1, },
--TODO: move these functions into the registration
on_construct = function(pos)
me.send_event(pos,"connect")
end,
after_destruct = function(pos)
me.send_event(pos,"disconnect")
end,
me_update = function(pos,_,ev)
if ev then
if ev.type ~= "disconnect" then return end
end
--maybe this shouldn't be called on every update
local meta = minetest.get_meta(pos)
if me.get_connected_network(pos) then
meta:set_string("infotext", "Network connected")
else
meta:set_string("infotext", "No Network")
end
end,
machine = {
type = "conductor",
},
})

@ -1,7 +1,7 @@
local me = microexpansion
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
--[[
@ -30,98 +30,397 @@ local function split_stack_values(stack)
end
--]]
function me.insert_item(stack, inv, listname)
if me.settings.huge_stacks == false then
return inv:add_item(listname, stack)
end
local to_insert = type(stack) == "userdata" and stack or ItemStack(stack)
local found = false
for i = 0, inv:get_size(listname) do
local inside = inv:get_stack(listname, i)
if inside:get_name() == to_insert:get_name() and inside:get_wear() == to_insert:get_wear() then
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)
local annotate_large_stack = function(stack, count)
local description = minetest.registered_items[stack:get_name()]
if description then
-- steel is an alias and won't be found in here, skip it
description = description.description
--stack:set_count(1)
--This screw up everything, autocrafting, item removal and more
--stack:get_meta():set_string("description", description.." "..count)
stack:get_meta():set_string("description", "")
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.."/autocraft.lua") -- Autocrafting
-- generate iterator to find all connected nodes
function me.connected_nodes(start_pos,include_ctrl)
-- nodes to be checked
local open_list = {{pos = start_pos}}
-- nodes that were checked
local closed_set = {}
-- local connected nodes function to reduce table lookups
local adjacent_connected_nodes = me.network.adjacent_connected_nodes
-- return the generated iterator
return function ()
-- start looking for next pos
local open = false
-- pos to be checked
local current
-- find next unclosed
while not open do
-- get unchecked pos
current = table.remove(open_list)
-- none are left
if current == nil then return end
-- assume it's open
open = true
-- check the closed positions
for _,closed in pairs(closed_set) do
-- if current is unclosed
if vector.equals(closed,current.pos) then
--found one was closed
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
-- nodes to be checked
local open_list = {{pos = start_pos}}
-- nodes that were checked
local closed_set = {}
-- local connected nodes function to reduce table lookups
local adjacent_connected_nodes = me.network.adjacent_connected_nodes
-- return the generated iterator
return function ()
-- start looking for next pos
local open = false
-- pos to be checked
local current
-- find next unclosed
while not open do
-- get unchecked pos
current = table.remove(open_list)
-- none are left
if current == nil then return end
-- assume it's open
open = true
-- check the closed positions
for _,closed in pairs(closed_set) do
-- if current is unclosed
if vector.equals(closed,current.pos) then
--found one was closed
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
-- get network connected to position
function me.get_connected_network(start_pos)
for npos,nn in me.connected_nodes(start_pos,true) do
if nn == "microexpansion:ctrl" then
local network = me.get_network(npos)
if network then
return network,npos
end
end
end
for npos,nn in me.connected_nodes(start_pos,true) do
if nn == "microexpansion:ctrl" then
local net = me.get_network(npos)
if net then
return net,npos
end
end
end
end
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 sn = microexpansion.get_node(start_pos)
local sn = me.get_node(start_pos)
local sd = minetest.registered_nodes[sn.name]
local sm = sd.machine or {}
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
me.update_node(npos,ev)
end
end
end
end
function me.send_event(spos,type,data)
@ -148,31 +447,31 @@ function me.send_event(spos,type,data)
end
function me.get_network(pos)
for i,net in pairs(networks) do
if net.controller_pos then
if vector.equals(pos, net.controller_pos) then
return net,i
end
end
end
for i,net in pairs(networks) do
if net.controller_pos then
if vector.equals(pos, net.controller_pos) then
return net,i
end
end
end
end
dofile(path.."/ctrl.lua") -- Controller/wires
-- load networks
function me.load()
local f = io.open(me.worldpath.."/microexpansion_networks", "r")
if f then
local res = minetest.deserialize(f:read("*all"))
f:close()
if type(res) == "table" then
for _,n in pairs(res) do
local net = me.network.new(n)
net:load()
table.insert(me.networks,net)
end
end
end
local f = io.open(me.worldpath.."/microexpansion_networks", "r")
if f then
local res = minetest.deserialize(f:read("*all"))
f:close()
if type(res) == "table" then
for _,n in pairs(res) do
local net = me.network.new(n)
net:load()
table.insert(me.networks,net)
end
end
end
end
-- load now
@ -181,12 +480,17 @@ me.load()
-- save networks
function me.save()
local data = {}
for _,v in pairs(me.networks) do
table.insert(data,v:serialize())
for _,net in pairs(me.networks) do
-- 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
local f = io.open(me.worldpath.."/microexpansion_networks", "w")
f:write(minetest.serialize(data))
f:close()
local f = io.open(me.worldpath.."/microexpansion_networks", "w")
f:write(minetest.serialize(data))
f:close()
end
-- 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
-- [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
local finv, tinv = inv1.inv, inv2.inv
local fname, tname = inv1.name, inv2.name
@ -66,14 +66,14 @@ function microexpansion.move_inv(inv1, inv2, max)
end
if tinv and tinv:room_for_item(tname, v) then
if huge then
microexpansion.insert_item(v, tinv, tname)
microexpansion.insert_item(v, net, tinv, tname)
finv:remove_item(fname, v)
else
local leftover = tinv:add_item(tname, v)
local leftovers = tinv:add_item(tname, v)
finv:remove_item(fname, v)
if leftover and not(leftover:is_empty()) then
microexpansion.log("leftover items when transferring inventory","warning")
finv:add_item(fname, leftover)
if leftovers and not leftovers:is_empty() then
microexpansion.log("leftover items when transferring inventory", "warning")
finv:add_item(fname, leftovers)
end
end
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
if d.dpos then
if vector.equals(pos, d.dpos) then
return d,i
return d,i
end
end
end
@ -51,10 +51,10 @@ local function set_drive_controller(dpos,setd,cpos,i)
local dt = netdrives[i]
if dt then
if setd then
dt.dpos = dpos
dt.dpos = dpos
end
if cpos ~= nil then
dt.cpos = cpos
dt.cpos = cpos
end
else
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)
if dt then
if setd then
dt.dpos = dpos
dt.dpos = dpos
end
if cpos ~= nil then
dt.cpos = cpos
dt.cpos = cpos
end
else
table.insert(netdrives,{dpos = dpos, cpos = cpos})
@ -75,26 +75,26 @@ local function set_drive_controller(dpos,setd,cpos,i)
end
local function write_to_cell(cell, items, item_count)
local size = microexpansion.get_cell_size(cell:get_name())
local item_meta = cell:get_meta()
--print(dump2(items,"cell_items"))
item_meta:set_string("items", minetest.serialize(items))
local base_desc = minetest.registered_craftitems[cell:get_name()].microexpansion.base_desc
-- Calculate Percentage
local percent = math.floor(item_count / size * 100)
-- Update description
item_meta:set_string("description", base_desc.."\n"..
minetest.colorize("grey", tostring(item_count).."/"..tostring(size).." Items ("..tostring(percent).."%)"))
return cell
local size = microexpansion.get_cell_size(cell:get_name())
local item_meta = cell:get_meta()
--print(dump2(items,"cell_items"))
item_meta:set_string("items", minetest.serialize(items))
local base_desc = minetest.registered_craftitems[cell:get_name()].microexpansion.base_desc
-- Calculate Percentage
local percent = math.floor(item_count / size * 100)
-- Update description
item_meta:set_string("description", base_desc.."\n"..
minetest.colorize("grey", tostring(item_count).."/"..tostring(size).." Items ("..tostring(percent).."%)"))
return cell
end
local function write_drive_cells(pos,network)
local function write_drive_cells(pos, net)
local meta = minetest.get_meta(pos)
local own_inv = meta:get_inventory()
if network == nil then
if net == nil then
return false
end
local ctrl_inv = network:get_inventory()
local ctrl_inv = net:get_inventory()
local cells = {}
for i = 1, own_inv:get_size("main") do
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 cell_items = {}
net:update_counts()
if not net.counts then
net.counts = {}
end
for i = 1, ctrl_inv:get_size("main") do
local stack_inside = ctrl_inv:get_stack("main", i)
local item_string = stack_inside:to_string()
local stack = ctrl_inv:get_stack("main", i)
local item_string = stack:to_string()
if item_string ~= "" then
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
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
while item_count ~= 0 and cell_idx ~= nil do
--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
--print(("stack to store: %q"):format(table.concat(item_string," ")))
own_inv:set_stack("main", cell_idx, write_to_cell(cells[cell_idx],cell_items,items_in_cell_count))
cell_idx = next(cells, cell_idx)
if cell_idx == nil then
--there may be other drives within the network
microexpansion.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
-- TODO: This should fail if we write 64k items onto a 64k
-- drive (or larger). Fix by writting the number first, that's
-- the bias and then the name. This requires that no node in
-- the storage system starts with a number. Then in read,
-- support that.
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))
cell_idx = next(cells, cell_idx)
if cell_idx == nil then
--there may be other drives within the network
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
if cell_idx == nil then
@ -162,6 +205,7 @@ local function write_drive_cells(pos,network)
end
local function take_all(pos,net)
-- me.log("take_all", "error")
local meta = minetest.get_meta(pos)
local own_inv = meta:get_inventory()
local ctrl_inv = net:get_inventory()
@ -172,21 +216,25 @@ local function take_all(pos,net)
if name ~= "" then
local its = minetest.deserialize(stack:get_meta():get_string("items"))
for _,s in pairs(its) do
table.insert(items,s)
table.insert(items,s)
end
end
end
end
for _,ostack in pairs(items) do
--this returns 99 (max count) even if it removes more
ctrl_inv:remove_item("main", ostack)
print(ostack)
--ctrl_inv:remove_item("main", 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
net:update()
me.send_event(pos,"items")
end
local function add_all(pos,net)
-- me.log("add_all", "error")
local meta = minetest.get_meta(pos)
local own_inv = meta:get_inventory()
local ctrl_inv = net:get_inventory()
@ -197,23 +245,22 @@ local function add_all(pos,net)
if name ~= "" then
local its = minetest.deserialize(stack:get_meta():get_string("items"))
if its then
for _,s in pairs(its) do
table.insert(items,s)
end
for _,s in pairs(its) do
table.insert(items,s)
end
end
end
end
for _,ostack in pairs(items) do
me.insert_item(ostack, ctrl_inv, "main")
print(ostack)
end
for _,ostack in pairs(items) do
me.insert_item(ostack, net, ctrl_inv, "main")
end
net:update()
me.send_event(pos,"items",{net = net})
end
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)
if not fc.cpos then
return
@ -228,7 +275,7 @@ function me.disconnect_drive(pos,ncpos)
if fnet then
take_all(pos,fnet)
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
@ -240,141 +287,146 @@ local function update_drive(pos,_,ev)
local cnet = ev.net or me.get_connected_network(pos)
if cnet 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)
add_all(pos,cnet)
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)
add_all(pos,cnet)
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))
set_drive_controller(pos,false,cnet.controller_pos,i)
add_all(pos,cnet)
me.disconnect_drive(pos,cnet.controller_pos)
else
if ev.origin.name == "microexpansion:ctrl" then
me.disconnect_drive(pos,false)
me.disconnect_drive(pos,false)
end
end
else
if fc then
if fc.cpos then
me.disconnect_drive(pos,false)
end
elseif fc then
if fc.cpos then
me.disconnect_drive(pos,false)
end
end
end
-- [me chest] Register node
microexpansion.register_node("drive", {
description = "ME Drive",
usedfor = "Stores items into ME storage cells",
tiles = {
"chest_top",
"chest_top",
"chest_side",
"chest_side",
"chest_side",
"drive_full",
},
recipe = {
description = "ME Drive",
usedfor = "Stores items into ME storage cells",
tiles = {
"chest_top",
"chest_top",
"chest_side",
"chest_side",
"chest_side",
"drive_full",
},
recipe = {
{ 1, {
{"default:steel_ingot", "default:chest", "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" },
{"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 },
paramtype = "light",
paramtype2 = "facedir",
me_update = update_drive,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("formspec",
"size[9,9.5]"..
microexpansion.gui_bg ..
microexpansion.gui_slots ..
[[
label[0,-0.23;ME Drive]
list[context;main;0,0.3;8,4]
list[current_player;main;0,5.5;8,1;]
list[current_player;main;0,6.73;8,3;8]
listring[current_name;main]
listring[current_player;main]
field_close_on_enter[filter;false]
]])
local inv = meta:get_inventory()
inv:set_size("main", 10)
me.send_event(pos,"connect")
end,
can_dig = function(pos, player)
if minetest.is_protected(pos, player) then
return false
end
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv:is_empty("main")
end,
after_destruct = function(pos)
is_ground_content = false,
groups = { cracky = 1, me_connect = 1 },
paramtype = "light",
paramtype2 = "facedir",
me_update = update_drive,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("formspec",
"size[9,7.5]"..
microexpansion.gui_bg ..
microexpansion.gui_slots ..
[[
label[0,-0.23;ME Drive]
list[context;main;0,0.3;5,2]
list[current_player;main;0,3.5;8,1;]
list[current_player;main;0,4.73;8,3;8]
listring[current_name;main]
listring[current_player;main]
field_close_on_enter[filter;false]
]])
local inv = meta:get_inventory()
inv:set_size("main", 10)
me.send_event(pos,"connect")
end,
can_dig = function(pos, player)
if minetest.is_protected(pos, player) then
return false
end
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv:is_empty("main")
end,
after_destruct = function(pos)
me.send_event(pos,"disconnect")
end,
allow_metadata_inventory_put = function(_, _, _, stack)
if minetest.is_protected(pos, player) or minetest.get_item_group(stack:get_name(), "microexpansion_cell") == 0 then
return 0
else
return 1
end
end,
on_metadata_inventory_put = function(pos, _, _, stack)
me.send_event(pos,"item_cap")
local network = me.get_connected_network(pos)
if network == nil then
return
end
local ctrl_inv = network:get_inventory()
local items = minetest.deserialize(stack:get_meta():get_string("items"))
if items == nil then
print("no items")
me.send_event(pos,"items",{net=network})
return
end
network:set_storage_space(#items)
for _,s in pairs(items) do
me.insert_item(s, ctrl_inv, "main")
end
me.send_event(pos,"items",{net=network})
end,
allow_metadata_inventory_take = function(pos,_,_,stack, player) --args: pos, listname, index, stack, player
if minetest.is_protected(pos, player) then
return 0
end
local network = me.get_connected_network(pos)
write_drive_cells(pos,network)
return stack:get_count()
end,
on_metadata_inventory_take = function(pos, _, _, stack)
local network = me.get_connected_network(pos)
if network == nil then
return
end
me.send_event(pos,"item_cap",{net=network})
local ctrl_inv = network:get_inventory()
local items = minetest.deserialize(stack:get_meta():get_string("items"))
if items == nil then
network:update()
return
end
for _,ostack in pairs(items) do
--this returns 99 (max count) even if it removes more
ctrl_inv:remove_item("main", ostack)
end
--print(stack:to_string())
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
return 0
end
return 1
end,
on_metadata_inventory_put = function(pos, _, _, stack)
me.send_event(pos,"item_cap")
local network = me.get_connected_network(pos)
if network == nil then
return
end
local ctrl_inv = network:get_inventory()
local items = minetest.deserialize(stack:get_meta():get_string("items"))
if items == nil then
print("no items")
me.send_event(pos,"items",{net=network})
return
end
-- network:set_storage_space(#items)
for _,stack in pairs(items) do
network:set_storage_space(true)
me.insert_item(stack, network, ctrl_inv, "main")
end
network:set_storage_space(true)
me.send_event(pos,"items",{net=network})
end,
allow_metadata_inventory_take = function(pos,_,_,stack, player) --args: pos, listname, index, stack, player
if minetest.is_protected(pos, player) then
return 0
end
local network = me.get_connected_network(pos)
write_drive_cells(pos,network)
return stack:get_count()
end,
on_metadata_inventory_take = function(pos, _, _, stack)
local network = me.get_connected_network(pos)
if network == nil then
return
end
me.send_event(pos,"item_cap",{net=network})
local ctrl_inv = network:get_inventory()
local items = minetest.deserialize(stack:get_meta():get_string("items"))
if items == nil then
network:update()
return
end
for _,ostack in pairs(items) do
local postack = ItemStack(ostack)
-- 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()
me.send_event(pos,"items",{net=network})
end,
me.send_event(pos,"items",{net=network})
end,
})

@ -13,3 +13,18 @@ dofile(module_path.."/storage.lua")
-- Load machines
dofile(module_path.."/drive.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
-- [drive] 8k
-- [drive] 1k
microexpansion.register_cell("cell_1k", {
description = "1k ME Storage Cell",
capacity = 1000,
@ -16,7 +16,7 @@ microexpansion.register_cell("cell_1k", {
},
})
-- [drive] 8k
-- [drive] 2k
microexpansion.register_cell("cell_2k", {
description = "2k ME Storage Cell",
capacity = 2000,
@ -31,7 +31,7 @@ microexpansion.register_cell("cell_2k", {
},
})
-- [drive] 16k
-- [drive] 4k
microexpansion.register_cell("cell_4k", {
description = "4k ME Storage Cell",
capacity = 4000,
@ -44,7 +44,7 @@ microexpansion.register_cell("cell_4k", {
},
})
-- [drive] 16k
-- [drive] 8k
microexpansion.register_cell("cell_8k", {
description = "8k ME Storage Cell",
capacity = 8000,
@ -53,7 +53,7 @@ microexpansion.register_cell("cell_8k", {
},
})
-- [drive] 32k
-- [drive] 16k
microexpansion.register_cell("cell_16k", {
description = "16k ME Storage Cell",
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
local me = microexpansion
@ -5,136 +6,149 @@ 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,cp = me.get_connected_network(pos)
local list
local page_number = ""
local buttons = ""
local query = q or ""
local net,cp = me.get_connected_network(pos)
if cp then
if listname and 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
list = list .. [[
list[current_player;main;0,5.5;8,1;]
list[current_player;main;0,6.73;8,3;8]
listring[detached:]]..ctrlinvname..[[;main]
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]
]]
else
list = "label[3,2;" .. minetest.colorize("red", "No connected drives!") .. "]"
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
if cp then
if listname and 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,5.5;9,4;]
]]
else
list = list .. [[
list[current_player;main;0,5.5;8,1;]
list[current_player;main;0,6.73;8,3;8]
]]
end
list = list .. [[
listring[detached:]]..ctrlinvname..[[;main]
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]
]]
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,9.5]
]]..
microexpansion.gui_bg ..
microexpansion.gui_slots ..
list ..
[[
label[0,-0.23;ME Terminal]
field_close_on_enter[filter;false]
]]..
page_number ..
buttons
return [[
size[9,9.5]
]]..
microexpansion.gui_bg ..
microexpansion.gui_slots ..
list ..
[[
label[0,-0.23;ME Terminal]
field_close_on_enter[filter;false]
]]..
page_number ..
buttons
end
local function update_chest(pos,_,ev)
--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")
meta:set_string("formspec", chest_formspec(pos, 1, "main", page_max))
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")
meta:set_string("formspec", chest_formspec(pos, 1, "main", page_max))
end
-- [me chest] Register node
microexpansion.register_node("term", {
description = "ME Terminal",
usedfor = "Can interact with storage cells in ME networks",
tiles = {
"chest_top",
"chest_top",
"chest_side",
"chest_side",
"chest_side",
"chest_front",
},
recipe = {
me.register_node("term", {
description = "ME 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, {
{"default:steel_ingot", "default:chest", "default:steel_ingot"},
{"default:steel_ingot", "microexpansion:machine_casing", "default:steel_ingot"},
{"default:steel_ingot", "microexpansion:cable", "default:steel_ingot"},
{"default:steel_ingot", "default:chest", "default:steel_ingot"},
{"default:steel_ingot", "microexpansion:machine_casing", "default:steel_ingot"},
{"default:steel_ingot", "microexpansion:cable", "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_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)
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)
local own_inv = meta:get_inventory()
own_inv:set_size("src", 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")
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,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
-- TODO: Check capacity, only allow if room
return stack:get_count()
end,
on_metadata_inventory_put = function(pos, listname, _, stack)
local net = me.get_connected_network(pos)
local inv = net:get_inventory()
me.insert_item(stack, inv, "main")
me.insert_item(stack, net, inv, "main")
net:set_storage_space(true)
end,
on_metadata_inventory_take = function(pos, listname, _, stack)
local net = me.get_connected_network(pos)
local inv = net:get_inventory()
inv:remove_item("main", stack)
end,
tube = {
on_metadata_inventory_take = function(pos, listname, _, stack)
local net = me.get_connected_network(pos)
local inv = net:get_inventory()
me.remove_item(net, inv, "main", stack)
end,
tube = {
can_insert = function(pos, _, stack) --pos, node, stack, direction
local net = me.get_connected_network(pos)
local inv = net:get_inventory()
@ -144,13 +158,13 @@ microexpansion.register_node("term", {
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
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
@ -158,101 +172,100 @@ microexpansion.register_node("term", {
insert_object = function(pos, _, stack)
local net = me.get_connected_network(pos)
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)
--TODO: leftover
return ItemStack()
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,cp = me.get_connected_network(pos)
if net then
if cp then
microexpansion.log("network and ctrl_pos","info")
else
microexpansion.log("network but no ctrl_pos","warning")
end
else
if cp then
microexpansion.log("no network but ctrl_pos","warning")
else
microexpansion.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 cp then
ctrl_inv = net:get_inventory()
else
microexpansion.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()})
net:set_storage_space(pinv:get_size("main"))
local space = net:get_item_capacity()
on_receive_fields = function(pos, _, fields, sender)
local net,cp = me.get_connected_network(pos)
if net then
if cp then
me.log("network and ctrl_pos","info")
else
me.log("network but no ctrl_pos","warning")
end
else
if cp 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 cp 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
if not s:is_empty() then
space = space - s:get_count()
end
end
microexpansion.move_inv({ inv=pinv, name="main" }, { inv=ctrl_inv, name="main",huge=true }, space)
net:set_storage_space(true)
end
end,
me.move_inv(net, { inv=pinv, name="main" }, { inv=ctrl_inv, name="main", huge=true }, space)
net:set_storage_space(true)
end
end,
})