fixed lots of bugs

fixed too far inventory accesses by detaching the networks inventory
fixed vanishsing items with dynamic inventory size allocation
This commit is contained in:
theFox6 2020-03-01 08:23:39 +01:00
parent d0e2a070bd
commit 4c5b36fc88
No known key found for this signature in database
GPG Key ID: C884FE8D3BCE128A
8 changed files with 318 additions and 192 deletions

39
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

@ -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

@ -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",
},

@ -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

@ -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

@ -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,
})

@ -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")

@ -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,
})