diff --git a/api.lua b/api.lua index 17c343d..62a9462 100644 --- a/api.lua +++ b/api.lua @@ -125,10 +125,11 @@ function microexpansion.get_node(pos) return minetest.get_node(pos) end -function microexpansion.update_node(pos) - local node = microexpansion.get_node(pos) +function microexpansion.update_node(pos,event) + local node = microexpansion.get_node(pos) local def = minetest.registered_nodes[node.name] - if def.me_update then - def.me_update(pos,node) + local ev = event or {type = "n/a"} + if def.me_update then + def.me_update(pos,node,ev) end end diff --git a/doc/network/events.md b/doc/network/events.md new file mode 100644 index 0000000..1df711f --- /dev/null +++ b/doc/network/events.md @@ -0,0 +1,14 @@ +`me_update(pos,event)` reveives an event table, +it may contain the following values: +* type: string can be: + * n/a event type was not given + * connect a new machine was added to the network + * disconnect a machine was or will be removed from the network + * item_cap the item capacity if the network changed + * items the items within the network changed +* origin: table + * pos: table (vector) the position of the sender + * name: string the itemstring of the sender + * type: string the machine type of the sender +* net: network (optional) +* payload: any, mostly table diff --git a/modules/network/ctrl.lua b/modules/network/ctrl.lua index 374f7e0..667b6f7 100644 --- a/modules/network/ctrl.lua +++ b/modules/network/ctrl.lua @@ -3,6 +3,8 @@ local me = microexpansion local network = me.network +--FIXME: accept multiple controllers in one network + -- [register node] Controller me.register_node("ctrl", { description = "ME Controller", @@ -42,38 +44,48 @@ me.register_node("ctrl", { }, groups = { cracky = 1, me_connect = 1, }, connect_sides = "nobottom", - me_update = function(pos) + me_update = function(pos,_,ev) 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, + on_construct = function(pos) + local meta = minetest.get_meta(pos) + local net = network.new({controller_pos = pos}) + table.insert(me.networks,net) + me.send_event(pos,"connect",{net=net}) + + 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) - table.insert(me.networks,network.new({controller_pos = pos})) - me.update_connected_machines(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) - if net then - net:destruct() - end - if idx then - table.remove(me.networks,idx) - end - me.update_connected_machines(pos) + local net = me.get_network(pos) + local net,idx = me.get_network(pos) + --disconnect all those who need the network + me.send_event(pos,"disconnect",{net=net}) + if net then + net:destruct() + end + if idx then + table.remove(me.networks,idx) + end + --disconnect all those that haven't realized the network is gone + me.send_event(pos,"disconnect") end, - after_dig_node = function(pos) - me.update_connected_machines(pos) + after_destruct = function(pos) + --disconnect all those that haven't realized the controller was disconnected + me.send_event(pos,"disconnect") end, machine = { - type = "transporter", + type = "controller", }, }) @@ -102,9 +114,18 @@ me.register_machine("cable", { }, paramtype = "light", groups = { crumbly = 1, }, - after_place_node = me.update_connected_machines, - after_dig_node = me.update_connected_machines, - me_update = function(pos) + --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") @@ -113,6 +134,6 @@ me.register_machine("cable", { end end, machine = { - type = "transporter", + type = "conductor", }, }) diff --git a/modules/network/init.lua b/modules/network/init.lua index c907796..40a5769 100644 --- a/modules/network/init.lua +++ b/modules/network/init.lua @@ -107,13 +107,35 @@ function me.get_connected_network(start_pos) end end -function me.update_connected_machines(start_pos) - print("updating connected machines") - for npos in me.connected_nodes(start_pos) do - me.update_node(npos) +function me.update_connected_machines(start_pos,event,include_start) + minetest.log("action","updating connected machines") + local ev = event or {type = "n/a"} + local sn = microexpansion.get_node(start_pos) + local sd = minetest.registered_nodes[sn.name] + local sm = sd.machine or {} + ev.origin = { + pos = start_pos, + name = sn.name, + type = sm.type + } + --print(dump2(ev,"event")) + for npos in me.connected_nodes(start_pos) do + if include_start or not vector.equals(npos,start_pos) then + me.update_node(npos,ev) + end end end +function me.send_event(spos,type,data) + local d = data or {} + local event = { + type = type, + net = d.net, + payload = d.payload + } + me.update_connected_machines(spos,event,false) +end + function me.get_network(pos) for i,net in pairs(networks) do if net.controller_pos then diff --git a/modules/network/network.lua b/modules/network/network.lua index 98766ed..95c9568 100644 --- a/modules/network/network.lua +++ b/modules/network/network.lua @@ -294,7 +294,7 @@ function network:serialize() end function network:destruct() + minetest.remove_detached_inventory(self:get_inventory_name()) 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 8c52fd3..040be80 100644 --- a/modules/storage/drive.lua +++ b/modules/storage/drive.lua @@ -2,8 +2,72 @@ local me = microexpansion -local function update_drive(pos) - --FIXME: check if we got connected/disconnected and reroute items +local netdrives + +-- load drives +local function load_drives() + local f = io.open(me.worldpath.."/microexpansion_drives", "r") + if f then + netdrives = minetest.deserialize(f:read("*all")) or {} + f:close() + if type(res) == "table" then + for _,d in pairs(res) do + table.insert(netdrives,d) + end + end + end +end + +-- load now +load_drives() + +-- save drives +local function save_drives() + local f = io.open(me.worldpath.."/microexpansion_drives", "w") + f:write(minetest.serialize(netdrives)) + f:close() +end + +-- save on server shutdown +minetest.register_on_shutdown(save_drives) + +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 + end + end + end + return --false,#netdrives+1 +end + +local function set_drive_controller(dpos,setd,cpos,i) + if i then + local dt = netdrives[i] + if dt then + if setd then + dt.dpos = dpos + end + if cpos ~= nil then + dt.cpos = cpos + end + else + netdrives[i] = {dpos = dpos, cpos = cpos} + end + else + local dt = get_drive_controller(dpos) + if dt then + if setd then + dt.dpos = dpos + end + if cpos ~= nil then + dt.cpos = cpos + end + else + table.insert(netdrives,{dpos = dpos, cpos = cpos}) + end + end end local function write_to_cell(cell, items, item_count) @@ -20,6 +84,179 @@ local function write_to_cell(cell, items, item_count) return cell end +local function write_drive_cells(pos,network) --args: pos, listname, index, stack, player + local meta = minetest.get_meta(pos) + local own_inv = meta:get_inventory() + if network == nil then + return false + end + 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 + cells[i] = cell + end + end + local cell_idx = next(cells) + if cell_idx == nil then + return + end + local size = microexpansion.get_cell_size(cells[cell_idx]:get_name()) + local items_in_cell_count = 0 + local cell_items = {} + + for i = 1, ctrl_inv:get_size("main") do + local stack_inside = ctrl_inv:get_stack("main", i) + local stack_name = stack_inside:get_name() + if stack_name ~= "" then + local item_count = stack_inside:get_count() + while item_count ~= 0 and cell_idx ~= nil do + --print(("stack to store: %s %i"):format(stack_name,item_count)) + if size < items_in_cell_count + item_count then + local space = size - items_in_cell_count + table.insert(cell_items,("%s %i"):format(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)) + cell_idx = next(cells, cell_idx) + if cell_idx == nil then + --there may be other drives within the network + minetest.log("info","too many items to store in drive") + 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 + table.insert(cell_items, ("%s %i"):format(stack_name,item_count)) + item_count = 0 + end + end + end + if cell_idx == nil then + break + end + end + while cell_idx ~= nil do + 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) + end + + return true +end + +local function take_all(pos,net) + local meta = minetest.get_meta(pos) + local own_inv = meta:get_inventory() + local ctrl_inv = net:get_inventory() + local items = {} + for i = 1, own_inv:get_size("main") do + local stack = own_inv:get_stack("main", i) + local name = stack:get_name() + if name ~= "" then + local its = minetest.deserialize(stack:get_meta():get_string("items")) + for _,s in pairs(its) do + table.insert(items,s) + 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) + end + + net:update() + me.send_event(pos,"items") +end + +local function add_all(pos,net) + local meta = minetest.get_meta(pos) + local own_inv = meta:get_inventory() + local ctrl_inv = net:get_inventory() + local items = {} + for i = 1, own_inv:get_size("main") do + local stack = own_inv:get_stack("main", i) + local name = stack:get_name() + 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 + end + end + end + for _,ostack in pairs(items) do + me.insert_item(ostack, ctrl_inv, "main") + print(ostack) + end + + net:update() + me.send_event(pos,"items",{net = net}) +end + +function me.disconnect_drive(pos,ncpos) + minetest.log("action","disconnecting drive at "..minetest.pos_to_string(pos)) + local fc,i = get_drive_controller(pos) + if not fc.cpos then + return + end + local fnet = me.get_network(fc.cpos) + write_drive_cells(pos,fnet) + if ncpos then + set_drive_controller(pos,false,ncpos,i) + else + set_drive_controller(pos,false,false,i) + end + if fnet then + take_all(pos,fnet) + else + minetest.log("warning","drive couldn't take items from its former network") + end +end + +local function update_drive(pos,_,ev) + if ev.type~="connect" and ev.type~="disconnect" then + return + end + local fc,i = get_drive_controller(pos) + local cnet = ev.net or me.get_connected_network(pos) + if cnet then + if not fc then + minetest.log("action","connecting drive at "..minetest.pos_to_string(pos)) + set_drive_controller(pos,true,cnet.controller_pos,i) + add_all(pos,cnet) + elseif not fc.cpos then + minetest.log("action","connecting drive at "..minetest.pos_to_string(pos)) + set_drive_controller(pos,false,cnet.controller_pos,i) + add_all(pos,cnet) + elseif not vector.equals(fc.cpos,cnet.controller_pos) then + minetest.log("action","reconnecting drive at "..minetest.pos_to_string(pos)) + 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) + end + end + else + if fc then + if fc.cpos then + me.disconnect_drive(pos,false) + end + end + end +end + -- [me chest] Register node microexpansion.register_node("drive", { description = "ME Drive", @@ -62,12 +299,16 @@ microexpansion.register_node("drive", { ]]) local inv = meta:get_inventory() inv:set_size("main", 10) + me.send_event(pos,"connect") end, can_dig = function(pos) 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.get_item_group(stack:get_name(), "microexpansion_cell") ~= 0 then return 1 @@ -76,7 +317,7 @@ microexpansion.register_node("drive", { end end, on_metadata_inventory_put = function(pos, _, _, stack) - me.update_connected_machines(pos) + me.send_event(pos,"item_cap") local network = me.get_connected_network(pos) if network == nil then return @@ -85,77 +326,18 @@ microexpansion.register_node("drive", { local items = minetest.deserialize(stack:get_meta():get_string("items")) if items == nil then print("no items") - me.update_connected_machines(pos) + 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.update_connected_machines(pos) + me.send_event(pos,"items",{net=network}) end, allow_metadata_inventory_take = function(pos,_,_,stack) --args: pos, listname, index, stack, player - local meta = minetest.get_meta(pos) - local own_inv = meta:get_inventory() - local network = me.get_connected_network(pos) - if network == nil then - return stack:get_count() - end - 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 - 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 = {} - 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) - local stack_name = stack_inside:get_name() - if stack_name ~= "" then - local item_count = stack_inside:get_count() - while item_count ~= 0 and cell_idx ~= nil do - --print(("stack to store: %s %i"):format(stack_name,item_count)) - if size < items_in_cell_count + item_count then - local space = size - items_in_cell_count - table.insert(cell_items,("%s %i"):format(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)) - cell_idx = next(cells, cell_idx) - size = microexpansion.get_cell_size(cells[cell_idx]:get_name()) - items_in_cell_count = 0 - cell_items = {} - item_count = item_count - space - 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 - items_in_cell_count = items_in_cell_count + item_count - table.insert(cell_items, ("%s %i"):format(stack_name,item_count)) - item_count = 0 - end - end - end - if cell_idx == nil then - break - end - end - while cell_idx ~= nil do - 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) - 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) @@ -163,11 +345,11 @@ microexpansion.register_node("drive", { 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() - me.update_connected_machines(pos) return end for _,ostack in pairs(items) do @@ -177,6 +359,6 @@ microexpansion.register_node("drive", { --print(stack:to_string()) network:update() - me.update_connected_machines(pos) + me.send_event(pos,"items",{net=network}) end, }) diff --git a/modules/storage/terminal.lua b/modules/storage/terminal.lua index f33cf2c..2873b89 100644 --- a/modules/storage/terminal.lua +++ b/modules/storage/terminal.lua @@ -63,7 +63,9 @@ local function chest_formspec(pos, start_id, listname, page_max, q) buttons end -local function update_chest(pos) +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 @@ -112,10 +114,14 @@ microexpansion.register_node("term", { meta:set_string("inv_name", "none") meta:set_int("page", 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, on_metadata_inventory_take = function(pos, listname, _, stack) if listname == "search" then local net = me.get_connected_network(pos) diff --git a/textures/microexpansion_cell_2k.png b/textures/microexpansion_cell_2k.png new file mode 100644 index 0000000..6189751 Binary files /dev/null and b/textures/microexpansion_cell_2k.png differ