From d659cb38b32640656071c4cd2eddcbd97af36bf3 Mon Sep 17 00:00:00 2001 From: thetaepsilon-gamedev Date: Mon, 18 Dec 2017 22:07:53 +0000 Subject: item_transport.lua: read extra multimode parameter from go_next callback --- item_transport.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/item_transport.lua b/item_transport.lua index 551db07..8a92bdb 100644 --- a/item_transport.lua +++ b/item_transport.lua @@ -277,7 +277,7 @@ luaentity.register_entity("pipeworks:tubed_item", { end if moved then - local found_next, new_velocity = go_next(self.start_pos, velocity, stack, self.owner) -- todo: color + local found_next, new_velocity, multimode = go_next(self.start_pos, velocity, stack, self.owner) -- todo: color local rev_vel = vector.multiply(velocity, -1) local rev_dir = vector.direction(self.start_pos,vector.add(self.start_pos,rev_vel)) local rev_node = minetest.get_node(vector.round(vector.add(self.start_pos,rev_dir))) -- cgit v1.2.3 From 28a3ba44d165cce068491b90233a58f3cea4d1c2 Mon Sep 17 00:00:00 2001 From: thetaepsilon-gamedev Date: Mon, 18 Dec 2017 22:38:24 +0000 Subject: item_transport.lua: initial support in item luaentity for multiple outputs and trajectories from go_next() --- item_transport.lua | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/item_transport.lua b/item_transport.lua index 8a92bdb..64e4624 100644 --- a/item_transport.lua +++ b/item_transport.lua @@ -197,6 +197,12 @@ minetest.register_entity("pipeworks:color_entity", { on_activate = luaentity.on_activate, }) +-- see below for usage: +-- determine if go_next returned a multi-mode set. +local is_multimode = function(v) + return (type(v) == "table") and (v.__multimode) +end + luaentity.register_entity("pipeworks:tubed_item", { itemstring = '', item_entity = nil, @@ -297,6 +303,15 @@ luaentity.register_entity("pipeworks:tubed_item", { self:setpos(vector.subtract(self.start_pos, vector.multiply(vel, moved_by - 1))) self:setvelocity(velocity) end + elseif is_multimode(multimode) then + -- create new stacks according to returned data. + local s = self.start_pos + for _, split in ipairs(multimode) do + pipeworks.tube_inject_item(s, s, split.velocity, split.itemstack, self.owner) + end + -- remove ourself now the splits are sent + self:remove() + return end if new_velocity and not vector.equals(velocity, new_velocity) then -- cgit v1.2.3 From 56362cdd2acd8e855ceb79b46ce0b62f205776da Mon Sep 17 00:00:00 2001 From: thetaepsilon-gamedev Date: Mon, 18 Dec 2017 23:07:40 +0000 Subject: item_transport.lua: factor out tube overload code into separate function --- item_transport.lua | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/item_transport.lua b/item_transport.lua index 64e4624..cf9e5c4 100644 --- a/item_transport.lua +++ b/item_transport.lua @@ -50,6 +50,26 @@ minetest.register_globalstep(function(dtime) end end) + + +-- tube overload mechanism: +-- when the tube's item count (tracked in the above tube_item_count table) +-- exceeds the limit configured per tube, replace it with a broken one. +local crunch_tube = function(pos, cnode, cmeta) + if enable_max_limit then + local h = minetest.hash_node_position(pos) + local itemcount = tube_item_count[h] or 0 + if itemcount > max_tube_limit then + cmeta:set_string("the_tube_was", minetest.serialize(cnode)) + print("[Pipeworks] Warning - a tube at "..minetest.pos_to_string(pos).." broke due to too many items ("..itemcount..")") + minetest.swap_node(pos, {name = "pipeworks:broken_tube_1"}) + pipeworks.scan_for_tube_objects(pos) + end + end +end + + + local function go_next(pos, velocity, stack, owner) local next_positions = {} local max_priority = 0 @@ -96,16 +116,7 @@ local function go_next(pos, velocity, stack, owner) end end - if enable_max_limit then - local h = minetest.hash_node_position(pos) - local itemcount = tube_item_count[h] or 0 - if itemcount > max_tube_limit then - cmeta:set_string("the_tube_was", minetest.serialize(cnode)) - print("[Pipeworks] Warning - a tube at "..minetest.pos_to_string(pos).." broke due to too many items ("..itemcount..")") - minetest.swap_node(pos, {name = "pipeworks:broken_tube_1"}) - pipeworks.scan_for_tube_objects(pos) - end - end + crunch_tube(pos, cnode, cmeta) if not next_positions[1] then return false, nil -- cgit v1.2.3 From 755dd262086da5469489d74c919e6870870eca57 Mon Sep 17 00:00:00 2001 From: thetaepsilon-gamedev Date: Tue, 19 Dec 2017 18:17:22 +0000 Subject: item_transport.lua: move tube limit checking before can_go() callback in go_next() --- item_transport.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/item_transport.lua b/item_transport.lua index cf9e5c4..154b6db 100644 --- a/item_transport.lua +++ b/item_transport.lua @@ -89,6 +89,9 @@ local function go_next(pos, velocity, stack, owner) speed = 1 end vel.speed = speed + + crunch_tube(pos, cnode, cmeta) + if minetest.registered_nodes[cnode.name] and minetest.registered_nodes[cnode.name].tube and minetest.registered_nodes[cnode.name].tube.can_go then can_go = minetest.registered_nodes[cnode.name].tube.can_go(pos, cnode, vel, stack) else @@ -116,8 +119,6 @@ local function go_next(pos, velocity, stack, owner) end end - crunch_tube(pos, cnode, cmeta) - if not next_positions[1] then return false, nil end -- cgit v1.2.3 From 97522b6a244a34be9e384c151618ab8db74aea46 Mon Sep 17 00:00:00 2001 From: thetaepsilon-gamedev Date: Tue, 19 Dec 2017 18:38:02 +0000 Subject: item_transport.lua: update return statements in go_next() to include multimode parameter --- item_transport.lua | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/item_transport.lua b/item_transport.lua index 154b6db..d0b0e63 100644 --- a/item_transport.lua +++ b/item_transport.lua @@ -70,6 +70,15 @@ end +-- function called by the on_step callback of the pipeworks tube luaentity. +-- the routine is passed the current node position, velocity, itemstack, +-- and owner name. +-- returns three values: +-- * a boolean "found destination" status; +-- * a new velocity vector that the tubed item should use, or nil if not found; +-- * a "multi-mode" data table (or nil if N/A) where a stack was split apart. +-- if this is not nil, the luaentity spawns new tubed items for each new fragment stack, +-- then deletes itself (i.e. the original item stack). local function go_next(pos, velocity, stack, owner) local next_positions = {} local max_priority = 0 @@ -120,7 +129,7 @@ local function go_next(pos, velocity, stack, owner) end if not next_positions[1] then - return false, nil + return false, nil, nil end local n = (cmeta:get_int("tubedir") % (#next_positions)) + 1 @@ -128,9 +137,11 @@ local function go_next(pos, velocity, stack, owner) cmeta:set_int("tubedir", n) end local new_velocity = vector.multiply(next_positions[n].vect, vel.speed) - return true, new_velocity + return true, new_velocity, nil end + + minetest.register_entity("pipeworks:tubed_item", { initial_properties = { hp_max = 1, -- cgit v1.2.3 From fb91ba53f1af1d5d618f7610cdd04e3895ebd6c6 Mon Sep 17 00:00:00 2001 From: thetaepsilon-gamedev Date: Tue, 19 Dec 2017 22:53:18 +0000 Subject: item_transport.lua: read cycle direction at start of go_next() in preparation for refactoring --- item_transport.lua | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/item_transport.lua b/item_transport.lua index d0b0e63..72f6d7d 100644 --- a/item_transport.lua +++ b/item_transport.lua @@ -100,6 +100,11 @@ local function go_next(pos, velocity, stack, owner) vel.speed = speed crunch_tube(pos, cnode, cmeta) + -- cycling of outputs: + -- an integer counter is kept in each pipe's metadata, + -- which allows tracking which output was previously chosen. + -- note reliance on get_int returning 0 for uninitialised. + local cycledir = cmeta:get_int("tubedir") if minetest.registered_nodes[cnode.name] and minetest.registered_nodes[cnode.name].tube and minetest.registered_nodes[cnode.name].tube.can_go then can_go = minetest.registered_nodes[cnode.name].tube.can_go(pos, cnode, vel, stack) @@ -132,7 +137,9 @@ local function go_next(pos, velocity, stack, owner) return false, nil, nil end - local n = (cmeta:get_int("tubedir") % (#next_positions)) + 1 + local n = (cycledir % (#next_positions)) + 1 + -- if not using output cycling, + -- don't update the field so it stays the same for the next item. if pipeworks.enable_cyclic_mode then cmeta:set_int("tubedir", n) end -- cgit v1.2.3 From a2e183c6f3d76e809bbc2d6b596e7e000f06ee66 Mon Sep 17 00:00:00 2001 From: thetaepsilon-gamedev Date: Tue, 19 Dec 2017 23:15:44 +0000 Subject: item_transport.lua: split out determination of next position in go_next() into separate function --- item_transport.lua | 95 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 35 deletions(-) diff --git a/item_transport.lua b/item_transport.lua index 72f6d7d..7219f7e 100644 --- a/item_transport.lua +++ b/item_transport.lua @@ -70,47 +70,24 @@ end --- function called by the on_step callback of the pipeworks tube luaentity. --- the routine is passed the current node position, velocity, itemstack, --- and owner name. --- returns three values: --- * a boolean "found destination" status; --- * a new velocity vector that the tubed item should use, or nil if not found; --- * a "multi-mode" data table (or nil if N/A) where a stack was split apart. --- if this is not nil, the luaentity spawns new tubed items for each new fragment stack, --- then deletes itself (i.e. the original item stack). -local function go_next(pos, velocity, stack, owner) +-- compatibility behaviour for the existing can_go() callbacks, +-- which can only specify a list of possible positions. +local function go_next_compat(pos, cnode, cmeta, cycledir, vel, stack, owner) local next_positions = {} local max_priority = 0 - local cnode = minetest.get_node(pos) - local cmeta = minetest.get_meta(pos) local can_go - local speed = math.abs(velocity.x + velocity.y + velocity.z) - if speed == 0 then - speed = 1 - end - local vel = {x = velocity.x/speed, y = velocity.y/speed, z = velocity.z/speed,speed=speed} - if speed >= 4.1 then - speed = 4 - elseif speed >= 1.1 then - speed = speed - 0.1 - else - speed = 1 - end - vel.speed = speed - - crunch_tube(pos, cnode, cmeta) - -- cycling of outputs: - -- an integer counter is kept in each pipe's metadata, - -- which allows tracking which output was previously chosen. - -- note reliance on get_int returning 0 for uninitialised. - local cycledir = cmeta:get_int("tubedir") if minetest.registered_nodes[cnode.name] and minetest.registered_nodes[cnode.name].tube and minetest.registered_nodes[cnode.name].tube.can_go then can_go = minetest.registered_nodes[cnode.name].tube.can_go(pos, cnode, vel, stack) else can_go = pipeworks.notvel(adjlist, vel) end + -- can_go() is expected to return an array-like table of candidate offsets. + -- for each one, look at the node at that offset and determine if it can accept the item. + -- also note the prioritisation: + -- if any tube is found with a greater priority than previously discovered, + -- then the valid positions are reset and and subsequent positions under this are skipped. + -- this has the effect of allowing only equal priorities to co-exist. for _, vect in ipairs(can_go) do local npos = vector.add(pos, vect) pipeworks.load_position(npos) @@ -133,18 +110,66 @@ local function go_next(pos, velocity, stack, owner) end end + -- indicate not found if no valid rules were picked up, + -- and don't change the counter. if not next_positions[1] then - return false, nil, nil + return cycledir, false, nil, nil end + -- otherwise rotate to the next output direction and return that local n = (cycledir % (#next_positions)) + 1 + local new_velocity = vector.multiply(next_positions[n].vect, vel.speed) + return n, true, new_velocity, nil +end + + + + +-- function called by the on_step callback of the pipeworks tube luaentity. +-- the routine is passed the current node position, velocity, itemstack, +-- and owner name. +-- returns three values: +-- * a boolean "found destination" status; +-- * a new velocity vector that the tubed item should use, or nil if not found; +-- * a "multi-mode" data table (or nil if N/A) where a stack was split apart. +-- if this is not nil, the luaentity spawns new tubed items for each new fragment stack, +-- then deletes itself (i.e. the original item stack). +local function go_next(pos, velocity, stack, owner) + local cnode = minetest.get_node(pos) + local cmeta = minetest.get_meta(pos) + local speed = math.abs(velocity.x + velocity.y + velocity.z) + if speed == 0 then + speed = 1 + end + local vel = {x = velocity.x/speed, y = velocity.y/speed, z = velocity.z/speed,speed=speed} + if speed >= 4.1 then + speed = 4 + elseif speed >= 1.1 then + speed = speed - 0.1 + else + speed = 1 + end + vel.speed = speed + + crunch_tube(pos, cnode, cmeta) + -- cycling of outputs: + -- an integer counter is kept in each pipe's metadata, + -- which allows tracking which output was previously chosen. + -- note reliance on get_int returning 0 for uninitialised. + local cycledir = cmeta:get_int("tubedir") + + -- pulled out and factored out into go_next_compat() above. + -- n is the new value of the cycle counter. + -- XXX: this probably needs cleaning up after being split out, + -- seven args is a bit too many + local n, found, new_velocity, multimode = go_next_compat(pos, cnode, cmeta, cycledir, vel, stack, owner) + -- if not using output cycling, -- don't update the field so it stays the same for the next item. if pipeworks.enable_cyclic_mode then cmeta:set_int("tubedir", n) end - local new_velocity = vector.multiply(next_positions[n].vect, vel.speed) - return true, new_velocity, nil + return found, new_velocity, multimode end -- cgit v1.2.3 From 57fc8c67f827a41ab1278fd0d25b2566032da61a Mon Sep 17 00:00:00 2001 From: thetaepsilon-gamedev Date: Fri, 22 Dec 2017 17:13:50 +0000 Subject: teleport_tube.lua: fix 32-bit clamping issues on some systems for %d in string.format Some servers running ubuntu in particular were reporting issues with teleport tubes not working. On investigation, all tube entries were colliding as string.format("%d", ...) was returning either -2^31 or 2^31-1 depending on system bit width, causing hash entries to be overwritten. This is possibly related to the use of C sprintf within lua. Fix this by using %g instead to interpret as double without int conversion, with a large enough number of digits such that all possible 2^48 values from minetest.hash_node_position() can be correctly serialised. --- teleport_tube.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/teleport_tube.lua b/teleport_tube.lua index bb364db..9a66de2 100644 --- a/teleport_tube.lua +++ b/teleport_tube.lua @@ -4,7 +4,7 @@ local tp_tube_db = nil -- nil forces a read local tp_tube_db_version = 2.0 local function hash(pos) - return string.format("%d", minetest.hash_node_position(pos)) + return string.format("%.30g", minetest.hash_node_position(pos)) end local function save_tube_db() -- cgit v1.2.3 From 91bd0c7e985c6d5f98fddd22dccb6a4d30cae86f Mon Sep 17 00:00:00 2001 From: thetaepsilon-gamedev Date: Fri, 22 Dec 2017 17:57:00 +0000 Subject: teleport_tube.lua: add checks for hash collisions in positions table --- teleport_tube.lua | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/teleport_tube.lua b/teleport_tube.lua index 9a66de2..d707717 100644 --- a/teleport_tube.lua +++ b/teleport_tube.lua @@ -50,6 +50,11 @@ local function read_tube_db() return tp_tube_db end +-- debug formatter for coordinates used below +local fmt = function(pos) + return pos.x..", "..pos.y..", "..pos.z +end + -- updates or adds a tube local function set_tube(pos, channel, can_receive) local tubes = tp_tube_db or read_tube_db() @@ -63,6 +68,19 @@ local function set_tube(pos, channel, can_receive) end -- we haven't found any tp tube to update, so lets add it + -- but sanity check that the hash has not already been inserted. + -- if so, complain very loudly and refuse the update so the player knows something is amiss. + -- to catch regressions of https://github.com/minetest-mods/pipeworks/issues/166 + local existing = tp_tube_db[hash] + if existing ~= nil then + local e = "error" + minetest.log(e, "pipeworks teleport tube update refused due to position hash collision") + minetest.log(e, "collided hash: "..hash) + minetest.log(e, "tried-to-place tube: "..fmt(pos)) + minetest.log(e, "existing tube: "..fmt(existing)) + return + end + tp_tube_db[hash] = {x=pos.x,y=pos.y,z=pos.z,channel=channel,cr=can_receive} save_tube_db() end -- cgit v1.2.3