aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authororwell96 <orwell@bleipb.de>2022-01-13 21:03:55 +0100
committerorwell96 <orwell@bleipb.de>2023-05-27 12:26:10 +0200
commitf284d395d700ffb0fad29c18a9cfe466ed92961b (patch)
treec64f5310a9adf28ce8e6503c57ad4230386a9281
parent283efc44ce78001ebd17f64555eb795e36b27a61 (diff)
downloadadvtrains-f284d395d700ffb0fad29c18a9cfe466ed92961b.tar.gz
advtrains-f284d395d700ffb0fad29c18a9cfe466ed92961b.tar.bz2
advtrains-f284d395d700ffb0fad29c18a9cfe466ed92961b.zip
Add TrackIterator interface as a common framework for walking along tracks (also for third-party libs)
This will replace the interlocking traverser and will be used in the new itrainmap implementation
-rw-r--r--advtrains/couple.lua4
-rw-r--r--advtrains/debugitems.lua37
-rw-r--r--advtrains/helpers.lua116
-rw-r--r--advtrains/init.lua3
-rw-r--r--advtrains/settingtypes.txt4
-rw-r--r--advtrains_interlocking/database.lua68
6 files changed, 170 insertions, 62 deletions
diff --git a/advtrains/couple.lua b/advtrains/couple.lua
index 3e6c432..49c8a5d 100644
--- a/advtrains/couple.lua
+++ b/advtrains/couple.lua
@@ -475,7 +475,7 @@ minetest.register_entity("advtrains:couple", {
self.object:remove()
end,
on_step=function(self, dtime)
- if advtrains.wagon_outside_range(self.object:getpos()) then
+ if advtrains.wagon_outside_range(self.object:get_pos()) then
--atdebug("Couple Removing outside range")
self.object:remove()
return
@@ -514,7 +514,7 @@ minetest.register_entity("advtrains:couple", {
tp2=advtrains.path_get_interpolated(train2, train2.end_index)
end
local pos_median=advtrains.pos_median(tp1, tp2)
- if not vector.equals(pos_median, self.object:getpos()) then
+ if not vector.equals(pos_median, self.object:get_pos()) then
self.object:set_pos(pos_median)
end
self.position_set=true
diff --git a/advtrains/debugitems.lua b/advtrains/debugitems.lua
index e598216..a59efc7 100644
--- a/advtrains/debugitems.lua
+++ b/advtrains/debugitems.lua
@@ -81,3 +81,40 @@ minetest.register_tool("advtrains:wagonpos_tester",
end,
}
)
+
+
+local function trackitest(initial_pos, initial_connid)
+ local ti, pos, connid, ok
+ ti = advtrains.get_track_iterator(initial_pos, initial_connid, 500, true)
+ atdebug("Starting at pos:",initial_pos,initial_connid)
+ while ti:has_next_branch() do
+ pos, connid = ti:next_branch() -- in first iteration, this will be the node at initial_pos. In subsequent iterations this will be the switch node from which we are branching off
+ atdebug("Next Branch:",pos, connid)
+ ok = true
+ while ok do
+ ok, pos, connid = ti:next_track()
+ atdebug("Next Track:", ok, pos, connid)
+ end
+ end
+ atdebug("End of traverse. Visited: ",table.concat(ti.visited, ","))
+end
+
+minetest.register_tool("advtrains:trackitest",
+{
+ description = "Track Iterator Tester (leftclick conn 1, rightclick conn 2)",
+ groups = {cracky=1}, -- key=name, value=rating; rating=1..3.
+ inventory_image = "advtrains_track_swlcr_45.png",
+ wield_image = "advtrains_track_swlcr_45.png",
+ stack_max = 1,
+ range = 7.0,
+
+ on_place = function(itemstack, placer, pointed_thing)
+ if pointed_thing.type ~= "node" then return end
+ trackitest(pointed_thing.under, 2)
+ end,
+ on_use = function(itemstack, user, pointed_thing)
+ if pointed_thing.type ~= "node" then return end
+ trackitest(pointed_thing.under, 1)
+ end,
+}
+)
diff --git a/advtrains/helpers.lua b/advtrains/helpers.lua
index cf890ca..bef4903 100644
--- a/advtrains/helpers.lua
+++ b/advtrains/helpers.lua
@@ -470,3 +470,119 @@ else
end
end
end
+
+
+-- TrackIterator interface --
+
+-- Metatable:
+local trackiter_mt = {
+ -- get whether there are still unprocessed branches
+ has_next_branch = function(self)
+ return #self.branches > 0
+ end,
+ -- go to the next unprocessed branch
+ -- returns track_pos, track_connid of the switch/crossing node where the track branches off
+ next_branch = function(self)
+ local br = table.remove(self.branches, 1)
+ -- Advance internal state
+ local adj_pos, adj_connid, _, _, adj_conns = advtrains.get_adjacent_rail(br.pos, nil, br.connid)
+ self.pos = adj_pos
+ self.bconnid = adj_connid
+ self.tconns = adj_conns
+ self.limit = br.limit - 1
+ self.visited[advtrains.encode_pos(br.pos)] = true
+ return br.pos, br.connid
+ end,
+ -- get the next track along the current branch,
+ -- potentially adding branching tracks to the unprocessed branches list
+ -- returns status, track_pos, track_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)
+ next_track = function(self)
+ local pos = self.pos
+ if not pos then
+ -- last run found track end. Return false
+ return false, "track_end"
+ end
+ -- if limit hit, return nil to signal this
+ if self.limit <= 0 then
+ return nil, "limit_hit"
+ 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)
+ local mconnid = advtrains.get_matching_conn(self.bconnid, #self.tconns)
+ -- If there are more connections, add these to branches
+ for nconnid,_ in ipairs(self.tconns) do
+ if nconnid~=mconnid and nconnid~=self.bconnid then
+ table.insert(self.branches, {pos = self.pos, connid = nconnid, limit=self.limit})
+ end
+ end
+ -- Advance internal state
+ local adj_pos, adj_connid, _, _, adj_conns = advtrains.get_adjacent_rail(pos, self.tconns, mconnid)
+ self.pos = adj_pos
+ self.bconnid = adj_connid
+ self.tconns = adj_conns
+ self.limit = self.limit - 1
+ self.visited[advtrains.encode_pos(pos)] = true
+ return pos, mconnid
+ end,
+}
+
+-- Returns a new TrackIterator object
+
+-- Parameters:
+-- initial_pos: the initial track position of the track iterator
+-- initial_connid: the connection index in which to traverse. If nil, adds a "branch" for every connection of the track (traverse in all directions)
+-- limit: maximum distance from the start point after which the traverser stops
+-- follow_all: if true, follows all branches at multi-connection tracks, even the ones pointing backwards or the crossing track on crossings. If false, follows only switches in driving direction.
+
+-- Functions of the returned TrackIterator can be called via the Lua : notation, such as ti:next_track()
+-- If only the main track needs to be followed, use only the ti:next_track() function and do not call ti:next_branch().
+function advtrains.get_track_iterator(initial_pos, initial_connid, limit, follow_all)
+ local ti = {
+ visited = {}
+ }
+ if initial_connid then
+ ti.branches = { {pos = initial_pos, connid = initial_connid, limit=limit} }
+ else
+ -- get track info here
+ local node_ok, conns, rail_y=advtrains.get_rail_info_at(initial_pos)
+ assert(node_ok, "get_track_iterator called with non-track node!")
+ ti.branches = {}
+ for coni, _ in pairs(conns) do
+ table.insert(ti.branches, {pos = initial_pos, connid = coni, limit=limit})
+ end
+ end
+ setmetatable(ti, {__index=trackiter_mt})
+ return ti
+end
+
+--[[
+Example TrackIterator usage structure:
+
+local ti, pos, connid, ok
+ti = advtrains.get_track_iterator(initial_pos, initial_connid, 500, true)
+while ti:has_next_branch() do
+ pos, connid = ti:next_branch() -- in first iteration, this will be the node at initial_pos. In subsequent iterations this will be the switch node from which we are branching off
+ repeat
+ <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
+ ok, pos, connid = ti:next_track()
+ until not ok -- 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
+end
+
+Example for walking only a single track (without branching):
+
+local ti, pos, connid, ok
+ti = advtrains.get_track_iterator(initial_pos, initial_connid, 500, true)
+
+pos, connid = ti:next_branch() -- this always needs to be done at least one time, and gets the track at initial_pos
+repeat
+ <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
+ ok, pos, connid = ti:next_track()
+until not ok -- this stops the loop when either the track end is reached or the limit is hit
+]]
diff --git a/advtrains/init.lua b/advtrains/init.lua
index a7e5764..2213937 100644
--- a/advtrains/init.lua
+++ b/advtrains/init.lua
@@ -229,6 +229,9 @@ end
dofile(advtrains.modpath.."/lzb.lua")
+if minetest.settings:get_bool("advtrains_register_debugitems") then
+ dofile(advtrains.modpath.."/debugitems.lua")
+end
--load/save
diff --git a/advtrains/settingtypes.txt b/advtrains/settingtypes.txt
index 2b627cb..79a1e4c 100644
--- a/advtrains/settingtypes.txt
+++ b/advtrains/settingtypes.txt
@@ -7,6 +7,10 @@ advtrains_show_ids (Show ID's in infotext) bool false
# You probably want to leave this setting set to false.
advtrains_enable_debugging (Enable debugging) bool false
+# Register certain debug items, for example the tunnelborer
+# Do not use on productive servers!
+advtrains_register_debugitems (Register Debug Items) bool false
+
# Enable the logging of certain events related to advtrains
# Logs are saved in the world directory as advtrains.log
# This setting is useful for multiplayer servers
diff --git a/advtrains_interlocking/database.lua b/advtrains_interlocking/database.lua
index 6787c50..5f163e4 100644
--- a/advtrains_interlocking/database.lua
+++ b/advtrains_interlocking/database.lua
@@ -2,71 +2,19 @@
-- saving the location of TCB's, their neighbors and their state
--[[
-== THIS COMMENT IS PARTIALLY INCORRECT AND OUTDATED! ==
-
-The interlocking system is based on track circuits.
-Track circuit breaks must be manually set by the user. Signals must be assigned to track circuit breaks and to a direction(connid).
-To simplify the whole system, there is no overlap.
-== Trains ==
-Trains always occupy certain track circuits. These are shown red in the signalbox view (TRAIN occupation entry).
-== Database storage ==
-The things that are actually saved are the Track Circuit Breaks. Each TCB holds a list of the TCBs that are adjacent in each direction.
-TC occupation/state is then saved inside each (TCB,Direction) and held in sync across all TCBs adjacent to this one. If something should not be in sync,
-all entries are merged to perform the most restrictive setup.
-== Traverser function ==
-To determine and update the list of neighboring TCBs, we need a traverser function.
-It will start at one TCB in a specified direction (connid) and use get_adjacent_rail to crawl along the track. When encountering a turnout or a crossing,
-it needs to branch(call itself recursively) to find all required TCBs. Those found TCBs are then saved in a list as tuples (TCB,Dir)
-In the last step, they exchange their neighbors.
-== TC states ==
-A track circuit does not have a state as such, but has more or less a list of "reservations"
-type can be one of these:
-TRAIN See Trains obove
-ROUTE Route set from a signal, but no train has yet passed that signal.
-Not implemented (see note by reversible): OWNED - former ROUTE segments that a train has begun passing (train_id assigned)
- - Space behind a train up to the next signal, when a TC is set as REVERSIBLE
-Certain TCs can be marked as "allow call-on".
-== Route setting: ==
-Routes are set from a signal (the entry signal) to another signal facing the same direction (the exit signal)
-Remember that signals are assigned to a TCB and a connid.
-Whenever this is done, the following track circuits are set "reserved" by the train by saving the entry signal's ID:
-- all TCs on the direct way of the route - set as ROUTE
-Route setting fails whenever any TC that we want to set ROUTE to is already set ROUTE or TRAIN from another signal (except call-on, see below)
-Apart from this, we need to set turnouts
-- Turnouts on the track are set held as ROUTE
-- Turnouts that purpose as flank protection are set held as FLANK (NOTE: left as an idea for later, because it's not clear how to do this properly without an engineer)
-Note: In SimSig, it is possible to set a route into an still occupied section on the victoria line sim. (at the depot exit at seven sisters), although
- there are still segments set ahead of the first train passing, remaining from another route.
- Because our system will be able to remember "requested routes" and set them automatically once ready, this is not necessary here.
-== Call-On/Multiple Trains ==
-It will be necessary to join and split trains using call-on routes. A call-on route may be set when:
-- there are no ROUTE reservations
-- there are TRAIN reservations only inside TCs that have "allow call-on" set
-== TC Properties ==
-Note: Reversible property will not be implemented, assuming everything as non-rev.
-This is sufficient to cover all use cases, and is done this way in reality.
- REVERSIBLE - Whether trains are allowed to reverse while on track circuit
- This property is supposed to be set for station tracks, where there is a signal at each end, and for sidings.
- It should in no case be set for TCs covering turnouts, or for main running lines.
- When a TC is not set as reversible, the OWNED status is cleared from the TC right after the train left it,
- to allow other trains to pass it.
- If it is set reversible, interlocking will keep the OWNED state behind the train up to the next signal, clearing it
- as soon as the train passes another signal or enters a non-reversible section.
-CALL_ON_ALLOWED - Whether this TC being blocked (TRAIN or ROUTE) does not prevent shunt routes being set through this TC
-== More notes ==
-- It may not be possible to switch turnouts when their TC has any state entry
-
== Route releasing (TORR) ==
A train passing through a route happens as follows:
Route set from entry to exit signal
-Train passes entry signal and enters first TC past the signal
--> Route from signal cleared (TCs remain locked)
--> ROUTE status of first TC past signal cleared
+Train passes entry signal and enters first TS past the signal
+-> Route from signal cleared (TSs remain locked)
+-> 'route' status of first TS past signal cleared
+-> 'route_post' (holding the turnout locks) remains set
Train continues along the route.
-Whenever train leaves a TC
+Whenever train leaves a TS
-> Clearing any routes set from this TC outward recursively - see "Reversing problem"
-Whenever train enters a TC
--> Clear route status from the just entered TC
+-> Free turnout locks and clear 'route_post'
+Whenever train enters a TS
+-> Clear route status from the just entered TC (but not route_post)
Note that this prohibits by design that the train clears the route ahead of it.
== Reversing Problem ==
Encountered at the Royston simulation in SimSig. It is solved there by imposing a time limit on the set route. Call-on routes can somehow be set anyway.