advtrains/advtrains_interlocking/distant.lua

201 lines
5.2 KiB
Lua

--- Distant signaling.
-- This module implements a database backend for distant signal assignments.
-- The actual modifications to signal aspects are still done by signal aspect accessors.
-- @module advtrains.interlocking.distant
local db_distant = {}
local db_distant_of = {}
local pts = advtrains.encode_pos
local stp = advtrains.decode_pos
--- Replace the distant signal assignment database.
-- @function load
-- @param db The new database to load.
local function db_load(x)
if type(x) ~= "table" then
return
end
db_distant = x.distant
db_distant_of = x.distant_of
end
--- Retrieve the current distant signal assignment database.
-- @function save
-- @return The current database.
local function db_save()
return {
distant = db_distant,
distant_of = db_distant_of,
}
end
local update_signal, update_main, update_dst
--- Unassign a distant signal.
-- @function unassign_dst
-- @param dst The position of the distant signal.
-- @param[opt=false] force Whether to skip callbacks.
local function unassign_dst(dst, force)
local pts_dst = pts(dst)
local main = db_distant_of[pts_dst]
db_distant_of[pts_dst] = nil
if main then
local pts_main = main[1]
local t = db_distant[pts_main]
if t then
t[pts_dst] = nil
end
end
if not force then
update_dst(dst)
end
end
--- Unassign a main signal.
-- @function unassign_main
-- @param main The position of the main signal.
-- @param[opt=false] force Whether to skip callbacks.
local function unassign_main(main, force)
local pts_main = pts(main)
local t = db_distant[pts_main]
if not t then
return
end
for pts_dst in pairs(t) do
local realmain = db_distant_of[pts_dst]
if realmain and realmain[1] == pts_main then
db_distant_of[pts_dst] = nil
if not force then
local dst = stp(pts_dst)
update_dst(dst)
end
end
end
db_distant[pts_main] = nil
end
--- Remove all (main and distant) signal assignments from a signal.
-- @function unassign_all
-- @param pos The position of the signal.
-- @param[opt=false] force Whether to skip callbacks.
local function unassign_all(pos, force)
unassign_main(pos)
unassign_dst(pos, force)
end
--- Check whether a signal is "appropriate" for the distant signal system.
-- Currently, a signal is considered appropriate if its signal aspect can be set.
-- @function appropriate_signal
-- @param pos The position of the signal
local function appropriate_signal(pos)
local node = advtrains.ndb.get_node(pos)
local ndef = minetest.registered_nodes[node.name] or {}
if not ndef then
return false
end
local atdef = ndef.advtrains
if not atdef then
return false
end
return atdef.supported_aspects and atdef.set_aspect and true
end
--- Assign a distant signal to a main signal.
-- @function assign
-- @param main The position of the main signal.
-- @param dst The position of the distant signal.
-- @param[opt="manual"] by The method of assignment.
-- @param[opt=false] skip_update Whether to skip callbacks.
local function assign(main, dst, by, skip_update)
if not (appropriate_signal(main) and appropriate_signal(dst)) then
return
end
local pts_main = pts(main)
local pts_dst = pts(dst)
local t = db_distant[pts_main]
if not t then
t = {}
db_distant[pts_main] = t
end
if not by then
by = "manual"
end
unassign_dst(dst, true)
t[pts_dst] = by
db_distant_of[pts_dst] = {pts_main, by}
if not skip_update then
update_dst(dst)
end
end
--- Get the distant signals assigned to a main signal.
-- @function get_distant
-- @param main The position of the main signal.
-- @treturn {[pos]=by,...} A table of distant signals, with the positions encoded using `advtrains.encode_pos`.
local function get_distant(main)
local pts_main = pts(main)
return db_distant[pts_main] or {}
end
--- Get the main signal assigned the a distant signal.
-- @function get_main
-- @param dst The position of the distant signal.
-- @return The position of the main signal.
-- @return The method of assignment.
local function get_main(dst)
local pts_dst = pts(dst)
local main = db_distant_of[pts_dst]
if not main then
return
end
if main[1] then
return stp(main[1]), unpack(main, 2)
else
return unpack(main)
end
end
--- Update all distant signals assigned to a main signal.
-- @function update_main
-- @param main The position of the main signal.
update_main = function(main)
local pts_main = pts(main)
local t = get_distant(main)
for pts_dst in pairs(t) do
local dst = stp(pts_dst)
advtrains.interlocking.signal_readjust_aspect(dst)
end
end
--- Update the aspect of a distant signal.
-- @function update_dst
-- @param dst The position of the distant signal.
update_dst = function(dst)
advtrains.interlocking.signal_readjust_aspect(dst)
end
--- Update the aspect of a combined (main and distant) signal and all distant signals assigned to it.
-- @function update_signal
-- @param pos The position of the signal.
update_signal = function(pos)
update_main(pos)
update_dst(pos)
end
advtrains.distant = {
load = db_load,
save = db_save,
assign = assign,
unassign_dst = unassign_dst,
unassign_main = unassign_main,
unassign_all = unassign_all,
get_distant = get_distant,
get_dst = get_distant,
get_main = get_main,
update_main = update_main,
update_dst = update_dst,
update_signal = update_signal,
appropriate_signal = appropriate_signal,
}