v0.4 - on_update function for secondary nodes introduced

This commit is contained in:
Joachim Stolberg 2018-11-09 23:37:23 +01:00
parent e3ae449a7b
commit e04f493667
8 changed files with 566 additions and 537 deletions

@ -43,9 +43,8 @@ The 3 free MSB bits of param2 of tube nodes are used to store the number of conn
The data of the peer head tube are stored as meta data: "peer_pos" and "peer_dir"
Tubelib2 provides no update mechanism for connected "secondary" nodes.
That means, all secondary nodes have to check their surrounding for any changes,
based on player interaction and/or on LBM basis.
Tubelib2 provides an update mechanism for connected "secondary" nodes. A callback function
"tubelib2_on_update" will be called for every change on the connected tubes.
@ -64,3 +63,4 @@ default
- 2018-10-20 v0.1 * Tested against hyperloop elevator.
- 2018-10-27 v0.2 * Tested against and enhanced for the hyperloop mod.
- 2018-10-27 v0.3 * Further improvements.
- 2018-11-09 v0.4 * on_update function for secondary nodes introduced

@ -35,7 +35,7 @@ end
function Tube:convert_tube_line(pos, dir)
local convert_next_tube = function(self, pos, dir)
local npos, node = self:get_next_node(pos, dir)
local npos, node = self:get_node(pos, dir)
if self.legacy_node_names[node.name] then
local dir1, dir2, num
if self.convert_tube_clbk then

@ -55,52 +55,90 @@ dir1/dir2 1/2 1/4 2/3 3/4
axis/rot 2/3 3/2 1/3 1/1
####################################################
after_place_node()
Tube:update_tubes_after_place_node()
get_player_data()
determine_tube_dirs()
friendly_primary_node()
tube_data_to_table()
add_tube_dir()
del_meta_data()
get_next_node()
decode_param2()
update_after_place_node()
get_meta()
repair_tube_line()
get_pos()
update_secondary_node()
after_place_tube()
update_after_place_tube()
del_meta()
repair_tube_line()
add_meta()
update_secondary_node()
after_dig_node()
Tube:update_tubes_after_dig_node()
decode_param2()
del_tube_dir()
del_meta_data()
decode_param2()
tube_data_to_table()
update_after_dig_node()
get_meta()
repair_tube_line()
get_pos()
add_meta()
update_secondary_node()
on_timer()
Tube:get_connected_node_pos()
after_dig_tube()
update_after_dig_tube()
get_meta()
repair_tube_line()
get_pos()
add_meta()
update_secondary_node()
Wifi:after_place_node()
Tube:prepare_pairing()
del_meta_data()
get_connected_node_pos()
get_meta()
repair_tube_line()
get_pos()
Wifi:on_receive_fields()
Tube:pairing()
store_teleport_data()
del_meta_data()
tool_repair_tubes()
determine_dirs()
repair_tube_line()
add_meta()
update_secondary_node()
Wifi:on_destruct()
Tube:stop_pairing()
del_meta_data()
tool_remove_tube()
remove_tube()
repair_tube_line()
get_pos()
add_meta()
update_secondary_node()
Tool:repair_tubes()
Tube:repair_tubes()
get_tube_dirs()
repair_tube_line()
add_meta_data()
update_head_tube()
#####################################################################
primary_node()
secondary_node()
get_primary_dir()
get_pos()
del_meta()
get_meta()
add_meta()
repair_tube_line()
update_secondary_node()
update_after_place_node()
update_after_dig_node()
update_after_place_tube()
update_after_dig_tube()
#####################################################################
Neuer Ansatz
============
+------+ +------+
|peer->| |<-peer|
| |<--------------->| |
|dir-> | | <-dir|
+------+ +------+
Beim Setzen eines Nodes x werden immer die Meta-Daten in f1 und f2 aktualisiert
sowie die Secundary Update-Funktion von S1 und S2 aufgerufen.
[S1][f1]----[n1] x [n2]----[f2][S2]
Tool:remove_tube()
Tube:remove_tube()
get_tube_dirs()
friendly_primary_node()
repair_tubes()

@ -1,7 +1,8 @@
tubelib2 = {}
dofile(minetest.get_modpath("tubelib2") .. "/tube_api.lua")
dofile(minetest.get_modpath("tubelib2") .. "/internal.lua")
dofile(minetest.get_modpath("tubelib2") .. "/internal2.lua")
dofile(minetest.get_modpath("tubelib2") .. "/internal1.lua")
dofile(minetest.get_modpath("tubelib2") .. "/convert.lua")
-- Only for testing/demo purposes
--dofile(minetest.get_modpath("tubelib2") .. "/tube_test.lua")
dofile(minetest.get_modpath("tubelib2") .. "/tube_test.lua")

249
internal1.lua Normal file

