hide digtron_id from clients, change "construct" to "assemble", various other tidbits

This commit is contained in:
FaceDeer 2019-08-20 00:29:26 -06:00
parent 03c9a81b6b
commit 2283cdbbea
3 changed files with 161 additions and 112 deletions

@ -2,6 +2,11 @@
local MP = minetest.get_modpath(minetest.get_current_modname()) local MP = minetest.get_modpath(minetest.get_current_modname())
local S, NS = dofile(MP.."/intllib.lua") local S, NS = dofile(MP.."/intllib.lua")
-- This allows us to know which digtron the player has a formspec open for without
-- sending the digtron_id over the network
local player_interacting_with_digtron_id = {}
local player_interacting_with_digtron_pos = {}
local controller_nodebox = { local controller_nodebox = {
type = "fixed", type = "fixed",
fixed = { fixed = {
@ -18,23 +23,24 @@ local controller_nodebox = {
}, },
} }
local get_controller_unconstructed_formspec = function(pos, player_name) local get_controller_unassembled_formspec = function(pos, player_name)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
return "size[9,9]" return "size[9,9]"
.. "container[0.5,0]" .. "container[0.5,0]"
.. "button[0,0;1,1;construct;Construct]" .. "button[0,0;1,1;assemble;Assemble]"
.. "field[1.2,0.25;2,1;digtron_name;Digtron name;"..meta:get_string("infotext").."]" .. "field[1.2,0.25;2,1;digtron_name;Digtron name;"..meta:get_string("infotext").."]"
.. "field_close_on_enter[digtron_name;false]" .. "field_close_on_enter[digtron_name;false]"
.. "container_end[]" .. "container_end[]"
end end
local get_controller_constructed_formspec = function(pos, digtron_id, player_name) local get_controller_assembled_formspec = function(pos, digtron_id, player_name)
digtron.retrieve_inventory(digtron_id) -- ensures the detatched inventory exists and is populated digtron.retrieve_inventory(digtron_id) -- ensures the detatched inventory exists and is populated
return "size[9,9]" return "size[9,9]"
.. "container[0.5,0]" .. "container[0.5,0]"
.. "button[0,0;1,1;deconstruct;Deconstruct]" .. "button[0,0;1,1;disassemble;Disassemble]"
.. "field[1.2,0.25;2,1;digtron_name;Digtron name;"..digtron.get_name(digtron_id).."]" .. "field[1.2,0.25;2,1;digtron_name;Digtron name;"..digtron.get_name(digtron_id).."]"
.. "field_close_on_enter[digtron_name;false]" .. "field_close_on_enter[digtron_name;false]"
.. "button[3,0;1,1;move_forward;Move forward]"
.. "container_end[]" .. "container_end[]"
.. "container[0.5,1]" .. "container[0.5,1]"
.. "list[detached:" .. digtron_id .. ";main;0,0;8,2]" -- TODO: paging system for inventory, guard against non-existent listname .. "list[detached:" .. digtron_id .. ";main;0,0;8,2]" -- TODO: paging system for inventory, guard against non-existent listname
@ -141,23 +147,26 @@ minetest.register_node("digtron:controller", {
meta:set_string("infotext", title) meta:set_string("infotext", title)
meta:set_string("digtron_id", digtron_id) meta:set_string("digtron_id", digtron_id)
meta:mark_as_private("digtron_id")
end, end,
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
if clicker == nil then return end
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local digtron_id = meta:get_string("digtron_id") local digtron_id = meta:get_string("digtron_id")
local player_name local player_name = clicker:get_player_name()
if clicker then player_name = clicker:get_player_name() end
if digtron_id == "" then if digtron_id == "" then
player_interacting_with_digtron_pos[player_name] = pos
minetest.show_formspec(player_name, minetest.show_formspec(player_name,
"digtron_controller_unconstructed:"..minetest.pos_to_string(pos)..":"..player_name, "digtron:controller_unassembled",
get_controller_unconstructed_formspec(pos, player_name)) get_controller_unassembled_formspec(pos, player_name))
else else
-- initialized -- initialized
player_interacting_with_digtron_id[player_name] = digtron_id
minetest.show_formspec(player_name, minetest.show_formspec(player_name,
"digtron_controller_constructed:"..minetest.pos_to_string(pos)..":"..player_name..":"..digtron_id, "digtron:controller_assembled",
get_controller_constructed_formspec(pos, digtron_id, player_name)) get_controller_assembled_formspec(pos, digtron_id, player_name))
end end
end, end,
@ -167,30 +176,26 @@ minetest.register_node("digtron:controller", {
on_blast = digtron.on_blast, on_blast = digtron.on_blast,
}) })
-- Dealing with an unconstructed Digtron controller -- Dealing with an unassembled Digtron controller
minetest.register_on_player_receive_fields(function(player, formname, fields) minetest.register_on_player_receive_fields(function(player, formname, fields)
local formname_split = formname:split(":") if formname ~= "digtron:controller_unassembled" then
if #formname_split ~= 3 or formname_split[1] ~= "digtron_controller_unconstructed" then
return
end
local pos = minetest.string_to_pos(formname_split[2])
if pos == nil then
minetest.log("error", "[Digtron] Unable to parse position from formspec name " .. formname)
return
end
local name = formname_split[3]
if player:get_player_name() ~= name then
return return
end end
local name = player:get_player_name()
local pos = player_interacting_with_digtron_pos[name]
if pos == nil then return end
if fields.construct then if fields.assemble then
local digtron_id = digtron.construct(pos, name) local digtron_id = digtron.assemble(pos, name)
if digtron_id then if digtron_id then
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
meta:set_string("digtron_id", digtron_id) meta:set_string("digtron_id", digtron_id)
meta:mark_as_private("digtron_id")
player_interacting_with_digtron_id[name] = digtron_id
minetest.show_formspec(name, minetest.show_formspec(name,
"digtron_controller_constructed:"..minetest.pos_to_string(pos)..":"..name..":"..digtron_id, "digtron:controller_assembled",
get_controller_constructed_formspec(pos, digtron_id, name)) get_controller_assembled_formspec(pos, digtron_id, name))
end end
end end
@ -203,39 +208,30 @@ end)
-- Controlling a fully armed and operational Digtron -- Controlling a fully armed and operational Digtron
minetest.register_on_player_receive_fields(function(player, formname, fields) minetest.register_on_player_receive_fields(function(player, formname, fields)
local formname_split = formname:split(":") if formname ~= "digtron:controller_assembled" then
if #formname_split ~= 4 or formname_split[1] ~= "digtron_controller_constructed" then
return return
end end
local pos = minetest.string_to_pos(formname_split[2]) local name = player:get_player_name()
if pos == nil then local digtron_id = player_interacting_with_digtron_id[name]
minetest.log("error", "[Digtron] Unable to parse position from formspec name " .. formname) if digtron_id == nil then return end
return
end
local name = formname_split[3]
if player:get_player_name() ~= name then
return
end
local digtron_id = formname_split[4]
if fields.deconstruct then if fields.disassemble then
minetest.chat_send_all("Deconstructing " .. digtron_id) local pos = digtron.disassemble(digtron_id, name)
if pos then
local meta = minetest.get_meta(pos) player_interacting_with_digtron_pos[name] = pos
local digtron_id = meta:get_string("digtron_id")
if digtron_id == "" then
minetest.log("error", "[Digtron] tried to deconstruct Digtron at pos "
.. minetest.pos_to_string(pos) .. " but it had no digtron_id in the node's metadata")
else
digtron.deconstruct(digtron_id, pos, name)
minetest.show_formspec(name, minetest.show_formspec(name,
"digtron_controller_unconstructed:"..minetest.pos_to_string(pos)..":"..name, "digtron:controller_unassembled",
get_controller_unconstructed_formspec(pos, name)) get_controller_unassembled_formspec(pos, name))
end end
end end
if fields.move_forward then
end
--TODO: this isn't recording the field when using ESC to exit the formspec --TODO: this isn't recording the field when using ESC to exit the formspec
if fields.key_enter_field == "digtron_name" or fields.digtron_name then if fields.key_enter_field == "digtron_name" or fields.digtron_name then
local pos = digtron.get_pos(digtron_id)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
meta:set_string("infotext", fields.digtron_name) meta:set_string("infotext", fields.digtron_name)
digtron.set_name(digtron_id, fields.digtron_name) digtron.set_name(digtron_id, fields.digtron_name)

@ -1,2 +1,3 @@
default default
intllib? intllib?
creative?

@ -3,6 +3,7 @@ local mod_meta = minetest.get_mod_storage()
digtron.layout = {} digtron.layout = {}
digtron.adjacent = {} digtron.adjacent = {}
digtron.bounding_box = {} digtron.bounding_box = {}
digtron.pos = {}
--minetest.debug(dump(mod_meta:to_table())) --minetest.debug(dump(mod_meta:to_table()))
@ -120,17 +121,17 @@ end)
-------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------
local create_new_id = function() local create_new_id = function()
local last_id = mod_meta:get_int("last_id") -- returns 0 when uninitialized, so 0 will never be a valid digtron_id. local digtron_id = "digtron" .. tostring(math.random(1, 2^21)) -- TODO: use SecureRandom()
local new_id = last_id + 1 -- It's super unlikely that we'll get a collision, but what the heck - maybe something will go
mod_meta:set_int("last_id", new_id) -- ensure each call to this method gets a unique number -- wrong with the random number source
while mod_meta:get_string(digtron_id..":layout") ~= "" do
local digtron_id = "digtron" .. tostring(new_id) digtron_id = "digtron" .. tostring(math.random(1, 2^21))
end
local inv = minetest.create_detached_inventory(digtron_id, detached_inventory_callbacks) local inv = minetest.create_detached_inventory(digtron_id, detached_inventory_callbacks)
return digtron_id, inv return digtron_id, inv
end end
-- Deletes a Digtron record. Note: just throws everything away, this is not digtron.deconstruct. -- Deletes a Digtron record. Note: just throws everything away, this is not digtron.disassemble.
local dispose_id = function(digtron_id) local dispose_id = function(digtron_id)
minetest.remove_detached_inventory(digtron_id) minetest.remove_detached_inventory(digtron_id)
digtron.layout[digtron_id] = nil digtron.layout[digtron_id] = nil
@ -140,17 +141,22 @@ local dispose_id = function(digtron_id)
mod_meta:set_string(digtron_id..":adjacent", "") mod_meta:set_string(digtron_id..":adjacent", "")
mod_meta:set_string(digtron_id..":name", "") mod_meta:set_string(digtron_id..":name", "")
mod_meta:set_string(digtron_id..":bounding_box", "") mod_meta:set_string(digtron_id..":bounding_box", "")
mod_meta:set_string(digtron_id..":pos", "")
end end
-------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------
-- Name -- Name
-- Not bothering with a dynamic table store for names, they're just strings with no need for serialization or deserialization
digtron.get_name = function(digtron_id) digtron.get_name = function(digtron_id)
return mod_meta:get_string(digtron_id..":name") return mod_meta:get_string(digtron_id..":name")
end end
digtron.set_name = function(digtron_id, digtron_name) digtron.set_name = function(digtron_id, digtron_name)
mod_meta:set_string(digtron_id..":name", digtron_name) -- Don't allow a name to be set for a non-existent Digtron
if mod_meta:get(digtron_id..":layout") then
mod_meta:set_string(digtron_id..":name", digtron_name)
end
end end
------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------
@ -186,6 +192,10 @@ local persist_adjacent = get_persist_table_function("adjacent")
local retrieve_adjacent = get_retrieve_table_function("adjacent") local retrieve_adjacent = get_retrieve_table_function("adjacent")
local persist_bounding_box = get_persist_table_function("bounding_box") local persist_bounding_box = get_persist_table_function("bounding_box")
local retrieve_bounding_box = get_retrieve_table_function("bounding_box") local retrieve_bounding_box = get_retrieve_table_function("bounding_box")
local persist_pos = get_persist_table_function("pos")
local retrieve_pos = get_retrieve_table_function("pos")
digtron.get_pos = retrieve_pos
local cardinal_directions = { local cardinal_directions = {
{x=1,y=0,z=0}, {x=1,y=0,z=0},
@ -235,32 +245,32 @@ get_all_adjacent_digtron_nodes = function(pos, digtron_nodes, digtron_adjacent,
end end
-------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------
-- Construct and deconstruct -- assemble and disassemble
local origin_hash = minetest.hash_node_position({x=0,y=0,z=0}) local origin_hash = minetest.hash_node_position({x=0,y=0,z=0})
-- Returns the id of the new Digtron record, or nil on failure -- Returns the id of the new Digtron record, or nil on failure
digtron.construct = function(root_pos, player_name) digtron.assemble = function(root_pos, player_name)
local node = minetest.get_node(root_pos) local node = minetest.get_node(root_pos)
-- TODO: a more generic test? Not needed with the more generic controller design, as far as I can tell. There's only going to be the one type of controller. -- TODO: a more generic test? Not needed with the more generic controller design, as far as I can tell. There's only going to be the one type of controller.
if node.name ~= "digtron:controller" then if node.name ~= "digtron:controller" then
-- Called on an incorrect node -- Called on an incorrect node
minetest.log("error", "[Digtron] digtron.construct called with pos " .. minetest.pos_to_string(root_pos) minetest.log("error", "[Digtron] digtron.assemble called with pos " .. minetest.pos_to_string(root_pos)
.. " but the node at this location was " .. node.name) .. " but the node at this location was " .. node.name)
return nil return nil
end end
local root_meta = minetest.get_meta(root_pos) local root_meta = minetest.get_meta(root_pos)
if root_meta:contains("digtron_id") then if root_meta:contains("digtron_id") then
-- Already constructed. TODO: validate that the digtron_id actually exists as well -- Already assembled. TODO: validate that the digtron_id actually exists as well
minetest.log("error", "[Digtron] digtron.construct called with pos " .. minetest.pos_to_string(root_pos) minetest.log("error", "[Digtron] digtron.assemble called with pos " .. minetest.pos_to_string(root_pos)
.. " but the controller at this location was already part of a constructed Digtron.") .. " but the controller at this location was already part of a assembled Digtron.")
return nil return nil
end end
local root_hash = minetest.hash_node_position(root_pos) local root_hash = minetest.hash_node_position(root_pos)
local digtron_nodes = {[root_hash] = node} -- Nodes that are part of Digtron. local digtron_nodes = {[root_hash] = node} -- Nodes that are part of Digtron.
-- Initialize with the controller, it won't be added by get_all_adjacent_digtron_nodes -- Initialize with the controller, it won't be added by get_all_adjacent_digtron_nodes
local digtron_adjacent = {} -- Nodes that are adjacent to Digtron but not a part of it local digtron_adjacent = {} -- Nodes that are adjacent to Digtron but not a part of it
local bounding_box = {minp=vector.new(root_pos), maxp=vector.new(root_pos), root = root_pos} local bounding_box = {minp=vector.new(root_pos), maxp=vector.new(root_pos)}
get_all_adjacent_digtron_nodes(root_pos, digtron_nodes, digtron_adjacent, bounding_box, player_name) get_all_adjacent_digtron_nodes(root_pos, digtron_nodes, digtron_adjacent, bounding_box, player_name)
local digtron_id, digtron_inv = create_new_id(root_pos) local digtron_id, digtron_inv = create_new_id(root_pos)
@ -280,7 +290,7 @@ digtron.construct = function(root_pos, player_name)
if current_meta_table.fields.digtron_id then if current_meta_table.fields.digtron_id then
-- Trying to incorporate part of an existing digtron, should be impossible. -- Trying to incorporate part of an existing digtron, should be impossible.
minetest.log("error", "[Digtron] digtron.construct tried to incorporate a Digtron node of type " minetest.log("error", "[Digtron] digtron.assemble tried to incorporate a Digtron node of type "
.. node.name .. " at " .. minetest.pos_to_string(minetest.get_position_from_hash(hash)) .. node.name .. " at " .. minetest.pos_to_string(minetest.get_position_from_hash(hash))
.. " that was already assigned to digtron id " .. current_meta_table.fields.digtron_id) .. " that was already assigned to digtron id " .. current_meta_table.fields.digtron_id)
dispose_id(digtron_id) dispose_id(digtron_id)
@ -313,17 +323,18 @@ digtron.construct = function(root_pos, player_name)
persist_layout(digtron_id, layout) persist_layout(digtron_id, layout)
persist_adjacent(digtron_id, digtron_adjacent) persist_adjacent(digtron_id, digtron_adjacent)
persist_bounding_box(digtron_id, bounding_box) persist_bounding_box(digtron_id, bounding_box)
persist_pos(digtron_id, root_pos)
-- Wipe out the inventories of all in-world nodes, it's stored in the mod_meta now. -- Wipe out the inventories of all in-world nodes, it's stored in the mod_meta now.
-- Wait until now to do it in case the above loop fails partway through. -- Wait until now to do it in case the above loop fails partway through.
for hash, node in pairs(digtron_nodes) do for hash, node in pairs(digtron_nodes) do
local digtron_meta local node_meta
if hash == root_hash then if hash == root_hash then
digtron_meta = root_meta -- we're processing the controller, we already have a reference to its meta node_meta = root_meta -- we're processing the controller, we already have a reference to its meta
else else
digtron_meta = minetest.get_meta(minetest.get_position_from_hash(hash)) node_meta = minetest.get_meta(minetest.get_position_from_hash(hash))
end end
local inv = digtron_meta:get_inventory() local inv = node_meta:get_inventory()
for listname, items in pairs(inv:get_lists()) do for listname, items in pairs(inv:get_lists()) do
for i = 1, #items do for i = 1, #items do
@ -331,10 +342,13 @@ digtron.construct = function(root_pos, player_name)
end end
end end
digtron_meta:set_string("digtron_id", digtron_id) node_meta:set_string("digtron_id", digtron_id)
node_meta:mark_as_private("digtron_id")
end end
minetest.log("action", "Digtron " .. digtron_id .. " assembled at " .. minetest.pos_to_string(root_pos)
.. " by " .. player_name)
minetest.debug("constructed id " .. digtron_id)
return digtron_id return digtron_id
end end
@ -342,38 +356,41 @@ end
-- Returns pos, node, and meta for the digtron node provided the in-world node matches the layout -- Returns pos, node, and meta for the digtron node provided the in-world node matches the layout
-- returns nil otherwise -- returns nil otherwise
local get_valid_data = function(digtron_id, root_hash, hash, data, function_name) local get_valid_data = function(digtron_id, root_hash, hash, data, function_name)
local ipos = minetest.get_position_from_hash(hash + root_hash - origin_hash) local node_pos = minetest.get_position_from_hash(hash + root_hash - origin_hash)
local node = minetest.get_node(ipos) local node = minetest.get_node(node_pos)
local imeta = minetest.get_meta(ipos) local node_meta = minetest.get_meta(node_pos)
local target_digtron_id = imeta:get_string("digtron_id") local target_digtron_id = node_meta:get_string("digtron_id")
if data.node.name ~= node.name then if data.node.name ~= node.name then
minetest.log("error", "[Digtron] " .. function_name .. " tried interacting with one of ".. digtron_id .. "'s " minetest.log("error", "[Digtron] " .. function_name .. " tried interacting with one of ".. digtron_id .. "'s "
.. data.node.name .. "s at " .. minetest.pos_to_string(ipos) .. " but the node at that location was of type " .. data.node.name .. "s at " .. minetest.pos_to_string(node_pos) .. " but the node at that location was of type "
.. node.name) .. node.name)
return return
elseif target_digtron_id ~= digtron_id then elseif target_digtron_id ~= digtron_id then
if target_digtron_id ~= "" then if target_digtron_id ~= "" then
minetest.log("error", "[Digtron] " .. function_name .. " tried interacting with ".. digtron_id .. "'s " minetest.log("error", "[Digtron] " .. function_name .. " tried interacting with ".. digtron_id .. "'s "
.. data.node.name .. " at " .. minetest.pos_to_string(ipos) .. data.node.name .. " at " .. minetest.pos_to_string(node_pos)
.. " but the node at that location had a non-matching digtron_id value of \"" .. " but the node at that location had a non-matching digtron_id value of \""
.. target_digtron_id .. "\"") .. target_digtron_id .. "\"")
return return
else else
-- Allow digtron to recover from bad map metadata writes, the bane of Digtron 1.0's existence -- Allow digtron to recover from bad map metadata writes, the bane of Digtron 1.0's existence
minetest.log("warning", "[Digtron] " .. function_name .. " tried interacting with ".. digtron_id .. "'s " minetest.log("warning", "[Digtron] " .. function_name .. " tried interacting with ".. digtron_id .. "'s "
.. data.node.name .. " at " .. minetest.pos_to_string(ipos) .. data.node.name .. " at " .. minetest.pos_to_string(node_pos)
.. " but the node at that location had no digtron_id in its metadata. " .. " but the node at that location had no digtron_id in its metadata. "
.. "Since the node type matched the layout, however, it was included anyway. It's possible " .. "Since the node type matched the layout, however, it was included anyway. It's possible "
.. "its metadata was not written correctly by a previous Digtron activity.") .. "its metadata was not written correctly by a previous Digtron activity.")
return ipos, node, imeta return node_pos, node, node_meta
end end
end end
return ipos, node, imeta return node_pos, node, node_meta
end end
-- Turns the Digtron back into pieces -- Turns the Digtron back into pieces
digtron.deconstruct = function(digtron_id, root_pos, player_name) digtron.disassemble = function(digtron_id, player_name)
local bbox = retrieve_bounding_box(digtron_id)
local root_pos = retrieve_pos(digtron_id)
local root_meta = minetest.get_meta(root_pos) local root_meta = minetest.get_meta(root_pos)
root_meta:set_string("infotext", digtron.get_name(digtron_id)) root_meta:set_string("infotext", digtron.get_name(digtron_id))
@ -381,8 +398,8 @@ digtron.deconstruct = function(digtron_id, root_pos, player_name)
local inv = digtron.retrieve_inventory(digtron_id) local inv = digtron.retrieve_inventory(digtron_id)
if not (layout and inv) then if not (layout and inv) then
minetest.log("error", "digtron.deconstruct was unable to find either layout or inventory record for " .. digtron_id minetest.log("error", "digtron.disassemble was unable to find either layout or inventory record for " .. digtron_id
.. ", deconstruction was impossible. Clearing any other remaining data for this id.") .. ", disassembly was impossible. Clearing any other remaining data for this id.")
dispose_id(digtron_id) dispose_id(digtron_id)
return return
end end
@ -391,15 +408,15 @@ digtron.deconstruct = function(digtron_id, root_pos, player_name)
-- Write metadata and inventory to in-world node at this location -- Write metadata and inventory to in-world node at this location
for hash, data in pairs(layout) do for hash, data in pairs(layout) do
local ipos, node, imeta = get_valid_data(digtron_id, root_hash, hash, data, "digtron.deconstruct") local node_pos, node, node_meta = get_valid_data(digtron_id, root_hash, hash, data, "digtron.disassemble")
if ipos then if node_pos then
local iinv = imeta:get_inventory() local node_inv = node_meta:get_inventory()
for listname, size in pairs(data.meta.inventory) do for listname, size in pairs(data.meta.inventory) do
iinv:set_size(listname, size) node_inv:set_size(listname, size)
for i, itemstack in ipairs(inv:get_list(listname)) do for i, itemstack in ipairs(inv:get_list(listname)) do
-- add everything, putting leftovers back in the main inventory -- add everything, putting leftovers back in the main inventory
inv:set_stack(listname, i, iinv:add_item(listname, itemstack)) inv:set_stack(listname, i, node_inv:add_item(listname, itemstack))
end end
end end
@ -407,15 +424,17 @@ digtron.deconstruct = function(digtron_id, root_pos, player_name)
-- Ensure node metadata fields are all set, too -- Ensure node metadata fields are all set, too
for field, value in pairs(data.meta.fields) do for field, value in pairs(data.meta.fields) do
imeta:set_string(field, value) node_meta:set_string(field, value)
end end
-- Clear digtron_id, this node is no longer part of an active digtron -- Clear digtron_id, this node is no longer part of an active digtron
imeta:set_string("digtron_id", "") node_meta:set_string("digtron_id", "")
end end
end end
dispose_id(digtron_id) dispose_id(digtron_id)
return root_pos
end end
-- Removes the in-world nodes of a digtron -- Removes the in-world nodes of a digtron
@ -435,9 +454,9 @@ digtron.remove_from_world = function(digtron_id, root_pos, player_name)
local root_hash = minetest.hash_node_position(root_pos) local root_hash = minetest.hash_node_position(root_pos)
local nodes_to_destroy = {} local nodes_to_destroy = {}
for hash, data in pairs(layout) do for hash, data in pairs(layout) do
local ipos, node, imeta = get_valid_data(digtron_id, root_hash, hash, data, "digtron.destroy") local node_pos, node, node_meta = get_valid_data(digtron_id, root_hash, hash, data, "digtron.destroy")
if ipos then if node_pos then
table.insert(nodes_to_destroy, ipos) table.insert(nodes_to_destroy, node_pos)
end end
end end
@ -452,12 +471,12 @@ digtron.build_to_world = function(digtron_id, root_pos, player_name)
local permitted = true local permitted = true
for hash, data in pairs(layout) do for hash, data in pairs(layout) do
local ipos = minetest.get_position_from_hash(hash + root_hash - origin_hash) local node_pos = minetest.get_position_from_hash(hash + root_hash - origin_hash)
local node = minetest.get_node(ipos) local node = minetest.get_node(node_pos)
local node_def = minetest.registered_nodes[node.name] local node_def = minetest.registered_nodes[node.name]
-- TODO: lots of testing needed here -- TODO: lots of testing needed here
if not (node_def and node_def.buildable_to) then if not (node_def and node_def.buildable_to) then
minetest.chat_send_all("not permitted due to " .. node.name .. " at " .. minetest.pos_to_string(ipos)) minetest.chat_send_all("not permitted due to " .. node.name .. " at " .. minetest.pos_to_string(node_pos))
permitted = false permitted = false
break break
end end
@ -466,22 +485,23 @@ digtron.build_to_world = function(digtron_id, root_pos, player_name)
if permitted then if permitted then
-- TODO: voxelmanip might be better here, less likely than with destroy though since metadata needs to be written -- TODO: voxelmanip might be better here, less likely than with destroy though since metadata needs to be written
for hash, data in pairs(layout) do for hash, data in pairs(layout) do
local ipos = minetest.get_position_from_hash(hash + root_hash - origin_hash) local node_pos = minetest.get_position_from_hash(hash + root_hash - origin_hash)
minetest.set_node(ipos, data.node) minetest.set_node(node_pos, data.node)
local meta = minetest.get_meta(ipos) local meta = minetest.get_meta(node_pos)
meta:set_string("digtron_id", digtron_id)
for field, value in pairs(data.meta.fields) do for field, value in pairs(data.meta.fields) do
meta:set_string(field, value) meta:set_string(field, value)
end end
-- Not needed - local inventories not used by active digtron, will be restored if deconstructed meta:set_string("digtron_id", digtron_id)
meta:mark_as_private("digtron_id")
-- Not needed - local inventories not used by active digtron, will be restored if disassembled
-- local inv = meta:get_inventory() -- local inv = meta:get_inventory()
-- for listname, size in pairs(data.meta.inventory) do -- for listname, size in pairs(data.meta.inventory) do
-- inv:set_size(listname, size) -- inv:set_size(listname, size)
-- end -- end
end end
local bbox = retrieve_bounding_box(digtron_id) local bbox = retrieve_bounding_box(digtron_id)
bbox.root = root_pos
persist_bounding_box(digtron_id, bbox) persist_bounding_box(digtron_id, bbox)
persist_pos(digtron_id, root_pos)
end end
return permitted return permitted
@ -503,12 +523,13 @@ digtron.can_dig = function(pos, digger)
local node = minetest.get_node(pos) local node = minetest.get_node(pos)
local bbox = retrieve_bounding_box(digtron_id) local bbox = retrieve_bounding_box(digtron_id)
local root_pos = retrieve_pos(digtron_id)
local layout = retrieve_layout(digtron_id) local layout = retrieve_layout(digtron_id)
if bbox == nil or bbox.root == nil or layout == nil then if bbox == nil or root_pos == nil or layout == nil then
-- Somehow, this belongs to a digtron id that's missing information that should exist in persistence. -- Somehow, this belongs to a digtron id that's missing information that should exist in persistence.
local missing = "" local missing = ""
if bbox == nil then missing = missing .. "bounding_box " end if bbox == nil then missing = missing .. "bounding_box " end
if bbox ~= nil and bbox.root == nil then missing = missing .. "root_pos " end if root_pos == nil then missing = missing .. "root_pos " end
if layout == nil then missing = missing .. "layout " end if layout == nil then missing = missing .. "layout " end
minetest.log("error", "[Digtron] can_dig was called on a " .. node.name .. " at location " minetest.log("error", "[Digtron] can_dig was called on a " .. node.name .. " at location "
@ -520,7 +541,7 @@ digtron.can_dig = function(pos, digger)
return true return true
end end
local root_hash = minetest.hash_node_position(bbox.root) local root_hash = minetest.hash_node_position(root_pos)
local here_hash = minetest.hash_node_position(pos) local here_hash = minetest.hash_node_position(pos)
local layout_hash = here_hash - root_hash + origin_hash local layout_hash = here_hash - root_hash + origin_hash
local layout_data = layout[layout_hash] local layout_data = layout[layout_hash]
@ -550,13 +571,10 @@ digtron.on_blast = function(pos, intensity)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local digtron_id = meta:get_string("digtron_id") local digtron_id = meta:get_string("digtron_id")
if digtron_id ~= "" then if digtron_id ~= "" then
local bbox = retrieve_bounding_box(digtron_id) if not digtron.disassemble(digtron_id, "an explosion") then
if bbox.root then
digtron.deconstruct(digtron_id, bbox.root, "an explosion")
else
minetest.log("error", "[Digtron] a digtron node at " .. minetest.pos_to_string(pos) minetest.log("error", "[Digtron] a digtron node at " .. minetest.pos_to_string(pos)
.. " was hit by an explosion and had digtron_id " .. digtron_id .. " was hit by an explosion and had digtron_id " .. digtron_id
.. " but didn't have a root position recorded, so it could not be deconstructed.") .. " but didn't have a root position recorded, so it could not be disassembled.")
return return
end end
end end
@ -569,3 +587,37 @@ digtron.on_blast = function(pos, intensity)
minetest.remove_node(pos) minetest.remove_node(pos)
return drops return drops
end end
------------------------------------------------------------------------------------
-- Creative trash
-- This is wrapped in an after() call as a workaround for to https://github.com/minetest/minetest/issues/8827
if minetest.get_modpath("creative") then
minetest.after(1, function()
if minetest.get_inventory({type="detached", name="creative_trash"}) then
if minetest.remove_detached_inventory("creative_trash") then
-- Create the trash field
local trash = minetest.create_detached_inventory("creative_trash", {
-- Allow the stack to be placed and remove it in on_put()
-- This allows the creative inventory to restore the stack
allow_put = function(inv, listname, index, stack, player)
return stack:get_count()
end,
on_put = function(inv, listname, index, stack, player)
local stack = inv:get_stack(listname, index)
local stack_meta = stack:get_meta()
local digtron_id = stack_meta:get_string("digtron_id")
if digtron_id ~= "" then
minetest.log("action", player:get_player_name() .. " disposed of " .. digtron_id
.. " in the creative inventory's trash receptacle.")
dispose_id(digtron_id)
end
inv:set_list(listname, {})
end,
})
trash:set_size("main", 1)
end
end
end)
end