Auto-Repair Track Sections/TCBs (automatically when adding/removing or triggered by interlocking tool)
This commit is contained in:
parent
f284d395d7
commit
6a5540878f
|
@ -342,9 +342,10 @@ function advtrains.get_matching_conn(conn, nconns)
|
||||||
return connlku[nconns][conn]
|
return connlku[nconns][conn]
|
||||||
end
|
end
|
||||||
|
|
||||||
function advtrains.random_id()
|
function advtrains.random_id(lenp)
|
||||||
local idst=""
|
local idst=""
|
||||||
for i=0,5 do
|
local len = lenp or 6
|
||||||
|
for i=1,len do
|
||||||
idst=idst..(math.random(0,9))
|
idst=idst..(math.random(0,9))
|
||||||
end
|
end
|
||||||
return idst
|
return idst
|
||||||
|
@ -476,6 +477,14 @@ end
|
||||||
|
|
||||||
-- Metatable:
|
-- Metatable:
|
||||||
local trackiter_mt = {
|
local trackiter_mt = {
|
||||||
|
-- Internal State:
|
||||||
|
-- branches: A list of {pos, connid, limit} for where to restart
|
||||||
|
-- pos: The *next* position that the track iterator will return
|
||||||
|
-- bconnid: The connid of the connection of the rail at pos that points backward
|
||||||
|
-- tconns: The connections of the rail at pos
|
||||||
|
-- limit: the current limit
|
||||||
|
-- visited: a key-boolean table of already visited rails
|
||||||
|
|
||||||
-- get whether there are still unprocessed branches
|
-- get whether there are still unprocessed branches
|
||||||
has_next_branch = function(self)
|
has_next_branch = function(self)
|
||||||
return #self.branches > 0
|
return #self.branches > 0
|
||||||
|
@ -491,13 +500,18 @@ local trackiter_mt = {
|
||||||
self.tconns = adj_conns
|
self.tconns = adj_conns
|
||||||
self.limit = br.limit - 1
|
self.limit = br.limit - 1
|
||||||
self.visited[advtrains.encode_pos(br.pos)] = true
|
self.visited[advtrains.encode_pos(br.pos)] = true
|
||||||
|
self.last_track_already_visited = false
|
||||||
return br.pos, br.connid
|
return br.pos, br.connid
|
||||||
end,
|
end,
|
||||||
-- get the next track along the current branch,
|
-- get the next track along the current branch,
|
||||||
-- potentially adding branching tracks to the unprocessed branches list
|
-- potentially adding branching tracks to the unprocessed branches list
|
||||||
-- returns status, track_pos, track_connid
|
-- returns track_pos, track_connid, track_backwards_connid
|
||||||
-- status is true(ok), false(track has ended), nil(traversing limit has been reached) (when status~=true, track_pos and track_connid are nil)
|
-- On error, returns nil, reason; reason is one of "track_end", "limit_hit", "already_visited"
|
||||||
next_track = function(self)
|
next_track = function(self)
|
||||||
|
if self.last_track_already_visited then
|
||||||
|
-- see comment below
|
||||||
|
return nil, "already_visited"
|
||||||
|
end
|
||||||
local pos = self.pos
|
local pos = self.pos
|
||||||
if not pos then
|
if not pos then
|
||||||
-- last run found track end. Return false
|
-- last run found track end. Return false
|
||||||
|
@ -507,12 +521,17 @@ local trackiter_mt = {
|
||||||
if self.limit <= 0 then
|
if self.limit <= 0 then
|
||||||
return nil, "limit_hit"
|
return nil, "limit_hit"
|
||||||
end
|
end
|
||||||
if self.visited[advtrains.encode_pos(pos)] then
|
|
||||||
-- node was already seen. do not continue
|
|
||||||
return nil, "already_visited"
|
|
||||||
end
|
|
||||||
-- select next conn (main conn to follow is the associated connection)
|
-- select next conn (main conn to follow is the associated connection)
|
||||||
|
local old_bconnid = self.bconnid
|
||||||
local mconnid = advtrains.get_matching_conn(self.bconnid, #self.tconns)
|
local mconnid = advtrains.get_matching_conn(self.bconnid, #self.tconns)
|
||||||
|
if self.visited[advtrains.encode_pos(pos)] then
|
||||||
|
-- node was already seen
|
||||||
|
-- Due to special requirements for the track section updater, return this first already visited track once
|
||||||
|
-- but do not process any further rails on this branch
|
||||||
|
-- The next call will then throw already_visited error
|
||||||
|
self.last_track_already_visited = true
|
||||||
|
return pos, mconnid, old_bconnid
|
||||||
|
end
|
||||||
-- If there are more connections, add these to branches
|
-- If there are more connections, add these to branches
|
||||||
for nconnid,_ in ipairs(self.tconns) do
|
for nconnid,_ in ipairs(self.tconns) do
|
||||||
if nconnid~=mconnid and nconnid~=self.bconnid then
|
if nconnid~=mconnid and nconnid~=self.bconnid then
|
||||||
|
@ -526,7 +545,16 @@ local trackiter_mt = {
|
||||||
self.tconns = adj_conns
|
self.tconns = adj_conns
|
||||||
self.limit = self.limit - 1
|
self.limit = self.limit - 1
|
||||||
self.visited[advtrains.encode_pos(pos)] = true
|
self.visited[advtrains.encode_pos(pos)] = true
|
||||||
return pos, mconnid
|
self.last_track_already_visited = false
|
||||||
|
return pos, mconnid, old_bconnid
|
||||||
|
end,
|
||||||
|
|
||||||
|
add_branch = function(self, pos, connid)
|
||||||
|
table.insert(self.branches, {pos = pos, connid = connid, limit=self.limit})
|
||||||
|
end,
|
||||||
|
|
||||||
|
is_visited = function(self, pos)
|
||||||
|
return self.visited[advtrains.encode_pos(pos)]
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -569,8 +597,8 @@ while ti:has_next_branch() do
|
||||||
repeat
|
repeat
|
||||||
<do something with the track>
|
<do something with the track>
|
||||||
if <track satisfies an abort condition> then break end --for example, when traversing should stop at TCBs this can check if there is a tcb here
|
if <track satisfies an abort condition> then break end --for example, when traversing should stop at TCBs this can check if there is a tcb here
|
||||||
ok, pos, connid = ti:next_track()
|
pos, connid = ti:next_track()
|
||||||
until not ok -- this stops the loop when either the track end is reached or the limit is hit
|
until not pos -- this stops the loop when either the track end is reached or the limit is hit
|
||||||
-- while loop continues with the next branch ( diverging branch of one of the switches/crossings) until no more are left
|
-- while loop continues with the next branch ( diverging branch of one of the switches/crossings) until no more are left
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,22 @@ Another case of this:
|
||||||
The / here is a non-interlocked turnout (to a non-frequently used siding). For some reason, there is no exit node there,
|
The / here is a non-interlocked turnout (to a non-frequently used siding). For some reason, there is no exit node there,
|
||||||
so the route is set to the signal at the right end. The train is taking the exit to the siding and frees the TC, without ever
|
so the route is set to the signal at the right end. The train is taking the exit to the siding and frees the TC, without ever
|
||||||
having touched the right TC.
|
having touched the right TC.
|
||||||
|
|
||||||
|
|
||||||
|
== Terminology / Variable Names ==
|
||||||
|
|
||||||
|
"tcb" : A TCB table (as in track_circuit_breaks)
|
||||||
|
"tcbs" : One side of a tcb (that is tcb == {[1] = tcbs, [2] = tcbs})
|
||||||
|
"sigd" : A table of format {p=<position>, s=<side aka connid>} by which a "tcbs" is uniqely identified.
|
||||||
|
|
||||||
|
== Section Autorepair & Turnout Cache ==
|
||||||
|
|
||||||
|
As fundamental part of reworked route programming mechanism, Track Section objects become weak now. They are created and destroyed on demand.
|
||||||
|
ildb.repair_tcb automatically checks all nearby sections for issues and repairs them automatically.
|
||||||
|
|
||||||
|
Also the database now holds a cache of the turnouts in the section and their position for all possible driving paths.
|
||||||
|
Every time a repair operation takes place, and on every track edit operation, the affected sections need to have their cache updated.
|
||||||
|
|
||||||
]]--
|
]]--
|
||||||
|
|
||||||
local TRAVERSER_LIMIT = 1000
|
local TRAVERSER_LIMIT = 1000
|
||||||
|
@ -59,7 +75,22 @@ advtrains.interlocking.npr_rails = {}
|
||||||
function ildb.load(data)
|
function ildb.load(data)
|
||||||
if not data then return end
|
if not data then return end
|
||||||
if data.tcbs then
|
if data.tcbs then
|
||||||
track_circuit_breaks = data.tcbs
|
if data.tcbpts_conversion_applied then
|
||||||
|
track_circuit_breaks = data.tcbs
|
||||||
|
else
|
||||||
|
-- Convert legacy pos_to_string tcbs to new advtrains.encode_pos position strings
|
||||||
|
for pts, tcb in pairs(data.tcbs) do
|
||||||
|
local pos = minetest.string_to_pos(pts)
|
||||||
|
if pos then
|
||||||
|
-- that was a pos_to_string
|
||||||
|
local epos = advtrains.encode_pos(pos)
|
||||||
|
track_circuit_breaks[epos] = tcb
|
||||||
|
else
|
||||||
|
-- keep entry, it is already new
|
||||||
|
track_circuit_breaks[pts] = tcb
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
if data.ts then
|
if data.ts then
|
||||||
track_sections = data.ts
|
track_sections = data.ts
|
||||||
|
@ -121,6 +152,7 @@ function ildb.save()
|
||||||
rs_callbacks = advtrains.interlocking.route.rte_callbacks,
|
rs_callbacks = advtrains.interlocking.route.rte_callbacks,
|
||||||
influence_points = influence_points,
|
influence_points = influence_points,
|
||||||
npr_rails = advtrains.interlocking.npr_rails,
|
npr_rails = advtrains.interlocking.npr_rails,
|
||||||
|
tcbpts_conversion_applied = true, -- remark that legacy pos conversion has taken place
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -147,8 +179,6 @@ TCB data structure
|
||||||
-- This is the "B" side of the TCB
|
-- This is the "B" side of the TCB
|
||||||
[2] = { -- Variant: end of track-circuited area (initial state of TC)
|
[2] = { -- Variant: end of track-circuited area (initial state of TC)
|
||||||
ts_id = nil, -- this is the indication for end_of_interlocking
|
ts_id = nil, -- this is the indication for end_of_interlocking
|
||||||
section_free = <boolean>, --this can be set by an exit node via mesecons or atlatc,
|
|
||||||
-- or from the tc formspec.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +186,11 @@ Track section
|
||||||
[id] = {
|
[id] = {
|
||||||
name = "Some human-readable name"
|
name = "Some human-readable name"
|
||||||
tc_breaks = { <signal specifier>,... } -- Bounding TC's (signal specifiers)
|
tc_breaks = { <signal specifier>,... } -- Bounding TC's (signal specifiers)
|
||||||
-- Can be direct ends (auto-detected), conflicting routes or TCBs that are too far away from each other
|
rs_cache = { [<x>-<y>] = { [<encoded pos>] = "state" } }
|
||||||
|
-- Saves the turnout states that need to be locked when a route is set from tcb#x to tcb#y
|
||||||
|
-- e.g. "1-2" = { "800080008000" = "st" }
|
||||||
|
-- Recalculated on every change via update_ts_cache
|
||||||
|
|
||||||
route = {
|
route = {
|
||||||
origin = <signal>, -- route origin
|
origin = <signal>, -- route origin
|
||||||
entry = <sigd>, -- supposed train entry point
|
entry = <sigd>, -- supposed train entry point
|
||||||
|
@ -172,7 +206,9 @@ Track section
|
||||||
-- first says whether to clear the routesetting status from the origin signal.
|
-- first says whether to clear the routesetting status from the origin signal.
|
||||||
-- locks contains the positions where locks are held by this ts.
|
-- locks contains the positions where locks are held by this ts.
|
||||||
-- 'route' is cleared when train enters the section, while 'route_post' cleared when train leaves section.
|
-- 'route' is cleared when train enters the section, while 'route_post' cleared when train leaves section.
|
||||||
|
|
||||||
trains = {<id>, ...} -- Set whenever a train (or more) reside in this TC
|
trains = {<id>, ...} -- Set whenever a train (or more) reside in this TC
|
||||||
|
-- Note: The same train ID may be contained in this mapping multiple times, when it has entered the section in two different places.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -185,24 +221,13 @@ signal_assignments = {
|
||||||
}
|
}
|
||||||
]]
|
]]
|
||||||
|
|
||||||
|
-- Maximum scan length for track iterator
|
||||||
|
local TS_MAX_SCAN = 1000
|
||||||
|
|
||||||
--
|
-- basic functions
|
||||||
function ildb.create_tcb(pos)
|
|
||||||
local new_tcb = {
|
|
||||||
[1] = {},
|
|
||||||
[2] = {},
|
|
||||||
}
|
|
||||||
local pts = advtrains.roundfloorpts(pos)
|
|
||||||
if not track_circuit_breaks[pts] then
|
|
||||||
track_circuit_breaks[pts] = new_tcb
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function ildb.get_tcb(pos)
|
function ildb.get_tcb(pos)
|
||||||
local pts = advtrains.roundfloorpts(pos)
|
local pts = advtrains.encode_pos(pos)
|
||||||
return track_circuit_breaks[pts]
|
return track_circuit_breaks[pts]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -212,99 +237,281 @@ function ildb.get_tcbs(sigd)
|
||||||
return tcb[sigd.s]
|
return tcb[sigd.s]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function ildb.create_ts(sigd)
|
|
||||||
local tcbs = ildb.get_tcbs(sigd)
|
|
||||||
local id = advtrains.random_id()
|
|
||||||
|
|
||||||
while track_sections[id] do
|
|
||||||
id = advtrains.random_id()
|
|
||||||
end
|
|
||||||
|
|
||||||
track_sections[id] = {
|
|
||||||
name = "Section "..id,
|
|
||||||
tc_breaks = { sigd }
|
|
||||||
}
|
|
||||||
tcbs.ts_id = id
|
|
||||||
end
|
|
||||||
|
|
||||||
function ildb.get_ts(id)
|
function ildb.get_ts(id)
|
||||||
return track_sections[id]
|
return track_sections[id]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- retrieve full tables. Please use only read-only!
|
||||||
|
function ildb.get_all_tcb()
|
||||||
-- various helper functions handling sigd's
|
return track_circuit_breaks
|
||||||
local sigd_equal = advtrains.interlocking.sigd_equal
|
end
|
||||||
local function insert_sigd_nodouble(list, sigd)
|
function ildb.get_all_ts()
|
||||||
for idx, cmp in pairs(list) do
|
return track_sections
|
||||||
if sigd_equal(sigd, cmp) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
table.insert(list, sigd)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Checks the consistency of the track section at the given position
|
||||||
-- This function will actually handle the node that is in connid direction from the node at pos
|
-- This method attempts to autorepair track sections if they are inconsistent
|
||||||
-- so, this needs the conns of the node at pos, since these are already calculated
|
-- @param pos: the position to start from
|
||||||
local function traverser(found_tcbs, pos, conns, connid, count, brk_when_found_n)
|
-- Returns:
|
||||||
local adj_pos, adj_connid, conn_idx, nextrail_y, next_conns = advtrains.get_adjacent_rail(pos, conns, connid, advtrains.all_tracktypes)
|
-- ts_id - the track section that was found
|
||||||
if not adj_pos then
|
-- nil - No track section exists
|
||||||
--atdebug("Traverser found end-of-track at",pos, connid)
|
function ildb.check_and_repair_ts_at_pos(pos)
|
||||||
return
|
atdebug("check_and_repair_ts_at_pos", pos)
|
||||||
end
|
-- STEP 1: Ensure that only one section is at this place
|
||||||
-- look whether there is a TCB here
|
-- get all TCBs adjacent to this
|
||||||
if #next_conns == 2 then --if not, don't even try!
|
local all_tcbs = ildb.get_all_tcbs_adjacent(pos, nil)
|
||||||
local tcb = ildb.get_tcb(adj_pos)
|
local first_ts = true
|
||||||
if tcb then
|
local ts_id
|
||||||
-- done with this branch
|
for _,sigd in ipairs(all_tcbs) do
|
||||||
--atdebug("Traverser found tcb at",adj_pos, adj_connid)
|
ildb.tcbs_ensure_ts_ref_exists(sigd)
|
||||||
insert_sigd_nodouble(found_tcbs, {p=adj_pos, s=adj_connid})
|
local tcbs_ts_id = sigd.tcbs.ts_id
|
||||||
return
|
if first_ts then
|
||||||
end
|
-- this one determines
|
||||||
end
|
ts_id = tcbs_ts_id
|
||||||
-- recursion abort condition
|
first_ts = false
|
||||||
if count > TRAVERSER_LIMIT then
|
else
|
||||||
--atdebug("Traverser hit counter at",adj_pos, adj_connid)
|
-- these must be the same as the first
|
||||||
return true
|
if ts_id ~= tcbs_ts_id then
|
||||||
end
|
-- inconsistency is found, repair it
|
||||||
-- continue traversing
|
atdebug("check_and_repair_ts_at_pos: Inconsistency is found!")
|
||||||
local counter_hit = false
|
return ildb.repair_ts_merge_all(all_tcbs)
|
||||||
for nconnid, nconn in ipairs(next_conns) do
|
-- Step2 check is no longer necessary since we just created that new section
|
||||||
if adj_connid ~= nconnid then
|
|
||||||
counter_hit = counter_hit or traverser(found_tcbs, adj_pos, next_conns, nconnid, count + 1, brk_when_found_n)
|
|
||||||
if brk_when_found_n and #found_tcbs>=brk_when_found_n then
|
|
||||||
break
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return counter_hit
|
-- only one found (it is either nil or a ts id)
|
||||||
|
atdebug("check_and_repair_ts_at_pos: TS consistent id=",ts_id,"")
|
||||||
|
if not ts_id then
|
||||||
|
return
|
||||||
|
-- All TCBs agreed that there is no section here.
|
||||||
|
end
|
||||||
|
|
||||||
|
local ts = ildb.get_ts(ts_id)
|
||||||
|
if not ts then
|
||||||
|
-- This branch may never be reached, because ildb.tcbs_ensure_ts_ref_exists(sigd) is already supposed to clear out missing sections
|
||||||
|
error("check_and_repair_ts_at_pos: Resolved to nonexisting section although ildb.tcbs_ensure_ts_ref_exists(sigd) was supposed to prevent this. Panic!")
|
||||||
|
end
|
||||||
|
ildb.purge_ts_tcb_refs(ts_id)
|
||||||
|
-- STEP 2: Ensure that all_tcbs is equal to the track section's TCB list. If there are extra TCBs then the section should be split
|
||||||
|
-- ildb.tcbs_ensure_ts_ref_exists(sigd) has already make sure that all tcbs are found in the ts's tc_breaks list
|
||||||
|
-- That means it is sufficient to compare the LENGTHS of both lists, if one is longer then it is inconsistent
|
||||||
|
if #ts.tc_breaks ~= #all_tcbs then
|
||||||
|
atdebug("check_and_repair_ts_at_pos: Partition is found!")
|
||||||
|
return ildb.repair_ts_merge_all(all_tcbs)
|
||||||
|
end
|
||||||
|
return ts_id
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Helper function to prevent duplicates
|
||||||
|
local function insert_sigd_if_not_present(tab, sigd)
|
||||||
|
local found = false
|
||||||
|
for _, ssigd in ipairs(tab) do
|
||||||
|
if vector.equals(sigd.p, ssigd.p) and sigd.s==ssigd.s then
|
||||||
|
found = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not found then
|
||||||
|
table.insert(tab, sigd)
|
||||||
|
end
|
||||||
|
return not found
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Starting from a position, search all TCBs that can be reached from this position.
|
||||||
|
-- In a non-faulty setup, all of these should have the same track section assigned.
|
||||||
|
-- This function does not trigger a repair.
|
||||||
|
-- @param inipos: the initial position
|
||||||
|
-- @param inidir: the initial direction, or nil to search in all directions
|
||||||
|
-- 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)
|
||||||
|
atdebug("get_all_tcbs_adjacent: inipos",inipos,"inidir",inidir,"")
|
||||||
|
local found_sigd = {}
|
||||||
|
local ti = advtrains.get_track_iterator(inipos, inidir, TS_MAX_SCAN, true)
|
||||||
|
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
|
||||||
|
repeat
|
||||||
|
tcb = ildb.get_tcb(pos)
|
||||||
|
if tcb then
|
||||||
|
-- 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]})
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
pos, connid, bconnid = ti:next_track()
|
||||||
|
--atdebug("get_all_tcbs_adjacent: TRACK: ",pos, connid, bconnid)
|
||||||
|
until not pos
|
||||||
|
end
|
||||||
|
return found_sigd
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Called by frontend functions when multiple tcbs's that logically belong to one section have been determined to have different sections
|
||||||
|
-- Parameter is the output of ildb.get_all_tcbs_adjacent(pos)
|
||||||
|
-- Returns the ID of the track section that results after the merge
|
||||||
|
function ildb.repair_ts_merge_all(all_tcbs, force_create)
|
||||||
|
atdebug("repair_ts_merge_all: Instructed to merge sections of following TCBs:")
|
||||||
|
-- The first loop does the following for each TCBS:
|
||||||
|
-- a) Store the TS ID in the set of TS to update
|
||||||
|
-- b) Set the TS ID to nil, so that the TCBS gets removed from the section
|
||||||
|
local ts_to_update = {}
|
||||||
|
local ts_name_repo = {}
|
||||||
|
local any_ts = false
|
||||||
|
for _,sigd in ipairs(all_tcbs) do
|
||||||
|
local ts_id = sigd.tcbs.ts_id
|
||||||
|
atdebug(sigd, "ts=", ts_id)
|
||||||
|
if ts_id then
|
||||||
|
local ts = track_sections[ts_id]
|
||||||
|
if ts then
|
||||||
|
any_ts = true
|
||||||
|
ts_to_update[ts_id] = true
|
||||||
|
-- if nonstandard name, store this
|
||||||
|
if ts.name and not string.match(ts.name, "^Section") then
|
||||||
|
ts_name_repo[#ts_name_repo+1] = ts.name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
sigd.tcbs.ts_id = nil
|
||||||
|
end
|
||||||
|
if not any_ts and not force_create then
|
||||||
|
-- nothing to do at all, just no interlocking. Why were we even called
|
||||||
|
atdebug("repair_ts_merge_all: No track section present, will not create a new one")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
-- Purge every TS in turn. TS's that are now empty will be deleted. TS's that still have TCBs will be kept
|
||||||
|
for ts_id, _ in pairs(ts_to_update) do
|
||||||
|
local remain_ts = ildb.purge_ts_tcb_refs(ts_id)
|
||||||
|
end
|
||||||
|
-- Create a new fresh track section with all the TCBs we have in our collection
|
||||||
|
local new_ts_id, new_ts = ildb.create_ts_from_tcb_list(all_tcbs)
|
||||||
|
return new_ts_id
|
||||||
|
end
|
||||||
|
|
||||||
|
-- For the specified TS, go through the list of TCBs and purge all TCBs that have no corresponding backreference in their TCBS table.
|
||||||
|
-- If the track section ends up empty, it is deleted in this process.
|
||||||
|
-- Should the track section still exist after the purge operation, it is returned.
|
||||||
|
function ildb.purge_ts_tcb_refs(ts_id)
|
||||||
|
local ts = track_sections[ts_id]
|
||||||
|
if not ts then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local has_changed = false
|
||||||
|
local i = 1
|
||||||
|
while ts.tc_breaks[i] do
|
||||||
|
-- get TCBS
|
||||||
|
local sigd = ts.tc_breaks[i]
|
||||||
|
local tcbs = ildb.get_tcbs(sigd)
|
||||||
|
if tcbs then
|
||||||
|
if tcbs.ts_id == ts_id then
|
||||||
|
-- this one is legit
|
||||||
|
i = i+1
|
||||||
|
else
|
||||||
|
-- this one is to be purged
|
||||||
|
atdebug("purge_ts_tcb_refs(",ts_id,"): purging",sigd,"(backreference = ",tcbs.ts_id,")")
|
||||||
|
table.remove(ts.tc_breaks, i)
|
||||||
|
has_changed = true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- if not tcbs: was anyway an orphan, remove it
|
||||||
|
atdebug("purge_ts_tcb_refs(",ts_id,"): purging",sigd,"(referred nonexisting TCB)")
|
||||||
|
table.remove(ts.tc_breaks, i)
|
||||||
|
has_changed = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #ts.tc_breaks == 0 then
|
||||||
|
-- remove the section completely
|
||||||
|
atdebug("purge_ts_tcb_refs(",ts_id,"): after purging, the section is empty, is being deleted")
|
||||||
|
track_sections[ts_id] = nil
|
||||||
|
return nil
|
||||||
|
else
|
||||||
|
if has_changed then
|
||||||
|
-- needs to update route cache
|
||||||
|
ildb.update_ts_cache(ts_id)
|
||||||
|
end
|
||||||
|
return ts
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- For the specified TCBS, make sure that the track section referenced by it
|
||||||
|
-- (a) exists and
|
||||||
|
-- (b) has a backreference to this TCBS stored in its tc_breaks list
|
||||||
|
function ildb.tcbs_ensure_ts_ref_exists(sigd)
|
||||||
|
local tcbs = sigd.tcbs or ildb.get_tcbs(sigd)
|
||||||
|
if not tcbs or not tcbs.ts_id then return end
|
||||||
|
local ts = ildb.get_ts(tcbs.ts_id)
|
||||||
|
if not ts then
|
||||||
|
atdebug("tcbs_ensure_ts_ref_exists(",sigd,"): TS does not exist, setting to nil")
|
||||||
|
-- TS is deleted, clear own ts id
|
||||||
|
tcbs.ts_id = nil
|
||||||
|
return
|
||||||
|
end
|
||||||
|
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)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ildb.create_ts_from_tcb_list(sigd_list)
|
||||||
|
local id = advtrains.random_id(8)
|
||||||
|
|
||||||
|
while track_sections[id] do
|
||||||
|
id = advtrains.random_id(8)
|
||||||
|
end
|
||||||
|
atdebug("create_ts_from_tcb_list: sigd_list=",sigd_list, "new ID will be ",id)
|
||||||
|
|
||||||
|
local tcbr = {}
|
||||||
|
-- makes a copy of the sigd list, for use in repair mechanisms where sigd may contain a tcbs field which we dont want
|
||||||
|
for _, sigd in ipairs(sigd_list) do
|
||||||
|
table.insert(tcbr, {p=sigd.p, s=sigd.s})
|
||||||
|
local tcbs = sigd.tcbs or ildb.get_tcbs(sigd)
|
||||||
|
if tcbs.ts_id then
|
||||||
|
error("Trying to create TS with TCBS that is already assigned to other section")
|
||||||
|
end
|
||||||
|
tcbs.ts_id = id
|
||||||
|
end
|
||||||
|
|
||||||
|
local new_ts = {
|
||||||
|
tc_breaks = tcbr
|
||||||
|
}
|
||||||
|
track_sections[id] = new_ts
|
||||||
|
-- update the TCB markers
|
||||||
|
for _, sigd in ipairs(sigd_list) do
|
||||||
|
advtrains.interlocking.show_tcb_marker(sigd.p)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
ildb.update_ts_cache(id)
|
||||||
|
return id, new_ts
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Updates the turnout cache of the given track section
|
||||||
-- Merges the TS with merge_id into root_id and then deletes merge_id
|
function ildb.update_ts_cache(ts_id)
|
||||||
local function merge_ts(root_id, merge_id)
|
local ts = ildb.get_ts(ts_id)
|
||||||
local rts = ildb.get_ts(root_id)
|
if not ts then
|
||||||
local mts = ildb.get_ts(merge_id)
|
error("Update TS Cache called with nonexisting ts_id "..(ts_id or "nil"))
|
||||||
if not mts then return end -- This may be the case when sync_tcb_neighbors
|
|
||||||
-- inserts the same id twice. do nothing.
|
|
||||||
|
|
||||||
if not ildb.may_modify_ts(rts) then return false end
|
|
||||||
if not ildb.may_modify_ts(mts) then return false end
|
|
||||||
|
|
||||||
-- cobble together the list of TCBs
|
|
||||||
for _, msigd in ipairs(mts.tc_breaks) do
|
|
||||||
local tcbs = ildb.get_tcbs(msigd)
|
|
||||||
if tcbs then
|
|
||||||
insert_sigd_nodouble(rts.tc_breaks, msigd)
|
|
||||||
tcbs.ts_id = root_id
|
|
||||||
end
|
|
||||||
advtrains.interlocking.show_tcb_marker(msigd.p)
|
|
||||||
end
|
end
|
||||||
-- done
|
local rscache = {}
|
||||||
track_sections[merge_id] = nil
|
-- start on every of the TS's TCBs, walk the track forward and store locks along the way
|
||||||
|
-- TODO: Need change in handling of switches
|
||||||
|
atdebug("update_ts_cache",ts_id,"TODO: implement")
|
||||||
end
|
end
|
||||||
|
|
||||||
local lntrans = { "A", "B" }
|
local lntrans = { "A", "B" }
|
||||||
|
@ -312,127 +519,65 @@ local function sigd_to_string(sigd)
|
||||||
return minetest.pos_to_string(sigd.p).." / "..lntrans[sigd.s]
|
return minetest.pos_to_string(sigd.p).." / "..lntrans[sigd.s]
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check for near TCBs and connect to their TS if they have one, and syncs their data.
|
-- Create a new TCB at the position and update/repair the adjoining sections
|
||||||
function ildb.sync_tcb_neighbors(pos, connid)
|
function ildb.create_tcb_at(pos)
|
||||||
local found_tcbs = { {p = pos, s = connid} }
|
atdebug("create_tcb_at",pos)
|
||||||
local node_ok, conns, rhe = advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
|
local pts = advtrains.encode_pos(pos)
|
||||||
if not node_ok then
|
track_circuit_breaks[pts] = {[1] = {}, [2] = {}}
|
||||||
atwarn("update_tcb_neighbors but node is NOK: "..minetest.pos_to_string(pos))
|
local all_tcbs_1 = ildb.get_all_tcbs_adjacent(pos, 1)
|
||||||
return
|
atdebug("TCBs on A side",all_tcbs_1)
|
||||||
end
|
local all_tcbs_2 = ildb.get_all_tcbs_adjacent(pos, 2)
|
||||||
|
atdebug("TCBs on B side",all_tcbs_2)
|
||||||
--atdebug("Traversing from ",pos, connid)
|
-- perform TS repair
|
||||||
local counter_hit = traverser(found_tcbs, pos, conns, connid, 0)
|
ildb.repair_ts_merge_all(all_tcbs_1)
|
||||||
|
ildb.repair_ts_merge_all(all_tcbs_2)
|
||||||
local ts_id
|
|
||||||
local list_eoi = {}
|
|
||||||
local list_ok = {}
|
|
||||||
local list_mismatch = {}
|
|
||||||
local ts_to_merge = {}
|
|
||||||
|
|
||||||
for idx, sigd in pairs(found_tcbs) do
|
|
||||||
local tcbs = ildb.get_tcbs(sigd)
|
|
||||||
if not tcbs.ts_id then
|
|
||||||
--atdebug("Sync: put",sigd_to_string(sigd),"into list_eoi")
|
|
||||||
table.insert(list_eoi, sigd)
|
|
||||||
elseif not ts_id and tcbs.ts_id then
|
|
||||||
if not ildb.get_ts(tcbs.ts_id) then
|
|
||||||
atwarn("Track section database is inconsistent, there's no TS with ID=",tcbs.ts_id)
|
|
||||||
tcbs.ts_id = nil
|
|
||||||
table.insert(list_eoi, sigd)
|
|
||||||
else
|
|
||||||
--atdebug("Sync: put",sigd_to_string(sigd),"into list_ok")
|
|
||||||
ts_id = tcbs.ts_id
|
|
||||||
table.insert(list_ok, sigd)
|
|
||||||
end
|
|
||||||
elseif ts_id and tcbs.ts_id and tcbs.ts_id ~= ts_id then
|
|
||||||
atwarn("Track section database is inconsistent, sections share track!")
|
|
||||||
atwarn("Merging",tcbs.ts_id,"into",ts_id,".")
|
|
||||||
table.insert(list_mismatch, sigd)
|
|
||||||
table.insert(ts_to_merge, tcbs.ts_id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if ts_id then
|
|
||||||
local ts = ildb.get_ts(ts_id)
|
|
||||||
for _, sigd in ipairs(list_eoi) do
|
|
||||||
local tcbs = ildb.get_tcbs(sigd)
|
|
||||||
tcbs.ts_id = ts_id
|
|
||||||
table.insert(ts.tc_breaks, sigd)
|
|
||||||
advtrains.interlocking.show_tcb_marker(sigd.p)
|
|
||||||
end
|
|
||||||
for _, mts in ipairs(ts_to_merge) do
|
|
||||||
merge_ts(ts_id, mts)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function ildb.link_track_sections(merge_id, root_id)
|
-- Create a new TCB at the position and update/repair the now joined section
|
||||||
if merge_id == root_id then
|
function ildb.remove_tcb_at(pos)
|
||||||
return
|
atdebug("remove_tcb_at",pos)
|
||||||
end
|
local pts = advtrains.encode_pos(pos)
|
||||||
merge_ts(root_id, merge_id)
|
local old_tcb = track_circuit_breaks[pts]
|
||||||
end
|
|
||||||
|
|
||||||
function ildb.remove_from_interlocking(sigd)
|
|
||||||
local tcbs = ildb.get_tcbs(sigd)
|
|
||||||
if not ildb.may_modify_tcbs(tcbs) then return false end
|
|
||||||
|
|
||||||
if tcbs.ts_id then
|
|
||||||
local tsid = tcbs.ts_id
|
|
||||||
local ts = ildb.get_ts(tsid)
|
|
||||||
if not ts then
|
|
||||||
tcbs.ts_id = nil
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- remove entry from the list
|
|
||||||
local idx = 1
|
|
||||||
while idx <= #ts.tc_breaks do
|
|
||||||
local cmp = ts.tc_breaks[idx]
|
|
||||||
if sigd_equal(sigd, cmp) then
|
|
||||||
table.remove(ts.tc_breaks, idx)
|
|
||||||
else
|
|
||||||
idx = idx + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
tcbs.ts_id = nil
|
|
||||||
|
|
||||||
--ildb.sync_tcb_neighbors(sigd.p, sigd.s)
|
|
||||||
|
|
||||||
if #ts.tc_breaks == 0 then
|
|
||||||
track_sections[tsid] = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
advtrains.interlocking.show_tcb_marker(sigd.p)
|
|
||||||
if tcbs.signal then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function ildb.remove_tcb(pos)
|
|
||||||
local pts = advtrains.roundfloorpts(pos)
|
|
||||||
if not track_circuit_breaks[pts] then
|
|
||||||
return true --FIX: not an error, because tcb is already removed
|
|
||||||
end
|
|
||||||
for connid=1,2 do
|
|
||||||
if not ildb.remove_from_interlocking({p=pos, s=connid}) then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
track_circuit_breaks[pts] = nil
|
track_circuit_breaks[pts] = nil
|
||||||
|
-- purge the track sections adjacent
|
||||||
|
if old_tcb[1].ts_id then
|
||||||
|
ildb.purge_ts_tcb_refs(old_tcb[1].ts_id)
|
||||||
|
end
|
||||||
|
if old_tcb[2].ts_id then
|
||||||
|
ildb.purge_ts_tcb_refs(old_tcb[2].ts_id)
|
||||||
|
end
|
||||||
|
advtrains.interlocking.remove_tcb_marker(pos)
|
||||||
|
-- If needed, merge the track sections here
|
||||||
|
ildb.check_and_repair_ts_at_pos(pos)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function ildb.dissolve_ts(ts_id)
|
function ildb.create_ts_from_tcbs(sigd)
|
||||||
local ts = ildb.get_ts(ts_id)
|
atdebug("create_ts_from_tcbs",sigd)
|
||||||
if not ildb.may_modify_ts(ts) then return false end
|
local all_tcbs = ildb.get_all_tcbs_adjacent(sigd.p, sigd.s)
|
||||||
local tcbr = advtrains.merge_tables(ts.tc_breaks)
|
ildb.repair_ts_merge_all(all_tcbs, true)
|
||||||
for _,sigd in ipairs(tcbr) do
|
end
|
||||||
ildb.remove_from_interlocking(sigd)
|
|
||||||
|
-- Remove the given track section, leaving its TCBs with no section assigned
|
||||||
|
function ildb.remove_ts(ts_id)
|
||||||
|
atdebug("remove_ts",ts_id)
|
||||||
|
local ts = track_sections[ts_id]
|
||||||
|
if not ts then
|
||||||
|
error("remove_ts: "..ts_id.." doesn't exist")
|
||||||
end
|
end
|
||||||
-- Note: ts gets removed in the moment of the removal of the last TCB.
|
while ts.tc_breaks[i] do
|
||||||
return true
|
-- get TCBS
|
||||||
|
local sigd = ts.tc_breaks[i]
|
||||||
|
local tcbs = ildb.get_tcbs(sigd)
|
||||||
|
if tcbs then
|
||||||
|
atdebug("cleared TCB",sigd)
|
||||||
|
tcbs.ts_id = nil
|
||||||
|
else
|
||||||
|
atdebug("orphan TCB",sigd)
|
||||||
|
end
|
||||||
|
i = i+1
|
||||||
|
end
|
||||||
|
track_sections[ts_id] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Returns true if it is allowed to modify any property of a track section, such as
|
-- Returns true if it is allowed to modify any property of a track section, such as
|
||||||
|
@ -457,38 +602,8 @@ function ildb.may_modify_tcbs(tcbs)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Utilize the traverser to find the track section at the specified position
|
|
||||||
-- Returns:
|
-- Signals/IP --
|
||||||
-- ts_id, origin - the first found ts and the sigd of the found tcb
|
|
||||||
-- nil - there were no TCBs in TRAVERSER_MAX range of the position
|
|
||||||
-- false - the first found TCB stated End-Of-Interlocking, or track ends were reached
|
|
||||||
function ildb.get_ts_at_pos(pos)
|
|
||||||
local node_ok, conns, rhe = advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
|
|
||||||
if not node_ok then
|
|
||||||
error("get_ts_at_pos but node is NOK: "..minetest.pos_to_string(pos))
|
|
||||||
end
|
|
||||||
local limit_hit = false
|
|
||||||
local found_tcbs = {}
|
|
||||||
for connid, conn in ipairs(conns) do -- Note: a breadth-first-search would be better for performance
|
|
||||||
limit_hit = limit_hit or traverser(found_tcbs, pos, conns, connid, 0, 1)
|
|
||||||
if #found_tcbs >= 1 then
|
|
||||||
local tcbs = ildb.get_tcbs(found_tcbs[1])
|
|
||||||
local ts
|
|
||||||
if tcbs.ts_id then
|
|
||||||
return tcbs.ts_id, found_tcbs[1]
|
|
||||||
else
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if limit_hit then
|
|
||||||
-- there was at least one limit hit
|
|
||||||
return nil
|
|
||||||
else
|
|
||||||
-- all traverser ends were track ends
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- returns the sigd the signal at pos belongs to, if this is known
|
-- returns the sigd the signal at pos belongs to, if this is known
|
||||||
|
|
|
@ -88,12 +88,8 @@ minetest.register_node("advtrains_interlocking:tcb_node", {
|
||||||
local tcb = ildb.get_tcb(tcbpos)
|
local tcb = ildb.get_tcb(tcbpos)
|
||||||
if not tcb then return true end
|
if not tcb then return true end
|
||||||
for connid=1,2 do
|
for connid=1,2 do
|
||||||
if tcb[connid].ts_id or tcb[connid].signal then
|
if tcb[connid].signal then
|
||||||
minetest.chat_send_player(pname, "Can't remove TCB: Both sides must have no track section and no signal assigned!")
|
minetest.chat_send_player(pname, "Can't remove TCB: Both sides must have no signal assigned!")
|
||||||
return false
|
|
||||||
end
|
|
||||||
if not ildb.may_modify_tcbs(tcb[connid]) then
|
|
||||||
minetest.chat_send_player(pname, "Can't remove TCB: Side "..connid.." forbids modification (shouldn't happen).")
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -105,15 +101,7 @@ minetest.register_node("advtrains_interlocking:tcb_node", {
|
||||||
local tcbpts = oldmetadata.fields.tcb_pos
|
local tcbpts = oldmetadata.fields.tcb_pos
|
||||||
if tcbpts and tcbpts ~= "" then
|
if tcbpts and tcbpts ~= "" then
|
||||||
local tcbpos = minetest.string_to_pos(tcbpts)
|
local tcbpos = minetest.string_to_pos(tcbpts)
|
||||||
local success = ildb.remove_tcb(tcbpos)
|
ildb.remove_tcb_at(tcbpos)
|
||||||
if success and player then
|
|
||||||
minetest.chat_send_player(player:get_player_name(), "TCB has been removed.")
|
|
||||||
else
|
|
||||||
minetest.chat_send_player(player:get_player_name(), "Failed to remove TCB!")
|
|
||||||
minetest.set_node(pos, oldnode)
|
|
||||||
local meta = minetest.get_meta(pos)
|
|
||||||
meta:set_string("tcb_pos", minetest.pos_to_string(tcbpos))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
@ -167,15 +155,13 @@ minetest.register_on_punchnode(function(pos, node, player, pointed_thing)
|
||||||
if vector.distance(pos, tcbnpos)<=20 then
|
if vector.distance(pos, tcbnpos)<=20 then
|
||||||
local node_ok, conns, rhe = advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
|
local node_ok, conns, rhe = advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
|
||||||
if node_ok and #conns == 2 then
|
if node_ok and #conns == 2 then
|
||||||
local ok = ildb.create_tcb(pos)
|
-- if there is already a tcb here, reassign it
|
||||||
|
if ildb.get_tcb(pos) then
|
||||||
if not ok then
|
minetest.chat_send_player(pname, "Configuring TCB: Already existed at this position, it is now linked to this TCB marker")
|
||||||
minetest.chat_send_player(pname, "Configuring TCB: TCB already exists at this position! It has now been re-assigned.")
|
else
|
||||||
|
ildb.create_tcb_at(pos)
|
||||||
end
|
end
|
||||||
|
|
||||||
ildb.sync_tcb_neighbors(pos, 1)
|
|
||||||
ildb.sync_tcb_neighbors(pos, 2)
|
|
||||||
|
|
||||||
local meta = minetest.get_meta(tcbnpos)
|
local meta = minetest.get_meta(tcbnpos)
|
||||||
meta:set_string("tcb_pos", minetest.pos_to_string(pos))
|
meta:set_string("tcb_pos", minetest.pos_to_string(pos))
|
||||||
meta:set_string("infotext", "TCB assigned to "..minetest.pos_to_string(pos))
|
meta:set_string("infotext", "TCB assigned to "..minetest.pos_to_string(pos))
|
||||||
|
@ -234,22 +220,12 @@ local function mktcbformspec(tcbs, btnpref, offset, pname)
|
||||||
ts = ildb.get_ts(tcbs.ts_id)
|
ts = ildb.get_ts(tcbs.ts_id)
|
||||||
end
|
end
|
||||||
if ts then
|
if ts then
|
||||||
form = form.."label[0.5,"..offset..";Side "..btnpref..": "..minetest.formspec_escape(ts.name).."]"
|
form = form.."label[0.5,"..offset..";Side "..btnpref..": "..minetest.formspec_escape(ts.name or tcbs.ts_id).."]"
|
||||||
form = form.."button[0.5,"..(offset+0.5)..";5,1;"..btnpref.."_gotots;Show track section]"
|
form = form.."button[0.5,"..(offset+0.5)..";5,1;"..btnpref.."_gotots;Show track section]"
|
||||||
if ildb.may_modify_tcbs(tcbs) then
|
|
||||||
-- Note: the security check to prohibit those actions is located in database.lua in the corresponding functions.
|
|
||||||
form = form.."button[0.5,"..(offset+1.5)..";2.5,1;"..btnpref.."_update;Update near TCBs]"
|
|
||||||
form = form.."button[3 ,"..(offset+1.5)..";2.5,1;"..btnpref.."_remove;Remove from section]"
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
tcbs.ts_id = nil
|
tcbs.ts_id = nil
|
||||||
form = form.."label[0.5,"..offset..";Side "..btnpref..": ".."End of interlocking]"
|
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]"
|
form = form.."button[0.5,"..(offset+0.5)..";5,1;"..btnpref.."_makeil;Create Interlocked Track Section]"
|
||||||
--if tcbs.section_free then
|
|
||||||
--form = form.."button[0.5,"..(offset+1.5)..";5,1;"..btnpref.."_setlocked;Section is free]"
|
|
||||||
--else
|
|
||||||
--form = form.."button[0.5,"..(offset+1.5)..";5,1;"..btnpref.."_setfree;Section is blocked]"
|
|
||||||
--end
|
|
||||||
end
|
end
|
||||||
if tcbs.signal then
|
if tcbs.signal then
|
||||||
form = form.."button[0.5,"..(offset+2.5)..";5,1;"..btnpref.."_sigdia;Signalling]"
|
form = form.."button[0.5,"..(offset+2.5)..";5,1;"..btnpref.."_sigdia;Signalling]"
|
||||||
|
@ -297,11 +273,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||||
local tcb = ildb.get_tcb(pos)
|
local tcb = ildb.get_tcb(pos)
|
||||||
if not tcb then return end
|
if not tcb then return end
|
||||||
local f_gotots = {fields.A_gotots, fields.B_gotots}
|
local f_gotots = {fields.A_gotots, fields.B_gotots}
|
||||||
local f_update = {fields.A_update, fields.B_update}
|
|
||||||
local f_remove = {fields.A_remove, fields.B_remove}
|
|
||||||
local f_makeil = {fields.A_makeil, fields.B_makeil}
|
local f_makeil = {fields.A_makeil, fields.B_makeil}
|
||||||
local f_setlocked = {fields.A_setlocked, fields.B_setlocked}
|
|
||||||
local f_setfree = {fields.A_setfree, fields.B_setfree}
|
|
||||||
local f_asnsig = {fields.A_asnsig, fields.B_asnsig}
|
local f_asnsig = {fields.A_asnsig, fields.B_asnsig}
|
||||||
local f_sigdia = {fields.A_sigdia, fields.B_sigdia}
|
local f_sigdia = {fields.A_sigdia, fields.B_sigdia}
|
||||||
|
|
||||||
|
@ -312,29 +284,12 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||||
advtrains.interlocking.show_ts_form(tcbs.ts_id, pname)
|
advtrains.interlocking.show_ts_form(tcbs.ts_id, pname)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if f_update[connid] then
|
|
||||||
ildb.sync_tcb_neighbors(pos, connid)
|
|
||||||
end
|
|
||||||
if f_remove[connid] then
|
|
||||||
ildb.remove_from_interlocking({p=pos, s=connid})
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
if f_makeil[connid] then
|
if f_makeil[connid] then
|
||||||
-- try sinc_tcb_neighbors first
|
|
||||||
ildb.sync_tcb_neighbors(pos, connid)
|
|
||||||
-- if that didn't work, create new section
|
|
||||||
if not tcbs.ts_id then
|
if not tcbs.ts_id then
|
||||||
ildb.create_ts({p=pos, s=connid})
|
ildb.create_ts_from_tcbs({p=pos, s=connid})
|
||||||
ildb.sync_tcb_neighbors(pos, connid)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- non-interlocked
|
|
||||||
if f_setfree[connid] then
|
|
||||||
tcbs.section_free = true
|
|
||||||
end
|
|
||||||
if f_setlocked[connid] then
|
|
||||||
tcbs.section_free = nil
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
if f_asnsig[connid] and not tcbs.signal then
|
if f_asnsig[connid] and not tcbs.signal then
|
||||||
minetest.chat_send_player(pname, "Configuring TCB: Please punch the signal to assign.")
|
minetest.chat_send_player(pname, "Configuring TCB: Please punch the signal to assign.")
|
||||||
|
@ -357,10 +312,7 @@ end)
|
||||||
|
|
||||||
-- TS Formspec
|
-- TS Formspec
|
||||||
|
|
||||||
-- textlist selection temporary storage
|
function advtrains.interlocking.show_ts_form(ts_id, pname)
|
||||||
local ts_pselidx = {}
|
|
||||||
|
|
||||||
function advtrains.interlocking.show_ts_form(ts_id, pname, sel_tcb)
|
|
||||||
if not minetest.check_player_privs(pname, "interlocking") then
|
if not minetest.check_player_privs(pname, "interlocking") then
|
||||||
minetest.chat_send_player(pname, "Insufficient privileges to use this!")
|
minetest.chat_send_player(pname, "Insufficient privileges to use this!")
|
||||||
return
|
return
|
||||||
|
@ -369,7 +321,7 @@ function advtrains.interlocking.show_ts_form(ts_id, pname, sel_tcb)
|
||||||
if not ts_id then return end
|
if not ts_id then return end
|
||||||
|
|
||||||
local form = "size[10,10]label[0.5,0.5;Track Section Detail - "..ts_id.."]"
|
local form = "size[10,10]label[0.5,0.5;Track Section Detail - "..ts_id.."]"
|
||||||
form = form.."field[0.8,2;5.2,1;name;Section name;"..minetest.formspec_escape(ts.name).."]"
|
form = form.."field[0.8,2;5.2,1;name;Section name;"..minetest.formspec_escape(ts.name or "").."]"
|
||||||
form = form.."button[5.5,1.7;1,1;setname;Set]"
|
form = form.."button[5.5,1.7;1,1;setname;Set]"
|
||||||
local hint
|
local hint
|
||||||
|
|
||||||
|
@ -382,26 +334,8 @@ function advtrains.interlocking.show_ts_form(ts_id, pname, sel_tcb)
|
||||||
form = form.."textlist[0.5,3;5,3;tcblist;"..table.concat(strtab, ",").."]"
|
form = form.."textlist[0.5,3;5,3;tcblist;"..table.concat(strtab, ",").."]"
|
||||||
|
|
||||||
if ildb.may_modify_ts(ts) then
|
if ildb.may_modify_ts(ts) then
|
||||||
|
form = form.."button[5.5,4;4,1;remove;Remove Section]"
|
||||||
if players_link_ts[pname] then
|
|
||||||
local other_id = players_link_ts[pname]
|
|
||||||
local other_ts = ildb.get_ts(other_id)
|
|
||||||
if other_ts then
|
|
||||||
if ildb.may_modify_ts(other_ts) then
|
|
||||||
form = form.."button[5.5,3;3.5,1;mklink;Join with "..minetest.formspec_escape(other_ts.name).."]"
|
|
||||||
form = form.."button[9 ,3;0.5,1;cancellink;X]"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
form = form.."button[5.5,3;4,1;link;Join into other section]"
|
|
||||||
hint = 1
|
|
||||||
end
|
|
||||||
form = form.."button[5.5,4;4,1;dissolve;Dissolve Section]"
|
|
||||||
form = form.."tooltip[dissolve;This will remove the track section and set all its end points to End Of Interlocking]"
|
form = form.."tooltip[dissolve;This will remove the track section and set all its end points to End Of Interlocking]"
|
||||||
if sel_tcb then
|
|
||||||
form = form.."button[5.5,5;4,1;del_tcb;Unlink selected TCB]"
|
|
||||||
hint = 2
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
hint=3
|
hint=3
|
||||||
end
|
end
|
||||||
|
@ -421,16 +355,11 @@ function advtrains.interlocking.show_ts_form(ts_id, pname, sel_tcb)
|
||||||
|
|
||||||
form = form.."button[5.5,7;4,1;reset;Reset section state]"
|
form = form.."button[5.5,7;4,1;reset;Reset section state]"
|
||||||
|
|
||||||
if hint == 1 then
|
if hint == 3 then
|
||||||
form = form.."label[0.5,0.75;Use the 'Join' button to designate rail crosses and link not listed far-away TCBs]"
|
|
||||||
elseif hint == 2 then
|
|
||||||
form = form.."label[0.5,0.75;Unlinking a TCB will set it to non-interlocked mode.]"
|
|
||||||
elseif hint == 3 then
|
|
||||||
form = form.."label[0.5,0.75;You cannot modify track sections when a route is set or a train is on the section.]"
|
form = form.."label[0.5,0.75;You cannot modify track sections when a route is set or a train is on the section.]"
|
||||||
--form = form.."label[0.5,1;Trying to unlink a TCB directly connected to this track will not work.]"
|
--form = form.."label[0.5,1;Trying to unlink a TCB directly connected to this track will not work.]"
|
||||||
end
|
end
|
||||||
|
|
||||||
ts_pselidx[pname]=sel_tcb
|
|
||||||
minetest.show_formspec(pname, "at_il_tsconfig_"..ts_id, form)
|
minetest.show_formspec(pname, "at_il_tsconfig_"..ts_id, form)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -442,45 +371,15 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
-- independent of the formspec, clear this whenever some formspec event happens
|
-- independent of the formspec, clear this whenever some formspec event happens
|
||||||
local tpsi = ts_pselidx[pname]
|
|
||||||
ts_pselidx[pname] = nil
|
|
||||||
|
|
||||||
local ts_id = string.match(formname, "^at_il_tsconfig_(.+)$")
|
local ts_id = string.match(formname, "^at_il_tsconfig_(.+)$")
|
||||||
if ts_id and not fields.quit then
|
if ts_id and not fields.quit then
|
||||||
local ts = ildb.get_ts(ts_id)
|
local ts = ildb.get_ts(ts_id)
|
||||||
if not ts then return end
|
if not ts then return end
|
||||||
|
|
||||||
local sel_tcb
|
|
||||||
if fields.tcblist then
|
|
||||||
local tev = minetest.explode_textlist_event(fields.tcblist)
|
|
||||||
sel_tcb = tev.index
|
|
||||||
ts_pselidx[pname] = sel_tcb
|
|
||||||
elseif tpsi then
|
|
||||||
sel_tcb = tpsi
|
|
||||||
end
|
|
||||||
|
|
||||||
if ildb.may_modify_ts(ts) then
|
if ildb.may_modify_ts(ts) then
|
||||||
if players_link_ts[pname] then
|
if fields.remove then
|
||||||
if fields.cancellink then
|
ildb.remove_ts(ts_id)
|
||||||
players_link_ts[pname] = nil
|
|
||||||
elseif fields.mklink then
|
|
||||||
ildb.link_track_sections(players_link_ts[pname], ts_id)
|
|
||||||
players_link_ts[pname] = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if fields.del_tcb and sel_tcb and sel_tcb > 0 and sel_tcb <= #ts.tc_breaks then
|
|
||||||
if not ildb.remove_from_interlocking(ts.tc_breaks[sel_tcb]) then
|
|
||||||
minetest.chat_send_player(pname, "Please unassign signal first!")
|
|
||||||
end
|
|
||||||
sel_tcb = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
if fields.link then
|
|
||||||
players_link_ts[pname] = ts_id
|
|
||||||
end
|
|
||||||
if fields.dissolve then
|
|
||||||
ildb.dissolve_ts(ts_id)
|
|
||||||
minetest.close_formspec(pname, formname)
|
minetest.close_formspec(pname, formname)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
@ -489,7 +388,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||||
if fields.setname then
|
if fields.setname then
|
||||||
ts.name = fields.name
|
ts.name = fields.name
|
||||||
if ts.name == "" then
|
if ts.name == "" then
|
||||||
ts.name = "Section "..ts_id
|
ts.name = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -566,7 +465,7 @@ function advtrains.interlocking.show_tcb_marker(pos)
|
||||||
ts = ildb.get_ts(tcbs.ts_id)
|
ts = ildb.get_ts(tcbs.ts_id)
|
||||||
end
|
end
|
||||||
if ts then
|
if ts then
|
||||||
itex[connid] = ts.name
|
itex[connid] = ts.name or tcbs.ts_id or "???"
|
||||||
else
|
else
|
||||||
itex[connid] = "--EOI--"
|
itex[connid] = "--EOI--"
|
||||||
end
|
end
|
||||||
|
@ -589,6 +488,47 @@ function advtrains.interlocking.show_tcb_marker(pos)
|
||||||
markerent[pts] = obj
|
markerent[pts] = obj
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function advtrains.interlocking.remove_tcb_marker(pos)
|
||||||
|
local pts = advtrains.roundfloorpts(pos)
|
||||||
|
if markerent[pts] then
|
||||||
|
markerent[pts]:remove()
|
||||||
|
end
|
||||||
|
markerent[pts] = nil
|
||||||
|
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
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Signalling formspec - set routes a.s.o
|
-- Signalling formspec - set routes a.s.o
|
||||||
|
|
||||||
-- textlist selection temporary storage
|
-- textlist selection temporary storage
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 7.0 KiB |
|
@ -3,14 +3,64 @@
|
||||||
|
|
||||||
local ilrs = advtrains.interlocking.route
|
local ilrs = advtrains.interlocking.route
|
||||||
|
|
||||||
|
local function node_right_click(pos, pname)
|
||||||
|
if advtrains.is_passive(pos) then
|
||||||
|
local form = "size[7,5]label[0.5,0.5;Route lock inspector]"
|
||||||
|
local pts = minetest.pos_to_string(pos)
|
||||||
|
|
||||||
|
local rtl = ilrs.has_route_lock(pts)
|
||||||
|
|
||||||
|
if rtl then
|
||||||
|
form = form.."label[0.5,1;Route locks currently put:\n"..rtl.."]"
|
||||||
|
form = form.."button_exit[0.5,3.5; 5,1;clear;Clear]"
|
||||||
|
else
|
||||||
|
form = form.."label[0.5,1;No route locks set]"
|
||||||
|
form = form.."button_exit[0.5,3.5; 5,1;emplace;Emplace manual lock]"
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.show_formspec(pname, "at_il_rtool_"..pts, form)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If not a turnout, check the track section and show a form
|
||||||
|
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
|
||||||
|
end
|
||||||
|
|
||||||
|
local ts_id = advtrains.interlocking.db.check_and_repair_ts_at_pos(pos)
|
||||||
|
if ts_id then
|
||||||
|
advtrains.interlocking.show_ts_form(ts_id, pname)
|
||||||
|
else
|
||||||
|
minetest.chat_send_player(pname, "No track section at this location!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
end
|
||||||
|
|
||||||
|
local ts_id = advtrains.interlocking.db.check_and_repair_ts_at_pos(pos)
|
||||||
|
if ts_id then
|
||||||
|
advtrains.interlocking.highlight_track_section(pos)
|
||||||
|
else
|
||||||
|
minetest.chat_send_player(pname, "No track section at this location!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
minetest.register_craftitem("advtrains_interlocking:tool",{
|
minetest.register_craftitem("advtrains_interlocking:tool",{
|
||||||
description = "Interlocking tool\nright-click turnouts to inspect route locks",
|
description = "Interlocking tool\nPunch: Highlight track section\nPlace: check route locks/show track section info",
|
||||||
groups = {cracky=1}, -- key=name, value=rating; rating=1..3.
|
groups = {cracky=1}, -- key=name, value=rating; rating=1..3.
|
||||||
inventory_image = "at_il_tool.png",
|
inventory_image = "at_il_tool.png",
|
||||||
wield_image = "at_il_tool.png",
|
wield_image = "at_il_tool.png",
|
||||||
stack_max = 1,
|
stack_max = 1,
|
||||||
on_place = function(itemstack, placer, pointed_thing)
|
on_place = function(itemstack, player, pointed_thing)
|
||||||
local pname = placer:get_player_name()
|
local pname = player:get_player_name()
|
||||||
if not pname then
|
if not pname then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
@ -20,27 +70,23 @@ minetest.register_craftitem("advtrains_interlocking:tool",{
|
||||||
end
|
end
|
||||||
if pointed_thing.type=="node" then
|
if pointed_thing.type=="node" then
|
||||||
local pos=pointed_thing.under
|
local pos=pointed_thing.under
|
||||||
if advtrains.is_passive(pos) then
|
node_right_click(pos, pname)
|
||||||
local form = "size[7,5]label[0.5,0.5;Route lock inspector]"
|
|
||||||
local pts = minetest.pos_to_string(pos)
|
|
||||||
|
|
||||||
local rtl = ilrs.has_route_lock(pts)
|
|
||||||
|
|
||||||
if rtl then
|
|
||||||
form = form.."label[0.5,1;Route locks currently put:\n"..rtl.."]"
|
|
||||||
form = form.."button_exit[0.5,3.5; 5,1;clear;Clear]"
|
|
||||||
else
|
|
||||||
form = form.."label[0.5,1;No route locks set]"
|
|
||||||
form = form.."button_exit[0.5,3.5; 5,1;emplace;Emplace manual lock]"
|
|
||||||
end
|
|
||||||
|
|
||||||
minetest.show_formspec(pname, "at_il_rtool_"..pts, form)
|
|
||||||
else
|
|
||||||
minetest.chat_send_player(pname, "Cannot use this here.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
on_use = function(itemstack, player, pointed_thing)
|
||||||
|
local pname = player:get_player_name()
|
||||||
|
if not pname then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if not minetest.check_player_privs(pname, {interlocking=true}) then
|
||||||
|
minetest.chat_send_player(pname, "Insufficient privileges to use this!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if pointed_thing.type=="node" then
|
||||||
|
local pos=pointed_thing.under
|
||||||
|
node_left_click(pos, pname)
|
||||||
|
end
|
||||||
|
end
|
||||||
})
|
})
|
||||||
|
|
||||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||||
|
|
Loading…
Reference in New Issue