@ -0,0 +1,249 @@
--[[
Tube Library 2
==============
Copyright (C) 2018 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
internal1.lua
First level functions behind the API
]]--
-- for lazy programmers
local S = minetest.pos_to_string
local P = minetest.string_to_pos
local M = minetest.get_meta
local Tube = tubelib2.Tube
local Turn180Deg = tubelib2.Turn180Deg
local Dir6dToVector = tubelib2.Dir6dToVector
--------------------------------------------------------------------------------------
-- node get/test functions
--------------------------------------------------------------------------------------
-- Check if node at given position is a tube node
-- If dir == nil then node_pos = pos
-- Function returns the new pos or nil
function Tube:primary_node(pos, dir)
local npos, node = self:get_node(pos, dir)
if self.primary_node_names[node.name] then
return npos
end
end
-- Check if node at given position is a secondary node
-- If dir == nil then node_pos = pos
-- Function returns the new pos or nil
function Tube:secondary_node(pos, dir)
local npos, node = self:get_node(pos, dir)
if self.secondary_node_names[node.name] then
return npos
end
end
-- Used to determine the node side to the tube connection.
-- Function returns the first found dir value
-- to a primary node.
-- Only used by convert.set_pairing()
function Tube:get_primary_dir(pos)
-- Check all valid positions
for dir = 1,6 do
if self:primary_node(pos, dir) then
return dir
end
end
end
-- Return the new pos behind pos/dir and the dir our of the node.
function Tube:get_pos(pos, dir)
return vector.add(pos, Dir6dToVector[dir or 0]), Turn180Deg[dir]
end
--------------------------------------------------------------------------------------
-- get/del/add meta data functions
--------------------------------------------------------------------------------------
function Tube:del_meta(pos, dir)
local npos, node = self:get_node(pos, dir)
if self.primary_node_names[node.name] then
local meta = M(npos)
local peer_pos = meta:get_string("peer_pos")
local peer_dir = meta:get_int("peer_dir")
meta:from_table(nil)
if peer_pos ~= "" then
return P(peer_pos), peer_dir
end
end
return self:repair_tube_line(pos, dir)
end
function Tube:get_meta(pos, dir)
local npos = vector.add(pos, Dir6dToVector[dir or 0])
local meta = M(npos)
local peer_pos = meta:get_string("peer_pos")
local peer_dir = meta:get_int("peer_dir")
if peer_pos ~= "" then
return P(peer_pos), peer_dir
end
return self:repair_tube_line(pos, dir)
end
function Tube:get_oldmeta(pos, dir, oldmetadata)
if oldmetadata.fields and oldmetadata.fields.peer_pos then
return P(oldmetadata.fields.peer_pos), tonumber(oldmetadata.fields.peer_dir)
end
return self:repair_tube_line(pos, dir)
end
-- Add meta data from the other tube head node
function Tube:add_meta(pos, peer_pos, peer_dir)
local _, node = self:get_node(pos)
if self.primary_node_names[node.name] then
if self.show_infotext then
M(pos):set_string("infotext", S(peer_pos))
end
M(pos):set_string("peer_pos", S(peer_pos))
M(pos):set_int("peer_dir", peer_dir)
end
end
--------------------------------------------------------------------------------------
-- Further helper functions
--------------------------------------------------------------------------------------
-- Do a correction of param2, delete meta data of all 2-conn-tubes,
-- and return peer_pos, peer_dir, and number of tube nodes.
function Tube:repair_tube_line(pos, dir)
local repair_next_tube = function(self, pos, dir)
local npos, dir1, dir2 = self:determine_next_node(pos, dir)
if dir1 then
M(npos):from_table(nil)
if Turn180Deg[dir] == dir1 then
return npos, dir2
else
return npos, dir1
end
end
return self:get_next_teleport_node(pos, dir)
end
local cnt = 0
if not dir then return pos, dir, cnt end
while cnt <= self.max_tube_length do
--if cnt > 1 then M(pos):from_table(nil) end
local new_pos, new_dir = repair_next_tube(self, pos, dir)
if not new_dir then break end
pos, dir = new_pos, new_dir
cnt = cnt + 1
end
return pos, dir, cnt
end
-- fpos,fdir points to the secondary node to be updated.
-- npos,ndir are used to calculate the connection data to be written.
function Tube:update_secondary_node(fpos,fdir, npos,ndir)
-- [s]<-[n]----[f]->[s]
local fpos2, node = self:get_node(fpos, fdir)
if minetest.registered_nodes[node.name].tubelib2_on_update then
local npos2 = self:get_pos(npos, ndir)
minetest.registered_nodes[node.name].tubelib2_on_update(fpos2, npos2, ndir)
end
end
--------------------------------------------------------------------------------------
-- pairing functions
--------------------------------------------------------------------------------------
-- Pairing helper function
function Tube:store_teleport_data(pos, peer_pos)
local meta = M(pos)
meta:set_string("tele_pos", S(peer_pos))
meta:set_string("channel", nil)
meta:set_string("formspec", nil)
meta:set_string("infotext", "Connected with "..S(peer_pos))
return meta:get_int("tube_dir")
end
-------------------------------------------------------------------------------
-- update-after/get-dir functions
-------------------------------------------------------------------------------
function Tube:update_after_place_node(pos, dirs)
-- Check all valid positions
local lRes= {}
dirs = dirs or self.dirs_to_check
for _,dir in ipairs(dirs) do
local npos, d1, d2, num = self:add_tube_dir(pos, dir)
if npos then
self.clbk_after_place_tube(self:tube_data_to_table(npos, d1, d2, num))
lRes[#lRes+1] = dir
end
end
return lRes
end
function Tube:update_after_dig_node(pos, dirs)
-- Check all valid positions
local lRes= {}
dirs = dirs or self.dirs_to_check
for _,dir in ipairs(dirs) do
local npos, d1, d2, num = self:del_tube_dir(pos, dir)
if npos then
self.clbk_after_place_tube(self:tube_data_to_table(npos, d1, d2, num))
lRes[#lRes+1] = dir
end
end
return lRes
end
function Tube:update_after_place_tube(pos, placer, pointed_thing)
local preferred_pos, fdir = self:get_player_data(placer, pointed_thing)
local dir1, dir2, num_tubes = self:determine_tube_dirs(pos, preferred_pos, fdir)
if dir1 == nil then
return false
end
self.clbk_after_place_tube(self:tube_data_to_table(pos, dir1, dir2, num_tubes))
if num_tubes >= 1 then
local npos, d1, d2, num = self:add_tube_dir(pos, dir1)
if npos then
self.clbk_after_place_tube(self:tube_data_to_table(npos, d1, d2, num))
end
end
if num_tubes >= 2 then
local npos, d1, d2, num = self:add_tube_dir(pos, dir2)
if npos then
self.clbk_after_place_tube(self:tube_data_to_table(npos, d1, d2, num))
end
end
return true, dir1, dir2
end
function Tube:update_after_dig_tube(pos, param2)
local dir1, dir2, num_tubes = self:decode_param2(pos, param2)
local lRes = {}
local npos, d1, d2, num = self:del_tube_dir(pos, dir1)
if npos then
self.clbk_after_place_tube(self:tube_data_to_table(npos, d1, d2, num))
lRes[#lRes+1] = dir1
end
npos, d1, d2, num = self:del_tube_dir(pos, dir2)
if npos then
self.clbk_after_place_tube(self:tube_data_to_table(npos, d1, d2, num))
lRes[#lRes+1] = dir2
end
return lRes
end

@ -114,6 +114,38 @@ end
local Tube = tubelib2.Tube
function Tube:fdir(player)
local pitch = player:get_look_pitch()
if pitch > 1.1 and self.allowed_6d_dirs[6] then -- up?
return 6
elseif pitch < -1.1 and self.allowed_6d_dirs[5] then -- down?
return 5
elseif not self.allowed_6d_dirs[1] then
return 6
else
return minetest.dir_to_facedir(player:get_look_dir()) + 1
end
end
function Tube:get_player_data(placer, pointed_thing)
if placer and pointed_thing and pointed_thing.type == "node" then
if placer:get_player_control().sneak then
return pointed_thing.under, self:fdir(placer)
else
return nil, self:fdir(placer)
end
end
end
-- Return param2 and tube type ("A"/"S")
function Tube:encode_param2(dir1, dir2, num_conn)
if dir1 > dir2 then
dir1, dir2 = dir2, dir1
end
local param2, _type = unpack(DirToParam2[dir1 * 10 + dir2] or {0, "S"})
return (num_conn * 32) + param2, _type
end
-- Check if node has a connection on the given dir
function Tube:connected(pos, dir)
local npos = vector.add(pos, Dir6dToVector[dir or 0])
@ -138,15 +170,6 @@ function Tube:determine_dir1_dir2_and_num_conn(pos)
end
end
-- Return param2 and tube type ("A"/"S")
function Tube:encode_param2(dir1, dir2, num_conn)
if dir1 > dir2 then
dir1, dir2 = dir2, dir1
end
local param2, _type = unpack(DirToParam2[dir1 * 10 + dir2] or {0, "S"})
return (num_conn * 32) + param2, _type
end
-- Return dir1, dir2, num_conn
function Tube:decode_param2(pos, param2)
local val = Param2ToDir[param2 % 32]
@ -161,23 +184,14 @@ function Tube:decode_param2(pos, param2)
end
-- Return node next to pos in direction 'dir'
function Tube:get_next_node(pos, dir)
function Tube:get_node(pos, dir)
local npos = vector.add(pos, Dir6dToVector[dir or 0])
return npos, get_node_lvm(npos)
end
-- Return the param2 stored tube dirs or 5,6
function Tube:get_tube_dirs(pos)
local node = get_node_lvm(pos)
if self.primary_node_names[node.name] then
return self:decode_param2(pos, node.param2)
end
return 5,6
end
-- Return pos for a primary_node and true if num_conn < 2, else false
function Tube:friendly_primary_node(pos, dir)
local npos, node = self:get_next_node(pos, dir)
local npos, node = self:get_node(pos, dir)
if self.primary_node_names[node.name] then
local _,_,num_conn = self:decode_param2(npos, node.param2)
-- tube node with max one connection?
@ -185,91 +199,6 @@ function Tube:friendly_primary_node(pos, dir)
end
end
-- Jump over the teleport nodes to the next tube node
function Tube:get_next_teleport_node(pos, dir)
local npos = vector.add(pos, Dir6dToVector[dir or 0])
local meta = M(npos)
local s = meta:get_string("tele_pos")
if s ~= "" then
local tele_pos = P(s)
local tube_dir = M(tele_pos):get_int("tube_dir")
if tube_dir ~= 0 then
return tele_pos, tube_dir
end
end
end
-- Update meta data and number of connections in param2
-- pos1 is the node to be updated with the data pos2, dir2, num_tubes
function Tube:update_head_tube(pos1, pos2, dir2, num_tubes)
local node = get_node_lvm(pos1)
if self.primary_node_names[node.name] then
local d1, d2, num = self:decode_param2(pos1, node.param2)
if d1 and d2 then
num = (self:connected(pos1, d1) and 1 or 0) + (self:connected(pos1, d2) and 1 or 0)
node.param2 = self:encode_param2(d1, d2, num)
minetest.set_node(pos1, node)
if self.show_infotext then
M(pos1):set_string("infotext", S(pos2).." / "..num_tubes.." tubes")
end
M(pos1):set_string("peer_pos", S(pos2))
M(pos1):set_int("peer_dir", dir2)
end
end
end
-- Add meta data on both tube sides pos1 and pos2
-- dir1/dir2 are the tube output directions (inventory nodes)
function Tube:add_meta_data(pos1, pos2, dir1, dir2, num_tubes)
self:update_head_tube(pos1, pos2, dir2, num_tubes)
if not vector.equals(pos1, pos2) then
self:update_head_tube(pos2, pos1, dir1, num_tubes)
end
end
-- Delete meta data on both tube sides.
-- If dir is nil, pos is the position of one head node.
function Tube:del_meta_data(pos, dir)
local npos = vector.add(pos, Dir6dToVector[dir or 0])
local speer_pos = M(npos):get_string("peer_pos")
if speer_pos ~= "" then
local meta = M(pos)
if meta:get_string("peer_pos") ~= "" then
meta:from_table(nil)
end
meta = M(P(speer_pos))
if meta:get_string("peer_pos") ~= "" then
meta:from_table(nil)
end
return true
end
return false
end
function Tube:fdir(player)
local pitch = player:get_look_pitch()
if pitch > 1.1 and self.allowed_6d_dirs[6] then -- up?
return 6
elseif pitch < -1.1 and self.allowed_6d_dirs[5] then -- down?
return 5
elseif not self.allowed_6d_dirs[1] then
return 6
else
return minetest.dir_to_facedir(player:get_look_dir()) + 1
end
end
function Tube:get_player_data(placer, pointed_thing)
if placer and pointed_thing and pointed_thing.type == "node" then
if placer:get_player_control().sneak then
return pointed_thing.under, self:fdir(placer)
else
return nil, self:fdir(placer)
end
end
end
-- Check all 6 possible positions for known nodes considering preferred_pos
-- and the players fdir and return dir1, dir2 and the number of tubes to connect to (0..2).
function Tube:determine_tube_dirs(pos, preferred_pos, fdir)
@ -347,18 +276,41 @@ function Tube:determine_tube_dirs(pos, preferred_pos, fdir)
end
end
function Tube:determine_next_node(pos, dir)
local npos, node = self:get_node(pos, dir)
if self.primary_node_names[node.name] then
-- determine dirs on two ways
local da1,da2,numa = self:decode_param2(npos, node.param2)
local db1,db2,numb = self:determine_dir1_dir2_and_num_conn(npos)
-- both identical?
if da1 == db1 and da2 == db2 then
return npos, da1, da2
end
-- test if stored dirs point to valid nodes
if self:connected(npos, da1) and self:connected(npos, da2) then
return npos, da1, da2
end
-- use and store the determined dirs
if db1 and db2 then
node.param2 = self:encode_param2(db1,db2,numb)
minetest.set_node(npos, node)
return npos, db1, db2
end
return npos, da1, da2
end
end
-- format and return given data as table
function Tube:tube_data_to_table(pos, dir1, dir2, num_tubes)
local param2, tube_type = self:encode_param2(dir1, dir2, num_tubes)
return pos, param2, tube_type, num_tubes
end
-- Determine a tube side without connection, increment the number of connections
-- and return the new data to be able to update the node:
-- new_pos, dir1, dir2, num_connections (1, 2)
function Tube:add_tube_dir(pos, dir)
local npos, node = self:get_next_node(pos, dir)
local npos, node = self:get_node(pos, dir)
if self.primary_node_names[node.name] then
local d1, d2, num = self:decode_param2(npos, node.param2)
-- not already connected to the new tube?
@ -383,112 +335,13 @@ end
-- and return the new data to be able to update the node:
-- new_pos, dir1, dir2, num_connections (0, 1)
function Tube:del_tube_dir(pos, dir)
local npos, node = self:get_next_node(pos, dir)
local npos, node = self:get_node(pos, dir)
if self.primary_node_names[node.name] then
local d1, d2, num = self:decode_param2(npos, node.param2)
return npos, d1, d2, math.max(num - 1, 0)
end
end
-- Store the node data in out_tbl for later use
-- and return true/false
function Tube:is_tube_head(pos, dir, out_tbl)
out_tbl.pos, out_tbl.node = self:get_next_node(pos, dir)
if self.primary_node_names[out_tbl.node.name] then
local dir1, dir2, num_conn =
self:decode_param2(out_tbl.pos, out_tbl.node.param2)
if Turn180Deg[dir] == dir1 then
out_tbl.dir = dir2
else
out_tbl.dir = dir1
end
return true
end
return false
end
-- Go down the tube to the end position and
-- return pos, dir to the next node, and num tubes
function Tube:find_peer_tube_head(node_tbl)
local get_next_tube = function(self, pos, dir)
-- Return pos and dir to the next node of the tube node at pos/dir
local npos, node = self:get_next_node(pos, dir)
if self.primary_node_names[node.name] then
local dir1, dir2, num = self:decode_param2(npos, node.param2)
if Turn180Deg[dir] == dir1 then
return npos, dir2
else
return npos, dir1
end
end
return self:get_next_teleport_node(npos)
end
local cnt = 0
local pos = node_tbl.pos
local dir = node_tbl.dir
while cnt <= self.max_tube_length do
local new_pos, new_dir = get_next_tube(self, pos, dir)
if not new_dir then break end
pos, dir = new_pos, new_dir
cnt = cnt + 1
end
return pos, dir, cnt
end
function Tube:determine_next_node(pos, dir)
local npos, node = self:get_next_node(pos, dir)
if self.primary_node_names[node.name] then
-- determine dirs on two ways
local da1,da2,numa = self:decode_param2(npos, node.param2)
local db1,db2,numb = self:determine_dir1_dir2_and_num_conn(npos)
-- both identical?
if da1 == db1 and da2 == db2 then
return npos, da1, da2
end
-- test if stored dirs point to valid nodes
if self:connected(npos, da1) and self:connected(npos, da2) then
return npos, da1, da2
end
-- use and store the determined dirs
if db1 and db2 then
node.param2 = self:encode_param2(db1,db2,numb)
minetest.set_node(npos, node)
return npos, db1, db2
end
end
end
-- Do a correction of param2 and delete meta data of all 2-conn-tubes,
-- update the meta data of the head tubes and
-- return head-pos and number of nodes
function Tube:repair_tube_line(pos, dir)
local repair_next_tube = function(self, pos, dir)
local npos, dir1, dir2 = self:determine_next_node(pos, dir)
if dir1 then
M(npos):from_table(nil)
if Turn180Deg[dir] == dir1 then
return npos, dir2
else
return npos, dir1
end
end
return self:get_next_teleport_node(pos, dir)
end
local cnt = 0
if not dir then return pos, cnt end
while cnt <= self.max_tube_length do
local new_pos, new_dir = repair_next_tube(self, pos, dir)
if not new_dir then break end
pos, dir = new_pos, new_dir
cnt = cnt + 1
end
return pos, dir, cnt
end
-- Pairing helper function
function Tube:store_teleport_data(pos, peer_pos)
local meta = M(pos)
@ -499,51 +352,29 @@ function Tube:store_teleport_data(pos, peer_pos)
return meta:get_int("tube_dir")
end
function Tube:get_peer_tube_head(node_tbl, dir)
-- if meta data is available, return peer_pos, peer_pos
local meta = M(node_tbl.pos)
local spos = meta:get_string("peer_pos")
if spos ~= "" then
return P(spos), meta:get_int("peer_dir")
end
-- repair tube line
local pos2, dir2, cnt = self:find_peer_tube_head(node_tbl)
if pos2 then
self:add_meta_data(node_tbl.pos, pos2, Turn180Deg[dir], dir2, cnt+1)
return pos2, dir2
-- Jump over the teleport nodes to the next tube node
function Tube:get_next_teleport_node(pos, dir)
local npos = vector.add(pos, Dir6dToVector[dir or 0])
local meta = M(npos)
local s = meta:get_string("tele_pos")
if s ~= "" then
local tele_pos = P(s)
local tube_dir = M(tele_pos):get_int("tube_dir")
if tube_dir ~= 0 then
return tele_pos, tube_dir
end
end
end
-- pos is the position of the removed node
-- dir1, dir2 are the neighbor sides to be checked for meta data
-- oldmetadata is also used to check for meta data
-- If meta data is found (peer_pos), it is used to determine the tube head.
function Tube:delete_tube_meta_data(pos, dir1, dir2, oldmetadata)
-- tube with two connections?
if dir2 then
local res
if dir1 then
local npos = self:find_peer_tube_head({pos=pos, dir=dir1})
if not self:del_meta_data(npos) then
-- try the other direction
npos = self:find_peer_tube_head({pos=pos, dir=dir2})
self:del_meta_data(npos)
end
end
-- removed node with meta data?
elseif oldmetadata and oldmetadata.fields and oldmetadata.fields.peer_pos then
local npos = P(oldmetadata.fields.peer_pos)
self:del_meta_data(npos)
elseif dir1 then
local npos = vector.add(pos, Dir6dToVector[dir1])
-- node with peer meta data?
if not self:del_meta_data(npos) then
-- try teleport node
local tele_pos, tube_dir = self:get_next_teleport_node(npos)
if tele_pos then
local npos = self:find_peer_tube_head({pos=tele_pos, dir=tube_dir})
self:del_meta_data(npos)
end
end
function Tube:remove_tube(pos, sound)
local node = get_node_lvm(pos)
if self.primary_node_names[node.name] then
minetest.sound_play({
name=sound},{
gain=1,
max_hear_distance=5,
loop=false})
minetest.remove_node(pos)
return node, {}
end
end
end

@ -13,10 +13,11 @@
]]--
-- Version for compatibility checks, see readme.md/history
tubelib2.version = 0.3
tubelib2.version = 0.4
-- for lazy programmers
local M = minetest.get_meta
local S = minetest.pos_to_string
local Dir2Str = {"north", "east", "south", "west", "down", "up"}
@ -32,43 +33,17 @@ local function Tbl(list)
return tbl
end
local function after_place_node(self, pos)
-- Check all valid positions
for dir = 1,6 do
if self.allowed_6d_dirs[dir] then
self:delete_tube_meta_data(pos, dir)
local npos, d1, d2, num = self:add_tube_dir(pos, dir)
if npos then
self.clbk_after_place_tube(self:tube_data_to_table(npos, d1, d2, num))
end
end
end
end
local function after_dig_node(self, pos)
-- Check all valid positions
for dir = 1,6 do
if self.allowed_6d_dirs[dir] then
self:delete_tube_meta_data(pos, dir)
local npos, d1, d2, num = self:del_tube_dir(pos, dir)
if npos then
self.clbk_after_place_tube(self:tube_data_to_table(npos, d1, d2, num))
end
end
end
end
-- Tubelib2 Class
tubelib2.Tube = {}
local Tube = tubelib2.Tube
--
-- API Functions
--
-- Tubelib2 Class
tubelib2.Tube = {}
local Tube = tubelib2.Tube
function Tube:new(attr)
local o = {
allowed_6d_dirs = attr.allowed_6d_dirs or {true, true, true, true, true, true},
dirs_to_check = attr.dirs_to_check or {1,2,3,4,5,6},
max_tube_length = attr.max_tube_length or 1000,
primary_node_names = Tbl(attr.primary_node_names or {}),
secondary_node_names = Tbl(attr.secondary_node_names or {}),
@ -77,6 +52,7 @@ function Tube:new(attr)
clbk_after_place_tube = attr.after_place_tube,
pairingList = {}, -- teleporting nodes
}
o.allowed_6d_dirs = Tbl(o.dirs_to_check)
setmetatable(o, self)
self.__index = self
return o
@ -89,207 +65,100 @@ function Tube:add_secondary_node_names(names)
end
end
-- Check if node at given position is a tube node
-- If dir == nil then node_pos = pos
-- Function returns the new pos or nil
function Tube:primary_node(pos, dir)
local npos, node = self:get_next_node(pos, dir)
if self.primary_node_names[node.name] then
return npos
-- To be called after a secondary node is placed.
-- dirs is a list with valid dirs, like: {1,2,3,4}
function Tube:after_place_node(pos, dirs)
-- [s][f]----[n] x
-- s..secondary, f..far, n..near, x..node to be placed
for _,dir in ipairs(self:update_after_place_node(pos, dirs)) do
local fpos,fdir = self:get_meta(pos, dir)
local npos, ndir = self:get_pos(pos, dir)
self:update_secondary_node(fpos,fdir, npos,ndir)
self:update_secondary_node(npos,ndir, fpos,fdir)
end
end
-- Check if node at given position is a secondary node
-- If dir == nil then node_pos = pos
-- Function returns the new pos or nil
function Tube:secondary_node(pos, dir)
local npos, node = self:get_next_node(pos, dir)
if self.secondary_node_names[node.name] then
return npos
-- To be called after a tube/primary node is placed.
function Tube:after_place_tube(pos, placer, pointed_thing)
-- [s1][f1]----[n1] x [n2]-----[f2][s2]
-- s..secondary, f..far, n..near, x..node to be placed
local res,dir1,dir2 = self:update_after_place_tube(pos, placer, pointed_thing)
if res then
local fpos1,fdir1 = self:del_meta(pos, dir1)
local fpos2,fdir2 = self:del_meta(pos, dir2)
self:add_meta(fpos1, fpos2,fdir2)
self:add_meta(fpos2, fpos1,fdir1)
self:update_secondary_node(fpos1,fdir1, fpos2,fdir2)
self:update_secondary_node(fpos2,fdir2, fpos1,fdir1)
end
return res
end
function Tube:after_dig_node(pos, dirs)
-- [s][f]----[n] x
-- s..secondary, f..far, n..near, x..node to be removed
for _,dir in ipairs(self:update_after_dig_node(pos, dirs)) do
local fpos,fdir = self:get_meta(pos, dir)
local npos,ndir = self:get_pos(pos, dir)
self:add_meta(npos, fpos,fdir)
self:add_meta(fpos, npos,ndir)
self:update_secondary_node(fpos,fdir, npos,ndir)
end
end
-- Used to determine the node side to the tube connection.
-- Function returns the first found dir value
-- to a primary node.
function Tube:get_primary_dir(pos)
-- Check all valid positions
for dir = 1,6 do
if self:primary_node(pos, dir) then
return dir
end
function Tube:after_dig_tube(pos, oldnode, oldmetadata)
-- [s1][f1]----[n1] x [n2]-----[f2][s2]
-- s..secondary, f..far, n..near, x..node to be removed
for _,dir in ipairs(self:update_after_dig_tube(pos, oldnode.param2)) do
local fpos,fdir = self:get_oldmeta(pos, dir, oldmetadata)
local npos,ndir = self:get_pos(pos, dir)
self:add_meta(npos, fpos,fdir)
self:add_meta(fpos, npos,ndir)
self:update_secondary_node(fpos,fdir, npos,ndir)
end
end
-- From source node to destination node via tubes.
-- pos is the source node position, dir the output dir
-- The returned pos is the destination position, dir
-- is the direction into the destination node.
function Tube:get_connected_node_pos(pos, dir)
local node = {}
if self:is_tube_head(pos, dir, node) then
local npos, ndir = self:get_peer_tube_head(node, dir)
return vector.add(npos, tubelib2.Dir6dToVector[ndir or 0]), ndir
end
return vector.add(pos, tubelib2.Dir6dToVector[dir or 0]), dir
end
function Tube:beside(pos1, pos2)
local pos = vector.subtract(pos1, pos2)
local res = pos.x + pos.y + pos.z
return res == 1 or res == -1
end
-- From tube head to tube head.
-- pos is the tube head position, dir is the direction into the head node.
-- The returned pos is the peer head position, dir
-- is the direction out of the peer head node.
function Tube:get_tube_end_pos(pos, dir)
local node = {}
if self:is_tube_head(pos, nil, node) then
return self:get_peer_tube_head(node, dir)
end
return pos, dir
end
-- To be called after a secondary node is placed.
function Tube:after_place_node(pos, dir1, dir2)
if not dir1 and not dir2 then
after_place_node(self, pos)
return
end
self:delete_tube_meta_data(pos, dir1, dir2)
if dir1 then
local npos, d1, d2, num = self:add_tube_dir(pos, dir1)
if npos then
self.clbk_after_place_tube(self:tube_data_to_table(npos, d1, d2, num))
end
end
if dir2 then
local npos, d1, d2, num = self:add_tube_dir(pos, dir2)
if npos then
self.clbk_after_place_tube(self:tube_data_to_table(npos, d1, d2, num))
end
end
end
-- To be called after a tube/primary node is placed.
function Tube:after_place_tube(pos, placer, pointed_thing)
local preferred_pos, fdir = self:get_player_data(placer, pointed_thing)
local dir1, dir2, num_tubes = self:determine_tube_dirs(pos, preferred_pos, fdir)
if dir1 == nil then
return false
end
self:delete_tube_meta_data(pos, dir1, dir2)
self.clbk_after_place_tube(self:tube_data_to_table(pos, dir1, dir2, num_tubes))
if num_tubes >= 1 then
local npos, d1, d2, num = self:add_tube_dir(pos, dir1)
if npos then
self.clbk_after_place_tube(self:tube_data_to_table(npos, d1, d2, num))
end
end
if num_tubes >= 2 then
local npos, d1, d2, num = self:add_tube_dir(pos, dir2)
if npos then
self.clbk_after_place_tube(self:tube_data_to_table(npos, d1, d2, num))
end
end
return true
end
-- To be called after a secondary node is removed.
function Tube:after_dig_node(pos, dir1, dir2)
if not dir1 and not dir2 then
after_dig_node(self, pos)
return
end
self:delete_tube_meta_data(pos, dir1, dir2)
local npos, d1, d2, num = self:del_tube_dir(pos, dir1)
if npos then
self.clbk_after_place_tube(self:tube_data_to_table(npos, d1, d2, num))
end
npos, d1, d2, num = self:del_tube_dir(pos, dir2)
if npos then
self.clbk_after_place_tube(self:tube_data_to_table(npos, d1, d2, num))
end
end
-- To be called after a tube node is removed.
function Tube:after_dig_tube(pos, oldnode, oldmetadata)
local dir1, dir2, num_tubes = self:decode_param2(pos, oldnode.param2)
self:delete_tube_meta_data(pos, dir1, dir2, oldmetadata)
local npos, d1, d2, num = self:del_tube_dir(pos, dir1)
if npos then
self.clbk_after_place_tube(self:tube_data_to_table(npos, d1, d2, num))
end
npos, d1, d2, num = self:del_tube_dir(pos, dir2)
if npos then
self.clbk_after_place_tube(self:tube_data_to_table(npos, d1, d2, num))
end
local fpos,fdir = self:get_meta(pos, dir)
local npos,ndir = self:get_pos(fpos,fdir)
return npos, dir
end
-- To be called from a repair tool in the case of a "WorldEdit" or with
-- legacy nodes corrupted tube line.
function Tube:tool_repair_tubes(pos)
-- determine the dirs from pointed pos
local npos, d1, d2 = self:determine_next_node(pos)
M(pos):from_table(nil) -- delete meta data
local npos1, dir1, cnt1 = self:repair_tube_line(pos, d1)
local npos2, dir2, cnt2 = self:repair_tube_line(pos, d2)
self:add_meta_data(npos1, npos2, dir1, dir2, cnt1+cnt2+1)
return npos1, npos2, d1, d2, cnt1, cnt2
function Tube:tool_repair_tube(pos)
local res,dir1,dir2 = self:determine_next_node(pos)
if res then
local fpos1,fdir1,cnt1 = self:repair_tube_line(pos, dir1)
local fpos2,fdir2,cnt2 = self:repair_tube_line(pos, dir2)
self:add_meta(fpos1, fpos2,fdir2)
self:add_meta(fpos2, fpos1,fdir1)
self:update_secondary_node(fpos1,fdir1, fpos2,fdir2)
self:update_secondary_node(fpos2,fdir2, fpos1,fdir1)
return dir1, dir2, fpos1, fpos2, fdir1, fdir2, cnt1 or 0, cnt2 or 0
end
end
-- To be called from a repair tool in the case, tube nodes are "unbreakable".
function Tube:tool_remove_tube(pos, sound)
local dir1, dir2 = self:get_tube_dirs(pos)
if dir1 ~= 0 then
minetest.sound_play({
name=sound},{
gain=1,
max_hear_distance=5,
loop=false})
minetest.remove_node(pos)
self:delete_tube_meta_data(pos, dir1, dir2)
local npos, d1, d2, num = self:del_tube_dir(pos, dir1)
if npos then
self.clbk_after_place_tube(self:tube_data_to_table(npos, d1, d2, num))
end
npos, d1, d2, num = self:del_tube_dir(pos, dir2)
if npos then
self.clbk_after_place_tube(self:tube_data_to_table(npos, d1, d2, num))
end
local oldnode, oldmeta = self:remove_tube(pos, sound)
if oldnode then
self:after_dig_tube(pos, oldnode, oldmeta)
end
end
function Tube:prepare_pairing(pos, tube_dir, sFormspec)
local meta = M(pos)
meta:set_int("tube_dir", tube_dir)
-- break the connection
self:delete_tube_meta_data(pos, tube_dir)
meta:set_string("channel", nil)
meta:set_string("infotext", "Unconnected")
meta:set_string("formspec", sFormspec)
@ -299,13 +168,19 @@ function Tube:pairing(pos, channel)
if self.pairingList[channel] and pos ~= self.pairingList[channel] then
-- store peer position on both nodes
local peer_pos = self.pairingList[channel]
local tube_dir1 = self:store_teleport_data(pos, peer_pos)
local tube_dir2 = self:store_teleport_data(peer_pos, pos)
self:delete_tube_meta_data(pos, tube_dir1)
self:delete_tube_meta_data(peer_pos, tube_dir2)
--self:delete_tube_meta_data(pos, tube_dir1)
--self:delete_tube_meta_data(peer_pos, tube_dir2)
local fpos1,fdir1 = self:repair_tube_line(pos, tube_dir1)
local fpos2,fdir2 = self:repair_tube_line(peer_pos, tube_dir2)
self:add_meta(fpos1, fpos2,fdir2)
self:add_meta(fpos2, fpos1,fdir1)
self:update_secondary_node(fpos1,fdir1, fpos2,fdir2)
self:update_secondary_node(fpos2,fdir2, fpos1,fdir1)
self.pairingList[channel] = nil
return true
else
@ -323,16 +198,12 @@ function Tube:stop_pairing(pos, oldmetadata, sFormspec)
local tele_pos = minetest.string_to_pos(oldmetadata.fields.tele_pos)
local peer_meta = M(tele_pos)
if peer_meta then
self:delete_tube_meta_data(tele_pos, peer_meta:get_int("tube_dir"))
self:after_place_node(tele_pos, {peer_meta:get_int("tube_dir")})
peer_meta:set_string("channel", nil)
peer_meta:set_string("tele_pos", nil)
peer_meta:set_string("formspec", sFormspec)
peer_meta:set_string("infotext", "Unconnected")
end
end
if oldmetadata and oldmetadata.fields then
self:delete_tube_meta_data(pos, tonumber(oldmetadata.fields.tube_dir or 0), nil, oldmetadata)
end
end

@ -23,20 +23,20 @@ local M = minetest.get_meta
local Tube = tubelib2.Tube:new({
-- North, East, South, West, Down, Up
allowed_6d_dirs = {true, true, true, true, false, false}, -- horizontal only
-- allowed_6d_dirs = {false, false, false, false, true, true}, -- vertical only
dirs_to_check = {1,2,3,4}, -- horizontal only
-- dirs_to_check = {5,6}, -- vertical only
max_tube_length = 1000,
show_infotext = false,
primary_node_names = {"tubelib2:tubeS", "tubelib2:tubeA"},
secondary_node_names = {"default:chest", "default:chest_open",
"tubelib2:source", "tubelib2:teleporter"},
"tubelib2:source", "tubelib2:junction", "tubelib2:teleporter"},
after_place_tube = function(pos, param2, tube_type, num_tubes, tbl)
minetest.set_node(pos, {name = "tubelib2:tube"..tube_type, param2 = param2})
minetest.sound_play({
name="default_place_node_glass"},{
gain=1,
max_hear_distance=5,
loop=false})
-- minetest.sound_play({
-- name="default_place_node_glass"},{
-- gain=1,
-- max_hear_distance=5,
-- loop=false})
end,
})
@ -71,7 +71,6 @@ minetest.register_node("tubelib2:tubeS", {
{-2/8, -2/8, -4/8, 2/8, 2/8, 4/8},
},
},
node_placement_prediction = "", -- important!
on_rotate = screwdriver.disallow, -- important!
paramtype = "light",
sunlight_propagates = true,
@ -104,7 +103,7 @@ minetest.register_node("tubelib2:tubeA", {
{-2/8, -2/8, -4/8, 2/8, 2/8, -2/8},
},
},
--on_rotate = screwdriver.disallow, -- important!
on_rotate = screwdriver.disallow, -- important!
paramtype = "light",
sunlight_propagates = true,
is_ground_content = false,
@ -133,13 +132,13 @@ minetest.register_node("tubelib2:source", {
after_place_node = function(pos, placer)
local tube_dir = ((minetest.dir_to_facedir(placer:get_look_dir()) + 2) % 4) + 1
M(pos):set_int("tube_dir", tube_dir)
Tube:after_place_node(pos, tube_dir)
Tube:after_place_node(pos, {tube_dir})
minetest.get_node_timer(pos):start(2)
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
local tube_dir = tonumber(oldmetadata.fields.tube_dir or 0)
Tube:after_dig_node(pos, tube_dir)
Tube:after_dig_node(pos, {tube_dir})
end,
on_timer = function(pos, elapsed)
@ -172,6 +171,42 @@ minetest.register_node("tubelib2:source", {
sounds = default.node_sound_glass_defaults(),
})
minetest.register_node("tubelib2:junction", {
description = "Tubelib2 Junction",
tiles = {
'tubelib2_conn.png',
},
after_place_node = function(pos, placer, itemstack, pointed_thing)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", "Position "..S(pos))
Tube:after_place_node(pos)
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
Tube:after_dig_node(pos)
end,
tubelib2_on_update = function(pos, peer_pos, peer_dir)
if Tube:secondary_node(peer_pos) then
local sdir = tubelib2.dir_to_string(peer_dir)
local node = minetest.get_node(peer_pos)
print(S(pos).." connected with "..node.name)
else
local sdir = tubelib2.dir_to_string(peer_dir)
print(S(pos).." connected with "..S(peer_pos))
end
end,
paramtype2 = "facedir", -- important!
on_rotate = screwdriver.disallow, -- important!
paramtype = "light",
sunlight_propagates = true,
is_ground_content = false,
groups = {crumbly = 3, cracky = 3, snappy = 3},
sounds = default.node_sound_glass_defaults(),
})
minetest.register_node("tubelib2:teleporter", {
description = "Tubelib2 Teleporter",
tiles = {
@ -188,7 +223,7 @@ minetest.register_node("tubelib2:teleporter", {
-- the tube_dir calculation depends on the player look-dir and the hole side of the node
local tube_dir = ((minetest.dir_to_facedir(placer:get_look_dir()) + 2) % 4) + 1
Tube:prepare_pairing(pos, tube_dir, sFormspec)
Tube:after_place_node(pos, tube_dir)
Tube:after_place_node(pos, {tube_dir})
end,
on_receive_fields = function(pos, formname, fields, player)
@ -200,7 +235,7 @@ minetest.register_node("tubelib2:teleporter", {
after_dig_node = function(pos, oldnode, oldmetadata, digger)
Tube:stop_pairing(pos, oldmetadata, sFormspec)
local tube_dir = tonumber(oldmetadata.fields.tube_dir or 0)
Tube:after_dig_node(pos, tube_dir)
Tube:after_dig_node(pos, {tube_dir})
end,
paramtype2 = "facedir", -- important!
@ -220,23 +255,27 @@ local function read_param2(pos, player)
minetest.chat_send_player(player:get_player_name(), "[Tubelib2] param2 = "..node.param2.."/"..num.."/"..axis.."/"..rot)
end
local function chat_message(dir, cnt, peer_pos, peer_dir)
local sdir = tubelib2.dir_to_string(dir)
if Tube:secondary_node(peer_pos, peer_dir) then
local npos, node = Tube:get_node(peer_pos, peer_dir)
return "[Tubelib2] To the "..sdir..": "..cnt.." tube nodes to "..node.name.." at "..S(npos)
else
return "[Tubelib2] To the "..sdir..": "..cnt.." tube nodes to "..S(peer_pos)
end
end
local function repair_tubes(itemstack, placer, pointed_thing)
if pointed_thing.type == "node" then
local pos = pointed_thing.under
if placer:get_player_control().sneak then
local end_pos, dir = Tube:get_tube_end_pos(pos, 0)
if end_pos and dir then
minetest.chat_send_player(placer:get_player_name(), "[Tubelib2] end_pos = "..S(end_pos)..", dir = "..dir)
end
else
local t = minetest.get_us_time()
local pos1, pos2, dir1, dir2, cnt1, cnt2 = Tube:tool_repair_tubes(pos)
t = minetest.get_us_time() - t
print("time", t)
if pos1 and pos2 then
minetest.chat_send_player(placer:get_player_name(), "[Tubelib2] 1: "..S(pos1)..", dir = "..dir1..", "..cnt1.." tubes")
minetest.chat_send_player(placer:get_player_name(), "[Tubelib2] 2: "..S(pos2)..", dir = "..dir2..", "..cnt2.." tubes")
end
local t = minetest.get_us_time()
local dir1, dir2, fpos1, fpos2, fdir1, fdir2, cnt1, cnt2 =
Tube:tool_repair_tube(pos, placer, pointed_thing)
t = minetest.get_us_time() - t
print("time", t)
if fpos1 and fpos2 then
minetest.chat_send_player(placer:get_player_name(), chat_message(dir1, cnt1, fpos1, fdir1))
minetest.chat_send_player(placer:get_player_name(), chat_message(dir2, cnt2, fpos2, fdir2))
end
end
end