aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authororwell96 <orwell@bleipb.de>2023-03-19 17:19:40 +0100
committerorwell96 <orwell@bleipb.de>2023-05-27 12:26:14 +0200
commita14eb7fe737ffc4df4077a01f4859d74f62d0af2 (patch)
tree088ec0a6306ee415a01a084d41cb11dfa40e1b5e
parent6a5540878f334e97b78ef1430698a8bf8b3faa99 (diff)
downloadadvtrains-a14eb7fe737ffc4df4077a01f4859d74f62d0af2.tar.gz
advtrains-a14eb7fe737ffc4df4077a01f4859d74f62d0af2.tar.bz2
advtrains-a14eb7fe737ffc4df4077a01f4859d74f62d0af2.zip
TCB Xlinking added, to make nonconnected crossings possible
-rw-r--r--advtrains/helpers.lua2
-rw-r--r--advtrains_interlocking/database.lua168
-rwxr-xr-xadvtrains_interlocking/tcb_ts_ui.lua95
-rw-r--r--advtrains_interlocking/tool.lua13
4 files changed, 222 insertions, 56 deletions
diff --git a/advtrains/helpers.lua b/advtrains/helpers.lua
index 6d22bc5..7e078fb 100644
--- a/advtrains/helpers.lua
+++ b/advtrains/helpers.lua
@@ -297,6 +297,7 @@ end
function advtrains.get_adjacent_rail(this_posnr, this_conns_p, conn_idx, drives_on)
local this_pos = advtrains.round_vector_floor_y(this_posnr)
local this_conns = this_conns_p
+ local _
if not this_conns then
_, this_conns = advtrains.get_rail_info_at(this_pos)
end
@@ -583,6 +584,7 @@ function advtrains.get_track_iterator(initial_pos, initial_connid, limit, follow
table.insert(ti.branches, {pos = initial_pos, connid = coni, limit=limit})
end
end
+ ti.limit = limit -- safeguard if someone adds a branch before calling anything
setmetatable(ti, {__index=trackiter_mt})
return ti
end
diff --git a/advtrains_interlocking/database.lua b/advtrains_interlocking/database.lua
index 38fad77..35afc8d 100644
--- a/advtrains_interlocking/database.lua
+++ b/advtrains_interlocking/database.lua
@@ -163,6 +163,9 @@ TCB data structure
-- This is the "A" side of the TCB
[1] = { -- Variant: with adjacent TCs.
ts_id = <id> -- ID of the assigned track section
+ xlink = <other sigd> -- If two sections of track are not physically joined but must function as one TS (e.g. knights move crossing), a bidirectional link can be added with exactly one other TCB.
+ -- TS search will behave as if these two TCBs were physically connected.
+
signal = <pos> -- optional: when set, routes can be set from this tcb/direction and signal
-- aspect will be set accordingly.
routeset = <index in routes> -- Route set from this signal. This is the entry that is cleared once
@@ -249,17 +252,26 @@ function ildb.get_all_ts()
return track_sections
end
--- Checks the consistency of the track section at the given position
--- This method attempts to autorepair track sections if they are inconsistent
+-- Checks the consistency of the track section at the given position, attempts to autorepair track sections if they are inconsistent
+-- There are 2 operation modes:
+-- 1: pos is NOT a TCB, tcb_connid MUST be nil
+-- 2: pos is a TCB, tcb_connid MUST be given
-- @param pos: the position to start from
+-- @param tcb_connid: If provided node is a TCB,
-- Returns:
-- ts_id - the track section that was found
-- nil - No track section exists
-function ildb.check_and_repair_ts_at_pos(pos)
- atdebug("check_and_repair_ts_at_pos", pos)
+function ildb.check_and_repair_ts_at_pos(pos, tcb_connid)
+ atdebug("check_and_repair_ts_at_pos", pos, tcb_connid)
+ -- check prereqs
+ if ildb.get_tcb(pos) then
+ if not tcb_connid then error("check_and_repair_ts_at_pos: Startpoint is TCB, must provide tcb_connid!") end
+ else
+ if tcb_connid then error("check_and_repair_ts_at_pos: Startpoint is not TCB, must not provide tcb_connid!") end
+ end
-- STEP 1: Ensure that only one section is at this place
-- get all TCBs adjacent to this
- local all_tcbs = ildb.get_all_tcbs_adjacent(pos, nil)
+ local all_tcbs = ildb.get_all_tcbs_adjacent(pos, tcb_connid)
local first_ts = true
local ts_id
for _,sigd in ipairs(all_tcbs) do
@@ -321,41 +333,63 @@ end
-- This function does not trigger a repair.
-- @param inipos: the initial position
-- @param inidir: the initial direction, or nil to search in all directions
+-- @param per_track_callback: A callback function called with signature (pos, connid, bconnid) for every track encountered
-- Returns: a list of sigd's describing the TCBs found (sigd's point inward):
-- {p=<pos>, s=<side>, tcbs=<ref to tcbs table>}
-function ildb.get_all_tcbs_adjacent(inipos, inidir)
+function ildb.get_all_tcbs_adjacent(inipos, inidir, per_track_callback)
atdebug("get_all_tcbs_adjacent: inipos",inipos,"inidir",inidir,"")
local found_sigd = {}
local ti = advtrains.get_track_iterator(inipos, inidir, TS_MAX_SCAN, true)
+ -- if initial start is TCBS and has xlink, need to add that to the TI
+ local inisi = {p=inipos, s=inidir};
+ local initcbs = ildb.get_tcbs(inisi)
+ if initcbs then
+ ildb.validate_tcb_xlink(inisi, true)
+ if initcbs.xlink then
+ -- adding the tcb will happen when this branch is retrieved again using ti:next_branch()
+ atdebug("get_all_tcbs_adjacent: Putting xlink Branch for initial node",initcbs.xlink)
+ ti:add_branch(initcbs.xlink.p, initcbs.xlink.s)
+ end
+ end
local pos, connid, bconnid, tcb
while ti:has_next_branch() do
pos, connid = ti:next_branch()
--atdebug("get_all_tcbs_adjacent: BRANCH: ",pos, connid)
bconnid = nil
+ is_branch_start = true
repeat
+ -- callback
+ if per_track_callback then
+ per_track_callback(pos, connid, bconnid)
+ end
tcb = ildb.get_tcb(pos)
if tcb then
+ local using_connid = bconnid
-- found a tcb
- if not bconnid then
- -- A branch start point cannot be a TCB, as that would imply that it was a turnout/crossing (illegal)
- -- Only exception where this can happen is if the start point is a TCB, then we'd need to add the forward side of it to our list
- if pos.x==inipos.x and pos.y==inipos.y and pos.z==inipos.z then
- -- Note "connid" instead of "bconnid"
- atdebug("get_all_tcbs_adjacent: Found Startpoint TCB: ",pos, connid, "ts=", tcb[connid].ts_id)
- insert_sigd_if_not_present(found_sigd, {p=pos, s=connid, tcbs=tcb[connid]})
- else
- -- this may not happend
- error("Found TCB at TrackIterator new branch which is not the start point, this is illegal! pos="..minetest.pos_to_string(pos))
- end
-
- else
- -- add the sigd of this tcb and a reference to the tcb table in it
- atdebug("get_all_tcbs_adjacent: Found TCB: ",pos, bconnid, "ts=", tcb[bconnid].ts_id)
- insert_sigd_if_not_present(found_sigd, {p=pos, s=bconnid, tcbs=tcb[bconnid]})
+ if is_branch_start then
+ -- A branch cannot be a TCB, as that would imply that it was a turnout/crossing (illegal)
+ -- UNLESS: (a) it is the start point or (b) it was added via xlink
+ -- Then the correct conn to use is connid (pointing forward)
+ atdebug("get_all_tcbs_adjacent: Inserting TCB at branch start",pos, connid)
+ using_connid = connid
+ end
+ -- add the sigd of this tcb and a reference to the tcb table in it
+ atdebug("get_all_tcbs_adjacent: Found TCB: ",pos, using_connid, "ts=", tcb[using_connid].ts_id)
+ local si = {p=pos, s=using_connid, tcbs=tcb[using_connid]}
+ -- if xlink exists, add it now (only if we are not in branch start)
+ ildb.validate_tcb_xlink(si, true)
+ if not is_branch_start and si.tcbs.xlink then
+ -- adding the tcb will happen when this branch is retrieved again using ti:next_branch()
+ atdebug("get_all_tcbs_adjacent: Putting xlink Branch",si.tcbs.xlink)
+ ti:add_branch(si.tcbs.xlink.p, si.tcbs.xlink.s)
+ end
+ insert_sigd_if_not_present(found_sigd, si)
+ if not is_branch_start then
break
end
end
pos, connid, bconnid = ti:next_track()
+ is_branch_start = false
--atdebug("get_all_tcbs_adjacent: TRACK: ",pos, connid, bconnid)
until not pos
end
@@ -464,7 +498,7 @@ function ildb.tcbs_ensure_ts_ref_exists(sigd)
local did_insert = insert_sigd_if_not_present(ts.tc_breaks, {p=sigd.p, s=sigd.s})
if did_insert then
atdebug("tcbs_ensure_ts_ref_exists(",sigd,"): TCBS was missing reference in TS",tcbs.ts_id)
- ildb.update_ts_cache(ts_id)
+ ildb.update_ts_cache(tcbs.ts_id)
end
end
@@ -515,7 +549,7 @@ function ildb.update_ts_cache(ts_id)
end
local lntrans = { "A", "B" }
-local function sigd_to_string(sigd)
+function ildb.sigd_to_string(sigd)
return minetest.pos_to_string(sigd.p).." / "..lntrans[sigd.s]
end
@@ -546,12 +580,98 @@ function ildb.remove_tcb_at(pos)
if old_tcb[2].ts_id then
ildb.purge_ts_tcb_refs(old_tcb[2].ts_id)
end
+ -- update xlink partners
+ if old_tcb[1].xlink then
+ ildb.validate_tcb_xlink(old_tcb[1].xlink)
+ end
+ if old_tcb[2].xlink then
+ ildb.validate_tcb_xlink(old_tcb[2].xlink)
+ end
advtrains.interlocking.remove_tcb_marker(pos)
-- If needed, merge the track sections here
ildb.check_and_repair_ts_at_pos(pos)
return true
end
+-- Xlink: Connecting not-physically-connected sections handling
+
+-- Ensures that the xlink of this tcbs is bidirectional
+function ildb.validate_tcb_xlink(sigd, suppress_repairs)
+ local tcbs = sigd.tcbs or ildb.get_tcbs(sigd)
+ local osigd = tcbs.xlink
+ if not osigd then return end
+ local otcbs = ildb.get_tcbs(tcbs.xlink)
+ if not otcbs then
+ atdebug("validate_tcb_xlink",sigd,": Link partner ",osigd,"orphaned")
+ tcbs.xlink = nil
+ if not suppress_repairs then
+ ildb.check_and_repair_ts_at_pos(sigd.p, sigd.s)
+ end
+ return
+ end
+ if otcbs.xlink then
+ if not vector.equals(otcbs.xlink.p, sigd.p) or otcbs.xlink.s~=sigd.s then
+ atdebug("validate_tcb_xlink",sigd,": Link partner ",osigd,"backreferencing to someone else (namely",otcbs.xlink,") clearing it")
+ tcbs.xlink = nil
+ if not suppress_repairs then
+ ildb.check_and_repair_ts_at_pos(sigd.p, sigd.s)
+ atdebug("validate_tcb_xlink",sigd,": Link partner ",osigd," was backreferencing to someone else, now updating that")
+ ildb.validate_tcb_xlink(osigd)
+ end
+ end
+ else
+ atdebug("validate_tcb_xlink",sigd,": Link partner ",osigd,"wasn't backreferencing, clearing it")
+ tcbs.xlink = nil
+ if not suppress_repairs then
+ ildb.check_and_repair_ts_at_pos(sigd.p, sigd.s)
+ end
+ end
+end
+
+function ildb.add_tcb_xlink(sigd1, sigd2)
+ atdebug("add_tcb_xlink",sigd1, sigd2)
+ local tcbs1 = sigd1.tcbs or ildb.get_tcbs(sigd1)
+ local tcbs2 = sigd2.tcbs or ildb.get_tcbs(sigd2)
+ if vector.equals(sigd1.p, sigd2.p) then
+ atdebug("add_tcb_xlink Cannot xlink with same TCB")
+ return
+ end
+ if not tcbs1 or not tcbs2 then
+ atdebug("add_tcb_xlink TCBS doesnt exist")
+ return
+ end
+ if tcbs1.xlink or tcbs2.xlink then
+ atdebug("add_tcb_xlink One already linked")
+ return
+ end
+ -- apply link
+ tcbs1.xlink = {p=sigd2.p, s=sigd2.s}
+ tcbs2.xlink = {p=sigd1.p, s=sigd1.s}
+ -- update section. It should be sufficient to call update only once because the TCBs are linked anyway now
+ ildb.check_and_repair_ts_at_pos(sigd1.p, sigd1.s)
+end
+
+function ildb.remove_tcb_xlink(sigd)
+ atdebug("remove_tcb_xlink",sigd)
+ -- Validate first. If Xlink is gone already then, nothing to do
+ ildb.validate_tcb_xlink(sigd)
+ -- Checking all of these already done by validate
+ local tcbs = sigd.tcbs or ildb.get_tcbs(sigd)
+ local osigd = tcbs.xlink
+ if not osigd then
+ -- validate already cleared us
+ atdebug("remove_tcb_xlink: Already gone by validate")
+ return
+ end
+ local otcbs = ildb.get_tcbs(tcbs.xlink)
+ -- clear it
+ otcbs.xlink = nil
+ tcbs.xlink = nil
+ -- Update section for ourself and the other one
+ ildb.check_and_repair_ts_at_pos(sigd.p, sigd.s)
+ ildb.check_and_repair_ts_at_pos(osigd.p, osigd.s)
+end
+
function ildb.create_ts_from_tcbs(sigd)
atdebug("create_ts_from_tcbs",sigd)
local all_tcbs = ildb.get_all_tcbs_adjacent(sigd.p, sigd.s)
diff --git a/advtrains_interlocking/tcb_ts_ui.lua b/advtrains_interlocking/tcb_ts_ui.lua
index 97c28a8..08d1c32 100755
--- a/advtrains_interlocking/tcb_ts_ui.lua
+++ b/advtrains_interlocking/tcb_ts_ui.lua
@@ -2,6 +2,7 @@
local players_assign_tcb = {}
local players_assign_signal = {}
+local players_assign_xlink = {}
local players_link_ts = {}
local ildb = advtrains.interlocking.db
@@ -166,6 +167,7 @@ minetest.register_on_punchnode(function(pos, node, player, pointed_thing)
meta:set_string("tcb_pos", minetest.pos_to_string(pos))
meta:set_string("infotext", "TCB assigned to "..minetest.pos_to_string(pos))
minetest.chat_send_player(pname, "Configuring TCB: Successfully configured TCB")
+ advtrains.interlocking.show_tcb_marker(pos)
else
minetest.chat_send_player(pname, "Configuring TCB: This is not a normal two-connection rail! Aborted.")
end
@@ -213,9 +215,14 @@ end)
-- TCB Form
-local function mktcbformspec(tcbs, btnpref, offset, pname)
+local function mktcbformspec(pos, side, tcbs, offset, pname)
local form = ""
+ local btnpref = side==1 and "A" or "B"
local ts
+ -- ensure that mapping and xlink are up to date
+ ildb.tcbs_ensure_ts_ref_exists({p=pos, s=side, tcbs=tcbs})
+ ildb.validate_tcb_xlink({p=pos, s=side, tcbs=tcbs})
+ -- Note: repair operations may have been triggered by this
if tcbs.ts_id then
ts = ildb.get_ts(tcbs.ts_id)
end
@@ -227,6 +234,19 @@ local function mktcbformspec(tcbs, btnpref, offset, pname)
form = form.."label[0.5,"..offset..";Side "..btnpref..": ".."End of interlocking]"
form = form.."button[0.5,"..(offset+0.5)..";5,1;"..btnpref.."_makeil;Create Interlocked Track Section]"
end
+ -- xlink
+ if tcbs.xlink then
+ form = form.."label[0.5,"..(offset+1.5)..";Link:"..ildb.sigd_to_string(tcbs.xlink).."]"
+ form = form.."button[4.5,"..(offset+1.5)..";1,1;"..btnpref.."_xlinkdel;X]"
+ else
+ if players_assign_xlink[pname] then
+ form = form.."button[0.5,"..(offset+1.5)..";4,1;"..btnpref.."_xlinklink;Link "..ildb.sigd_to_string(players_assign_xlink[pname]).."]"
+ form = form.."button[4.5,"..(offset+1.5)..";1,1;"..btnpref.."_xlinkabrt;X]"
+ else
+ form = form.."label[0.5,"..(offset+1.5)..";No Link]"
+ form = form.."button[4.5,"..(offset+1.5)..";1,1;"..btnpref.."_xlinkadd;+]"
+ end
+ end
if tcbs.signal then
form = form.."button[0.5,"..(offset+2.5)..";5,1;"..btnpref.."_sigdia;Signalling]"
else
@@ -245,8 +265,8 @@ function advtrains.interlocking.show_tcb_form(pos, pname)
if not tcb then return end
local form = "size[6,9] label[0.5,0.5;Track Circuit Break Configuration]"
- form = form .. mktcbformspec(tcb[1], "A", 1, pname)
- form = form .. mktcbformspec(tcb[2], "B", 5, pname)
+ form = form .. mktcbformspec(pos, 1, tcb[1], 1, pname)
+ form = form .. mktcbformspec(pos, 2, tcb[2], 5, pname)
minetest.show_formspec(pname, "at_il_tcbconfig_"..minetest.pos_to_string(pos), form)
advtrains.interlocking.show_tcb_marker(pos)
@@ -276,6 +296,10 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
local f_makeil = {fields.A_makeil, fields.B_makeil}
local f_asnsig = {fields.A_asnsig, fields.B_asnsig}
local f_sigdia = {fields.A_sigdia, fields.B_sigdia}
+ local f_xlinkadd = {fields.A_xlinkadd, fields.B_xlinkadd}
+ local f_xlinkdel = {fields.A_xlinkdel, fields.B_xlinkdel}
+ local f_xlinklink = {fields.A_xlinklink, fields.B_xlinklink}
+ local f_xlinkabrt = {fields.A_xlinkabrt, fields.B_xlinkabrt}
for connid=1,2 do
local tcbs = tcb[connid]
@@ -291,6 +315,28 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end
end
end
+ if tcbs.xlink then
+ if f_xlinkdel[connid] then
+ ildb.remove_tcb_xlink({p=pos, s=connid})
+ end
+ else
+ local osigd = players_assign_xlink[pname]
+ if osigd then
+ if f_xlinklink[connid] then
+ ildb.add_tcb_xlink({p=pos, s=connid}, osigd)
+ players_assign_xlink[pname] = nil
+ elseif f_xlinkabrt[connid] then
+ players_assign_xlink[pname] = nil
+ end
+ else
+ if f_xlinkadd[connid] then
+ players_assign_xlink[pname] = {p=pos, s=connid}
+ minetest.chat_send_player(pname, "TCB Link: Select linked TCB now!")
+ minetest.close_formspec(pname, formname)
+ return -- to not reopen form
+ end
+ end
+ end
if f_asnsig[connid] and not tcbs.signal then
minetest.chat_send_player(pname, "Configuring TCB: Please punch the signal to assign.")
players_assign_signal[pname] = {p=pos, s=connid}
@@ -496,36 +542,25 @@ function advtrains.interlocking.remove_tcb_marker(pos)
markerent[pts] = nil
end
+local ts_showparticles_callback = function(pos, connid, bconnid)
+ minetest.add_particle({
+ pos = pos,
+ velocity = {x=0, y=0, z=0},
+ acceleration = {x=0, y=0, z=0},
+ expirationtime = 10,
+ size = 7,
+ vertical = true,
+ texture = "at_il_ts_highlight_particle.png",
+ glow = 6,
+ })
+end
+
-- Spawns particles to highlight the clicked track section
-- TODO: Adapt behavior to not dumb-walk anymore
function advtrains.interlocking.highlight_track_section(pos)
- local ti = advtrains.get_track_iterator(pos, nil, 100, true)
- local pos, connid, bconnid, tcb
- while ti:has_next_branch() do
- pos, connid = ti:next_branch()
- --atdebug("highlight_track_section: BRANCH: ",pos, connid)
- bconnid = nil
- repeat
- -- spawn particles
- minetest.add_particle({
- pos = pos,
- velocity = {x=0, y=0, z=0},
- acceleration = {x=0, y=0, z=0},
- expirationtime = 10,
- size = 7,
- vertical = true,
- texture = "at_il_ts_highlight_particle.png",
- glow = 6,
- })
- -- abort if TCB is found
- tcb = ildb.get_tcb(pos)
- if tcb then
- advtrains.interlocking.show_tcb_marker(pos)
- break
- end
- pos, connid, bconnid = ti:next_track()
- --atdebug("highlight_track_section: TRACK: ",pos, connid, bconnid)
- until not pos
+ local all_tcbs = ildb.get_all_tcbs_adjacent(pos, nil, ts_showparticles_callback)
+ for _,sigd in ipairs(all_tcbs) do
+ advtrains.interlocking.show_tcb_marker(sigd.p)
end
end
diff --git a/advtrains_interlocking/tool.lua b/advtrains_interlocking/tool.lua
index 4b701b4..6723f88 100644
--- a/advtrains_interlocking/tool.lua
+++ b/advtrains_interlocking/tool.lua
@@ -26,7 +26,11 @@ local function node_right_click(pos, pname)
local node_ok, conns, rail_y=advtrains.get_rail_info_at(pos)
if not node_ok then
minetest.chat_send_player(pname, "Node is not a track!")
- return
+ return
+ end
+ if advtrains.interlocking.db.get_tcb(pos) then
+ advtrains.interlocking.show_tcb_form(pos, pname)
+ return
end
local ts_id = advtrains.interlocking.db.check_and_repair_ts_at_pos(pos)
@@ -41,7 +45,12 @@ local function node_left_click(pos, pname)
local node_ok, conns, rail_y=advtrains.get_rail_info_at(pos)
if not node_ok then
minetest.chat_send_player(pname, "Node is not a track!")
- return
+ return
+ end
+
+ if advtrains.interlocking.db.get_tcb(pos) then
+ advtrains.interlocking.show_tcb_marker(pos)
+ return
end
local ts_id = advtrains.interlocking.db.check_and_repair_ts_at_pos(pos)