diff --git a/api.lua b/api.lua index cc19b8f..79c9a65 100644 --- a/api.lua +++ b/api.lua @@ -1,8 +1,6 @@ -- microexpansion/api.lua local BASENAME = "microexpansion" ---FIXME: this is very full of bad coding - -- [function] Register Recipe function microexpansion.register_recipe(output, recipe) -- Check if disabled @@ -10,38 +8,13 @@ function microexpansion.register_recipe(output, recipe) return end - local function isint(n) - return n==math.floor(n) - end - - local function get_amount(_) - if isint(recipe[_][1]) then - return recipe[_][1] - else return 1 end - end - - local function get_type(_) - if type(recipe[_][2]) == "string" then - return recipe[_][2] - end - end - - local function register(_) + for _,r in ipairs(recipe) do local def = { - type = get_type(_), - output = output.." "..tostring(get_amount(_)), - recipe = recipe[_][3] or recipe[_][2] - } - minetest.register_craft(def) - end - - -- Check if disabled - if recipe.disabled == true then - return - end - - for i in ipairs(recipe) do - register(i) + type = type(r[2]) == "string" and r[2], + output = output.." "..(r[1] or 1), + recipe = r[3] or r[2] + } + minetest.register_craft(def) end end diff --git a/init.lua b/init.lua index 3bd010b..b23e2da 100644 --- a/init.lua +++ b/init.lua @@ -46,7 +46,7 @@ end -- [function] Load module (overrides modules.conf) function microexpansion.load_module(name) - if loaded_modules[name] ~= false then + if not loaded_modules[name] then local module_path = microexpansion.get_module_path(name) if module_path then diff --git a/modules/network/ctrl.lua b/modules/network/ctrl.lua index 652c190..90c8c62 100644 --- a/modules/network/ctrl.lua +++ b/modules/network/ctrl.lua @@ -3,59 +3,6 @@ local me = microexpansion local network = me.network -local function update_ctrl(pos) - local cnetwork = me.get_network(pos) - if cnetwork == nil then - minetest.log("error","no network for ctrl at pos "..minetest.pos_to_string(pos)) - return - end - local size = cnetwork:get_item_capacity() - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - inv:set_size("main", me.int_to_stacks(size)) -end - -function me.insert_item(stack, inv, listname) - if me.settings.huge_stacks == false then - inv:add_item(listname, stack) - return - end - local stack_name - local stack_count - if type(stack) == "string" then - local split_string = stack:split(" ") - stack_name = split_string[1] - if (#split_string > 1) then - stack_count = tonumber(split_string[2]) - else - stack_count = 1 - end - else - stack_name = stack:get_name() - stack_count = stack:get_count() - end - local found = false - for i = 0, inv:get_size(listname) do - local inside = inv:get_stack(listname, i) - if inside:get_name() == stack_name then - local total_count = inside:get_count() + stack_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 - minetest.log("error"," adding items to stack in microexpansion network failed") - print("stack is now " .. inside:to_string()) - end - inv:set_stack(listname, i, inside) - found = true - break; - end - end - end - if not found then - inv:add_item(listname, stack) - end -end - -- [register node] Controller me.register_node("ctrl", { description = "ME Controller", @@ -87,11 +34,18 @@ me.register_node("ctrl", { }, groups = { cracky = 1, me_connect = 1, }, connect_sides = "nobottom", - me_update = update_ctrl, + me_update = function(pos) + local cnet = me.get_network(pos) + if cnet == nil then + minetest.log("error","no network for ctrl at pos "..minetest.pos_to_string(pos)) + return + end + cnet:update() + end, after_place_node = function(pos, player) local name = player:get_player_name() local meta = minetest.get_meta(pos) - table.insert(me.networks,network:new({controller_pos = pos})) + table.insert(me.networks,network.new({controller_pos = pos})) me.update_connected_machines(pos) meta:set_string("infotext", "Network Controller (owned by "..name..")") @@ -100,48 +54,15 @@ me.register_node("ctrl", { on_destruct = function(pos) local net,idx = me.get_network(pos) if net then - net.controller_pos = nil + net:destruct() end if idx then table.remove(me.networks,idx) end - end, - after_dig_node = function(pos) me.update_connected_machines(pos) end, - allow_metadata_inventory_put = function(pos, listname, index, stack) - local inv = minetest.get_meta(pos):get_inventory() - local inside_stack = inv:get_stack(listname, index) - local stack_name = stack:get_name() - -- improve performance by skipping unnessecary calls - if inside_stack:get_name() ~= stack_name or inside_stack:get_count() >= inside_stack:get_stack_max() then - if inv:get_stack(listname, index+1):get_name() ~= "" then - return stack:get_count() - end - end - local max_slots = inv:get_size(listname) - local max_items = math.floor(max_slots * 99) - - 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 - return math.max(math.min(stack:get_count(),max_items-items),0) - end, - on_metadata_inventory_put = function(pos, listname, _, stack) - local inv = minetest.get_meta(pos):get_inventory() - inv:remove_item(listname, stack) - me.insert_item(stack, inv, listname) - end, - allow_metadata_inventory_take = function(_, _, _, stack) - return math.min(stack:get_count(),stack:get_stack_max()) + after_dig_node = function(pos) + me.update_connected_machines(pos) end, machine = { type = "transporter", @@ -169,6 +90,14 @@ me.register_machine("cable", { groups = { crumbly = 1, }, after_place_node = me.update_connected_machines, after_dig_node = me.update_connected_machines, + me_update = function(pos) + 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 = "transporter", }, diff --git a/modules/network/init.lua b/modules/network/init.lua index 7ce7d42..0693b9d 100644 --- a/modules/network/init.lua +++ b/modules/network/init.lua @@ -3,14 +3,53 @@ me.networks = {} local networks = me.networks local path = microexpansion.get_module_path("network") --- load Resources +function me.insert_item(stack, inv, listname) + if me.settings.huge_stacks == false then + inv:add_item(listname, stack) + return + end + local stack_name + local stack_count + if type(stack) == "string" then + local split_string = stack:split(" ") + stack_name = split_string[1] + if (#split_string > 1) then + stack_count = tonumber(split_string[2]) + else + stack_count = 1 + end + else + stack_name = stack:get_name() + stack_count = stack:get_count() + end + local found = false + for i = 0, inv:get_size(listname) do + local inside = inv:get_stack(listname, i) + if inside:get_name() == stack_name then + local total_count = inside:get_count() + stack_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 + minetest.log("error"," adding items to stack in microexpansion network failed") + print("stack is now " .. inside:to_string()) + end + inv:set_stack(listname, i, inside) + found = true + break; + end + end + end + if not found then + inv:add_item(listname, stack) + end +end dofile(path.."/network.lua") -- Network Management -- generate iterator to find all connected nodes -function me.connected_nodes(start_pos) +function me.connected_nodes(start_pos,include_ctrl) -- nodes to be checked - local open_list = {start_pos} + local open_list = {{pos = start_pos}} -- nodes that were checked local closed_set = {} -- local connected nodes function to reduce table lookups @@ -20,42 +59,42 @@ function me.connected_nodes(start_pos) -- start looking for next pos local open = false -- pos to be checked - local current_pos + local current -- find next unclosed while not open do -- get unchecked pos - current_pos = table.remove(open_list) + current = table.remove(open_list) -- none are left - if current_pos == nil then return end + 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 + if vector.equals(closed,current.pos) then --found one was closed open = false end end end -- get all connected nodes - local next_pos = adjacent_connected_nodes(current_pos) + local nodes = adjacent_connected_nodes(current.pos,include_ctrl) -- iterate through them - for _,p in pairs(next_pos) do + for _,n in pairs(nodes) do -- mark position to be checked - table.insert(open_list,p) + table.insert(open_list,n) end -- add this one to the closed set - table.insert(closed_set,current_pos) + table.insert(closed_set,current.pos) -- return the one to be checked - return current_pos + return current.pos,current.name end end -- get network connected to position function me.get_connected_network(start_pos) - for npos in me.connected_nodes(start_pos) do - if me.get_node(npos).name == "microexpansion:ctrl" then + 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 @@ -85,12 +124,15 @@ dofile(path.."/ctrl.lua") -- Controller/wires -- load networks function me.load() - local res = io.open(me.worldpath.."/microexpansion.txt", "r") - if res then - res = minetest.deserialize(res:read("*all")) + 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.networks) do - table.insert(networks,me.network:new(n)) + for _,n in pairs(res) do + local net = me.network.new(n) + net:load() + table.insert(me.networks,net) end end end @@ -101,11 +143,13 @@ me.load() -- save networks function me.save() - local data = { - networks = networks, - } - - io.open(me.worldpath.."/microexpansion.txt", "w"):write(minetest.serialize(data)) + local data = {} + for _,v in pairs(me.networks) do + table.insert(data,v:serialize()) + end + local f = io.open(me.worldpath.."/microexpansion_networks", "w") + f:write(minetest.serialize(data)) + f:close() end -- save on server shutdown diff --git a/modules/network/network.lua b/modules/network/network.lua index f4f063c..783a696 100644 --- a/modules/network/network.lua +++ b/modules/network/network.lua @@ -13,17 +13,28 @@ microexpansion.network = network -- @function [parent=#network] new -- @param #table o the object to become a network or nil -- @return #table the new network object -function network:new(o) - return setmetatable(o or {}, {__index = self}) +function network.new(o) + return setmetatable(o or {}, {__index = network}) end --- check if a node can be connected -- @function [parent=#network] can_connect --- @param #table pos the position of the node to be checked +-- @param #table np the position of the node to be checked +-- the node itself or the name of the node -- @return #boolean whether this node has the group me_connect -function network.can_connect(pos) - local node = microexpansion.get_node(pos) - return minetest.get_item_group(node.name, "me_connect") > 0 +function network.can_connect(np) + local nn + if type(np)=="string" then + nn = np + else + if np.name then + nn = np.name + else + local node = microexpansion.get_node(np) + nn = node.name + end + end + return minetest.get_item_group(nn, "me_connect") > 0 end --- get all adjacent connected nodes @@ -44,15 +55,17 @@ function network.adjacent_connected_nodes(pos, include_ctrl) local nodes = {} for _,apos in pairs(adjacent) do - if network.can_connect(apos) then - if include_ctrl == false then - if not microexpansion.get_node(apos).name == "microexpansion:ctrl" then - table.insert(nodes, apos) - end - else - table.insert(nodes, apos) + local napos = microexpansion.get_node(apos) + local nn = napos.name + if network.can_connect(nn) then + if include_ctrl == false then + if nn ~= "microexpansion:ctrl" then + table.insert(nodes,{pos = apos, name = nn}) end + else + table.insert(nodes,{pos = apos, name = nn}) end + end end return nodes @@ -125,5 +138,148 @@ function network:get_item_capacity() cap = cap + get_drive_capacity(npos) end end + self.capacity_cache = cap return cap end + +function network:add_storage_slots(count,listname) + local c = count or 1 + local ln = listname or "main" + local inv = self:get_inventory() + local csize = inv:get_size(ln) + local space = 0 + local inside = 0 + local contents = inv:get_list(ln) or {} + for _,s in pairs(contents) do + if s:is_empty() then + space = space + 1 + else + inside = inside + s:get_count() + end + end + local cap = self:get_item_capacity() + --the capacity is allocated outside of the condition because it updates the cached capacity + if count == true then + if inside < cap then + c = 1 + else + c = 0 + end + end + local needed = c - space + needed = needed + csize + --TODO allow list removal + if needed == 0 then + needed = 1 + end + inv:set_size(ln, needed) +end + +--FIXME: add size removal function because items are removed when size decreases + +function network:update() + self:add_storage_slots(true) +end + +function network:get_inventory_name() + local cp = self.controller_pos + assert(cp, "trying to get inventory name of a network without controller") + return "microexpansion_storage_"..cp.x.."_"..cp.y.."_"..cp.z +end + +local function create_inventory(net) + local invname = net:get_inventory_name() + net.inv = minetest.create_detached_inventory(invname, { + allow_put = function(inv, listname, index, stack) + local inside_stack = inv:get_stack(listname, index) + local stack_name = stack:get_name() + -- improve performance by skipping unnessecary calls + if inside_stack:get_name() ~= stack_name or inside_stack:get_count() >= inside_stack:get_stack_max() then + if inv:get_stack(listname, index+1):get_name() ~= "" then + return stack:get_count() + end + end + local max_slots = inv:get_size(listname) + 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 + return math.max(math.min(stack:get_count(),max_items-items),0) + end, + on_put = function(inv, listname, _, stack) + inv:remove_item(listname, stack) + microexpansion.insert_item(stack, inv, listname) + net:add_storage_slots(true) + end, + allow_take = function(_, _, _, stack) + return math.min(stack:get_count(),stack:get_stack_max()) + end, + on_take = function() + net:add_storage_slots(true) + end + }) +end + +function network:get_inventory() + if not self.inv then + create_inventory(self) + assert(self.inv,"no inventory created") + end + return self.inv +end + +function network:load_inventory(lists) + local inv = self:get_inventory() + for listname,c in pairs(lists) do + inv:set_size(listname, #c) + for i,s in pairs(c) do + inv:set_stack(listname,i,s) + end + end +end + +function network:save_inventory() + local contents = {} + local lists = self.inv:get_lists() + for listname,c in pairs(lists or {}) do + local ct = {} + contents[listname] = ct + for i,stack in pairs(c) do + ct[i] = stack:to_string() + end + end + return contents +end + +function network:load() + if self.strinv then + self:load_inventory(self.strinv) + end +end + +function network:serialize() + local sert = {} + for i,v in pairs(self) do + if i == "inv" then + sert.strinv = self:save_inventory() + else + sert[i] = v + end + end + return sert +end + +function network:destruct() + self.controller_pos = nil + self.inv = nil + minetest.remove_detached_inventory(self:get_inventory_name()) +end diff --git a/modules/storage/drive.lua b/modules/storage/drive.lua index 429a2fe..64104df 100644 --- a/modules/storage/drive.lua +++ b/modules/storage/drive.lua @@ -9,6 +9,7 @@ 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 @@ -68,48 +69,44 @@ microexpansion.register_node("drive", { end, on_metadata_inventory_put = function(pos, _, _, stack) me.update_connected_machines(pos) - local network,cp = me.get_connected_network(pos) + local network = me.get_connected_network(pos) if network == nil then return end - local ctrl_meta = minetest.get_meta(cp) - local ctrl_inv = ctrl_meta:get_inventory() + local ctrl_inv = network:get_inventory() local items = minetest.deserialize(stack:get_meta():get_string("items")) if items == nil then print("no items") me.update_connected_machines(pos) return end + network:add_storage_slots(#items) for _,s in pairs(items) do me.insert_item(s, ctrl_inv, "main") end me.update_connected_machines(pos) end, allow_metadata_inventory_take = function(pos,_,_,stack) --args: pos, listname, index, stack, player - --FIXME sometimes items vanish if one cell is filled local meta = minetest.get_meta(pos) local own_inv = meta:get_inventory() - local network,cp = me.get_connected_network(pos) + local network = me.get_connected_network(pos) if network == nil then return stack:get_count() end - local ctrl_meta = minetest.get_meta(cp) - local ctrl_inv = ctrl_meta:get_inventory() + local ctrl_inv = network:get_inventory() local cells = {} for i = 1, own_inv:get_size("main") do local cell = own_inv:get_stack("main", i) local name = cell:get_name() if name ~= "" then - table.insert(cells, i, cell) + cells[i] = cell end end local cell_idx = next(cells) + local size = microexpansion.get_cell_size(cells[cell_idx]:get_name()) local items_in_cell_count = 0 local cell_items = {} - if cell_idx == nil then - minetest.log("warning","too many items to store in drive") - return stack:get_count() - end + assert(cell_idx,"cannot take a cell from an empty drive") for i = 1, ctrl_inv:get_size("main") do local stack_inside = ctrl_inv:get_stack("main", i) @@ -117,18 +114,19 @@ microexpansion.register_node("drive", { if stack_name ~= "" then local item_count = stack_inside:get_count() while item_count ~= 0 and cell_idx ~= nil do - local size = microexpansion.get_cell_size(cells[cell_idx]:get_name()) if size < items_in_cell_count + item_count then - local rest = size - items_in_cell_count - item_count = item_count - rest - table.insert(cell_items,stack_name.." "..rest) - items_in_cell_count = items_in_cell_count + rest + local space = size - items_in_cell_count + item_count = item_count - space + table.insert(cell_items,stack_name.." "..space) + 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)) - items_in_cell_count = 0 - cell_items = {} cell_idx = next(cells, cell_idx) - if cell_idx == nil then + size = microexpansion.get_cell_size(cells[cell_idx]:get_name()) + items_in_cell_count = 0 + cell_items = {} + if cell_idx == nil then + --there may be other drives within the network minetest.log("info","too many items to store in drive") end else @@ -152,14 +150,14 @@ microexpansion.register_node("drive", { return stack:get_count() end, on_metadata_inventory_take = function(pos, _, _, stack) - local network,cp = me.get_connected_network(pos) + local network = me.get_connected_network(pos) if network == nil then return end - local ctrl_meta = minetest.get_meta(cp) - local ctrl_inv = ctrl_meta:get_inventory() + local ctrl_inv = network:get_inventory() local items = minetest.deserialize(stack:get_meta():get_string("items")) if items == nil then + network:update() me.update_connected_machines(pos) return end @@ -169,6 +167,7 @@ microexpansion.register_node("drive", { end print(stack:to_string()) + network:update() me.update_connected_machines(pos) end, }) diff --git a/modules/storage/init.lua b/modules/storage/init.lua index 470d8fa..96a9364 100644 --- a/modules/storage/init.lua +++ b/modules/storage/init.lua @@ -11,5 +11,5 @@ dofile(module_path.."/api.lua") dofile(module_path.."/storage.lua") -- Load machines -dofile(module_path.."/chest.lua") dofile(module_path.."/drive.lua") +dofile(module_path.."/terminal.lua") diff --git a/modules/storage/terminal.lua b/modules/storage/terminal.lua index fb223d1..15b24cf 100644 --- a/modules/storage/terminal.lua +++ b/modules/storage/terminal.lua @@ -12,15 +12,17 @@ local function chest_formspec(pos, start_id, listname, page_max, q) if cp then if listname and net:get_item_capacity() > 0 then + local ctrlinvname = net:get_inventory_name() if listname == "main" then - list = "list[nodemeta:"..cp.x..","..cp.y..","..cp.z..";" .. listname .. ";0,0.3;8,4;" .. (start_id - 1) .. "]" + 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[nodemeta:]]..cp.x..","..cp.y..","..cp.z..[[;main] + listring[detached:]]..ctrlinvname..[[;main] listring[current_player;main] ]] buttons = [[ @@ -76,6 +78,9 @@ local function update_chest(pos) meta:set_string("formspec", chest_formspec(pos, 1, "main", page_max)) end +--FIXME: items inserted in a search inventory vanish +--TODO: add a main inv that transfers to the network + -- [me chest] Register node microexpansion.register_node("term", { description = "ME Terminal", @@ -105,26 +110,44 @@ microexpansion.register_node("term", { end, on_metadata_inventory_take = function(pos, listname, _, stack) if listname == "search" then - local _,cp = me.get_connected_network(pos) - local inv = minetest.get_meta(cp):get_inventory() + local net = me.get_connected_network(pos) + local inv = net:get_inventory() inv:remove_item("main", stack) end end, on_receive_fields = function(pos, _, fields, sender) - local _,cp = me.get_connected_network(pos) + local net,cp = me.get_connected_network(pos) + if net then + if cp then + minetest.log("none","network and ctrl_pos") + else + minetest.log("warning","network but no ctrl_pos") + end + else + if cp then + minetest.log("warning","no network but ctrl_pos") + else + minetest.log("info","no network and no ctrl_pos") + 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 = minetest.get_meta(cp):get_inventory() + ctrl_inv = net:get_inventory() + else + minetest.log("warning","no network connected") + 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 @@ -168,7 +191,9 @@ microexpansion.register_node("term", { 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:add_storage_slots(pinv:get_size("main")) microexpansion.move_inv({ inv=pinv, name="main" }, { inv=ctrl_inv, name="main" }) + net:add_storage_slots(true) end end, })