summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--item_transport.lua132
-rw-r--r--teleport_tube.lua20
2 files changed, 120 insertions, 32 deletions
diff --git a/item_transport.lua b/item_transport.lua
index 551db07..7219f7e 100644
--- a/item_transport.lua
+++ b/item_transport.lua
@@ -50,30 +50,44 @@ minetest.register_globalstep(function(dtime)
end
end)
-local function go_next(pos, velocity, stack, owner)
+
+
+-- 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
+
+
+
+-- 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
+
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)
@@ -96,29 +110,70 @@ 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
+ -- indicate not found if no valid rules were picked up,
+ -- and don't change the counter.
+ if not next_positions[1] then
+ return cycledir, false, nil, nil
end
- if not next_positions[1] then
- return false, nil
+ -- 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")
- local n = (cmeta:get_int("tubedir") % (#next_positions)) + 1
+ -- 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
+ return found, new_velocity, multimode
end
+
+
minetest.register_entity("pipeworks:tubed_item", {
initial_properties = {
hp_max = 1,
@@ -197,6 +252,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,
@@ -277,7 +338,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)))
@@ -297,6 +358,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
diff --git a/teleport_tube.lua b/teleport_tube.lua
index bb364db..d707717 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()
@@ -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