From f284d395d700ffb0fad29c18a9cfe466ed92961b Mon Sep 17 00:00:00 2001 From: orwell96 Date: Thu, 13 Jan 2022 21:03:55 +0100 Subject: 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 --- advtrains/couple.lua | 4 +- advtrains/debugitems.lua | 37 +++++++++++++++ advtrains/helpers.lua | 116 +++++++++++++++++++++++++++++++++++++++++++++ advtrains/init.lua | 3 ++ advtrains/settingtypes.txt | 4 ++ 5 files changed, 162 insertions(+), 2 deletions(-) (limited to 'advtrains') 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 + + if 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 + + if 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 -- cgit v1.2.3