Start changing APIs and applying proof-of-concept to ks signals

This commit is contained in:
orwell 2024-02-06 21:10:40 +01:00
parent aa9033f901
commit 2dab59f055
3 changed files with 156 additions and 89 deletions

View File

@ -12,7 +12,7 @@ end
local modpath = minetest.get_modpath(minetest.get_current_modname()) .. DIR_DELIM local modpath = minetest.get_modpath(minetest.get_current_modname()) .. DIR_DELIM
advtrains.interlocking.aspect = dofile(modpath.."aspect.lua") --advtrains.interlocking.aspect = dofile(modpath.."aspect.lua")
dofile(modpath.."database.lua") dofile(modpath.."database.lua")
dofile(modpath.."distant.lua") dofile(modpath.."distant.lua")

View File

@ -2,18 +2,19 @@
local F = advtrains.formspec local F = advtrains.formspec
local DANGER = { local signal = {}
signal.MASP_HALT = {
name = "halt"
halt = true,
}
signal.ASPI_HALT = {
main = 0, main = 0,
shunt = false, shunt = false,
} }
advtrains.interlocking.DANGER = DANGER
advtrains.interlocking.GENERIC_FREE = { signal.ASPI_FREE = {
main = -1,
shunt = false,
dst = false,
}
advtrains.interlocking.FULL_FREE = {
main = -1, main = -1,
shunt = false, shunt = false,
proceed_as_main = true, proceed_as_main = true,
@ -25,14 +26,12 @@ Most parts of ywang's implementation are fine, especially I like the formspecs.
- Signal gets distant assigned via field in signal aspect table (instead of explicitly) - Signal gets distant assigned via field in signal aspect table (instead of explicitly)
- Signal speed/shunt are no longer free-text but rather they need to be predefined in the node definition - Signal speed/shunt are no longer free-text but rather they need to be predefined in the node definition
To do this: Differentiation between: To do this: Differentiation between:
== Aspect Group == == Main Aspect ==
This is what a signal is assigned by either the route system or the user. This is what a signal is assigned by either the route system or the user.
It is a string key which has an appropriate entry in the node definition (where it has a description assigned) It is a string key which has an appropriate entry in the node definition (where it has a description assigned)
The signal mod defines a function to set a signal to the most appropriate aspect. This function gets The signal mod defines a function to set a signal to the most appropriate aspect. This function gets
a) the aspect group name a) the main aspect table (straight from node def)
b) the distant signal's aspect group name & aspect table b) the distant signal's aspect group name & aspect table
EVERY signal must define the special aspect group "halt". This must always be the most restrictive aspect possible.
The "halt" aspect group should ignore any distant info, in most cases it is called without them anyway.
== Aspect == == Aspect ==
One concrete combination of lights/shapes that a signal signal shows. Handling these is at the discretion of One concrete combination of lights/shapes that a signal signal shows. Handling these is at the discretion of
@ -50,6 +49,11 @@ Note that once apply_aspect returns, there is no need for advtrains anymore to q
When the signal, for any reason, wants to change its aspect by itself *without* going through the signal API then When the signal, for any reason, wants to change its aspect by itself *without* going through the signal API then
it should update the aspect info cache by calling advtrains.interlocking.signal.update_aspect_info(pos) it should update the aspect info cache by calling advtrains.interlocking.signal.update_aspect_info(pos)
Note that the apply_aspect function MUST accept the following main aspect, even if it is not defined in the main_aspects table:
{ name = "halt", halt = true }
It should cause the signal to show its most restrictive aspect. Typically it is a halt aspect, but e.g. for distant-only
signals this would be "expect stop".
== Aspect Info == == Aspect Info ==
The actual signal aspect in the already-known format. This is what the trains use to determine halt/proceed and speed. In this, the dst field has to be resolved. The actual signal aspect in the already-known format. This is what the trains use to determine halt/proceed and speed. In this, the dst field has to be resolved.
asp = { asp = {
@ -62,7 +66,10 @@ asp = {
Node definition of signals: Node definition of signals:
- The signal needs some logic to figure out, for each combination of its own aspect group and the distant signal's aspect, what aspect info it can/will show. - The signal needs some logic to figure out, for each combination of its own aspect group and the distant signal's aspect, what aspect info it can/will show.
ndef.advtrains = { ndef.advtrains = {
aspect_groups = { [name] = { description = "Proceed at full speed", <more data at discretion of signal>} } main_aspects = {
{ name = "proceed" description = "Proceed at full speed", <more data at discretion of signal>}
{ name = "proceed2" description = "Proceed at full speed", <more data at discretion of signal>}
} -- The numerical order determines the layout of the list in the selection dialog.
apply_aspect = function(pos, asp_group, dst_aspgrp, dst_aspinfo) apply_aspect = function(pos, asp_group, dst_aspgrp, dst_aspinfo)
-- set the node to show the desired aspect -- set the node to show the desired aspect
-- called by advtrains when this signal's aspect group or the distant signal's aspect changes -- called by advtrains when this signal's aspect group or the distant signal's aspect changes
@ -72,28 +79,61 @@ ndef.advtrains = {
} }
]] ]]
advtrains.interlocking.signal_convert_aspect_if_necessary = advtrains.interlocking.aspect -- Set a signal's aspect.
-- Signal aspects should only be set through this function. It takes care of:
-- - Storing the main aspect and dst pos for this signal permanently (until next change)
-- - Assigning the distant signal for this signal
-- - Calling apply_aspect() in the signal's node definition to make the signal show the aspect
-- - Calling apply_aspect() again whenever the distant signal changes its aspect
-- - Notifying this signal's distant signals about changes to this signal (unless skip_dst_notify is specified)
function signal.set_aspect(pos, main_aspect, dst_pos, skip_dst_notify)
-- TODO
end
function advtrains.interlocking.update_signal_aspect(tcbs, skipdst) -- Gets the stored main aspect and distant signal position for this signal
-- This information equals the information last passed to set_aspect
-- It does not take into consideration the actual speed signalling, please use
-- get_aspect_info() for this
-- returns: main_aspect, dst_pos
function signal.get_aspect(pos)
--TODO
end
function signal.get_distant_signals_of(pos)
--TODO
end
-- Called when either this signal has changed its main aspect
-- or when this distant signal's currently assigned main signal has changed its aspect
-- It retrieves the signal's main aspect and aspect info and calls apply_aspect of the node definition
-- to update the signal's appearance and aspect info
-- pts: The signal position to update as encoded_pos
function signal.reapply_aspect(pts, p_mainaspect)
--TODO
end
-- Update this signal's aspect based on the set route
--
function signal.update_route_aspect(tcbs, skip_dst_notify)
if tcbs.signal then if tcbs.signal then
local asp = tcbs.aspect or DANGER local asp = tcbs.aspect or signal.MASP_HALT
advtrains.interlocking.signal_set_aspect(tcbs.signal, asp, skipdst) signal.set_aspect(tcbs.signal, asp, skip_dst_notify)
end end
end end
function advtrains.interlocking.signal_can_dig(pos) function signal.can_dig(pos)
return not advtrains.interlocking.db.get_sigd_for_signal(pos) return not advtrains.interlocking.db.get_sigd_for_signal(pos)
end end
function advtrains.interlocking.signal_after_dig(pos) function advtrains.interlocking.signal_after_dig(pos)
-- clear influence point -- clear influence point
advtrains.interlocking.signal_clear_aspect(pos) advtrains.interlocking.signal_clear_aspect(pos)
advtrains.distant.unassign_all(pos, true) advtrains.distant.unassign_all(pos, true) -- TODO
end end
-- should be called when aspect has changed on this signal. -- Update waiting trains and distant signals about a changed signal aspect
function advtrains.interlocking.signal_on_aspect_changed(pos) function signal.notify_on_aspect_changed(pos, skip_dst_notify)
--TODO update distant?
local ipts, iconn = advtrains.interlocking.db.get_ip_by_signalpos(pos) local ipts, iconn = advtrains.interlocking.db.get_ip_by_signalpos(pos)
if not ipts then return end if not ipts then return end
local ipos = minetest.string_to_pos(ipts) local ipos = minetest.string_to_pos(ipts)
@ -103,7 +143,7 @@ function advtrains.interlocking.signal_on_aspect_changed(pos)
minetest.after(0, advtrains.invalidate_all_paths, ipos) minetest.after(0, advtrains.invalidate_all_paths, ipos)
end end
function advtrains.interlocking.signal_rc_handler(pos, node, player, itemstack, pointed_thing) function signal.on_rightclick(pos, node, player, itemstack, pointed_thing)
local pname = player:get_player_name() local pname = player:get_player_name()
local control = player:get_player_control() local control = player:get_player_control()
if control.aux1 then if control.aux1 then
@ -122,7 +162,7 @@ function advtrains.interlocking.show_signal_form(pos, node, pname)
if ndef.advtrains and ndef.advtrains.set_aspect then if ndef.advtrains and ndef.advtrains.set_aspect then
-- permit to set aspect manually -- permit to set aspect manually
local function callback(pname, aspect) local function callback(pname, aspect)
advtrains.interlocking.signal_set_aspect(pos, aspect) signal.set_aspect(pos, aspect)
end end
local isasp = advtrains.interlocking.signal_get_aspect(pos, node) local isasp = advtrains.interlocking.signal_get_aspect(pos, node)
@ -138,18 +178,6 @@ function advtrains.interlocking.show_signal_form(pos, node, pname)
end end
end end
-- Returns the aspect the signal at pos is supposed to show
function advtrains.interlocking.signal_get_supposed_aspect(pos)
local sigd = advtrains.interlocking.db.get_sigd_for_signal(pos)
if sigd then
local tcbs = advtrains.interlocking.db.get_tcbs(sigd)
if tcbs.aspect then
return convert_aspect_if_necessary(tcbs.aspect)
end
end
return DANGER;
end
local players_assign_ip = {} local players_assign_ip = {}
local function ipmarker(ipos, connid) local function ipmarker(ipos, connid)
@ -236,7 +264,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end) end)
-- inits the signal IP assignment process -- inits the signal IP assignment process
function advtrains.interlocking.signal_init_ip_assign(pos, pname) function signal.init_ip_assign(pos, pname)
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
@ -281,3 +309,6 @@ minetest.register_on_punchnode(function(pos, node, player, pointed_thing)
players_assign_ip[pname] = nil players_assign_ip[pname] = nil
end end
end) end)
advtrains.interlocking.signal = signal

View File

@ -11,10 +11,9 @@ local function asp_to_zs3type(asp)
return math.min(16,4*math.floor(n/4)) return math.min(16,4*math.floor(n/4))
end end
local function setzs3(msp, lim, rot) local function setzs3(msp, asp, rot)
local pos = {x = msp.x, y = msp.y+1, z = msp.z} local pos = {x = msp.x, y = msp.y+1, z = msp.z}
local node = advtrains.ndb.get_node(pos) local node = advtrains.ndb.get_node(pos)
local asp = asp_to_zs3type(lim)
if node.name:find("^advtrains_signals_ks:zs3_") then if node.name:find("^advtrains_signals_ks:zs3_") then
advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:zs3_"..asp.."_"..rot, param2 = node.param2}) advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:zs3_"..asp.."_"..rot, param2 = node.param2})
end end
@ -50,67 +49,106 @@ local function getzs3v(msp)
end end
local setaspectf = function(rot) local setaspectf = function(rot)
return function(pos, node, asp) return function(pos, node, main_aspect, dst_aspect, dst_aspect_info)
setzs3(pos, asp.main, rot) -- set zs3 signal to show speed according to main_aspect
if asp.main == 0 then setzs3(pos, asp.zs3, rot)
if asp.shunt then -- select appropriate lamps based on mainaspect and dst
if main_aspect.shunt then
advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:hs_shunt_"..rot, param2 = node.param2}) advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:hs_shunt_"..rot, param2 = node.param2})
else setzs3v(pos, nil, rot)
elseif main_aspect.halt then
advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:hs_danger_"..rot, param2 = node.param2}) advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:hs_danger_"..rot, param2 = node.param2})
end
setzs3v(pos, nil, rot) setzs3v(pos, nil, rot)
else else
if not asp.dst or asp.dst == -1 then if not dst_aspect_info
or not dst_aspect_info.main
or dst_aspect_info.main == -1 then
advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:hs_free_"..rot, param2 = node.param2}) advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:hs_free_"..rot, param2 = node.param2})
elseif asp.dst == 0 then setzs3v(pos, nil, rot)
elseif dst_aspect_info.main == 0 then
advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:hs_slow_"..rot, param2 = node.param2}) advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:hs_slow_"..rot, param2 = node.param2})
setzs3v(pos, nil, rot)
else else
advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:hs_nextslow_"..rot, param2 = node.param2}) advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:hs_nextslow_"..rot, param2 = node.param2})
setzs3v(pos, dst_aspect_info.main, rot)
end end
setzs3v(pos, asp.dst, rot)
end end
end end
end end
-- Main aspects main signal
local suppasp = { -- These aspects tell only the speed signalization at this signal.
main = {0, 4, 6, 8, 12, 16, -1}, -- Actual signal aspect is chosen based on this and the Dst signal.
dst = {0, 4, 6, 8, 12, 16, -1, false}, local mainaspects_main = {
shunt = nil, {
proceed_as_main = true, name = "proceed"
info = { description = "Proceed",
call_on = false, zs3 = "off"
dead_end = false, },
w_speed = nil, {
} name = "shunt"
description = "Shunt",
zs3 = "off",
shunt = true,
},
{
name = "proceed_16"
description = "Proceed (speed 16)",
zs3 = "16",
},
{
name = "proceed_12"
description = "Proceed (speed 12)",
zs3 = "12",
},
{
name = "proceed_8"
description = "Proceed (speed 8)",
zs3 = "8",
},
{
name = "proceed_6"
description = "Proceed (speed 6)",
zs3 = "6",
},
{
name = "proceed_4"
description = "Proceed (speed 4)",
zs3 = "4",
},
{
name = "halt"
description = "Halt",
zs3 = "off",
halt = true,
},
} }
--Rangiersignal --Rangiersignal
local setaspectf_ra = function(rot) local applyaspectf_ra = function(rot)
return function(pos, node, asp) -- we get here the full main_aspect table
if asp.shunt then return function(pos, node, main_aspect, dst_aspect, dst_aspect_info)
if main_aspect.shunt then
advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:ra_shuntd_"..rot, param2 = node.param2}) advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:ra_shuntd_"..rot, param2 = node.param2})
else else
advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:ra_danger_"..rot, param2 = node.param2}) advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:ra_danger_"..rot, param2 = node.param2})
end end
local meta = minetest.get_meta(pos)
if meta then
meta:set_string("infotext", minetest.serialize(asp))
end
end end
end end
local suppasp_ra = { -- Main aspects shunt signal
main = { false }, -- Shunt signals have only two states, distant doesn't matter
dst = { false }, local mainaspects_shunt = {
shunt = nil, {
proceed_as_main = false, name = "shunt"
description = "Shunt",
info = { shunt = true,
call_on = false, },
dead_end = false, {
w_speed = nil, name = "halt"
} description = "Halt",
halt = true,
},
} }
advtrains.trackplacer.register_tracktype("advtrains_signals_ks:hs") advtrains.trackplacer.register_tracktype("advtrains_signals_ks:hs")
@ -192,9 +230,9 @@ for _, rtab in ipairs({
drop = "advtrains_signals_ks:hs_danger_0", drop = "advtrains_signals_ks:hs_danger_0",
inventory_image = "advtrains_signals_ks_hs_inv.png", inventory_image = "advtrains_signals_ks_hs_inv.png",
advtrains = { advtrains = {
set_aspect = setaspectf(rot), main_aspects = mainaspects_main
supported_aspects = suppasp, apply_aspect = applyaspectf_main(rot),
get_aspect = afunc, get_aspect_info = afunc,
}, },
on_rightclick = advtrains.interlocking.signal_rc_handler, on_rightclick = advtrains.interlocking.signal_rc_handler,
can_dig = advtrains.interlocking.signal_can_dig, can_dig = advtrains.interlocking.signal_can_dig,
@ -235,11 +273,9 @@ for _, rtab in ipairs({
drop = "advtrains_signals_ks:ra_danger_0", drop = "advtrains_signals_ks:ra_danger_0",
inventory_image = "advtrains_signals_ks_ra_inv.png", inventory_image = "advtrains_signals_ks_ra_inv.png",
advtrains = { advtrains = {
set_aspect = setaspectf_ra(rot), main_aspects = mainaspects_ra,
supported_aspects = suppasp_ra, apply_aspect = applyaspectf_ra(rot),
get_aspect = function(pos, node) get_aspect_info = prts.asp,
return prts.asp
end,
}, },
on_rightclick = advtrains.interlocking.signal_rc_handler, on_rightclick = advtrains.interlocking.signal_rc_handler,
can_dig = advtrains.interlocking.signal_can_dig, can_dig = advtrains.interlocking.signal_can_dig,
@ -276,7 +312,7 @@ for _, rtab in ipairs({
drop = "advtrains_signals_ks:"..prefix.."_"..dtyp.."_0", drop = "advtrains_signals_ks:"..prefix.."_"..dtyp.."_0",
inventory_image = inv, inventory_image = inv,
advtrains = { advtrains = {
get_aspect = function() return asp end get_aspect_info = asp
}, },
on_rightclick = advtrains.interlocking.signal_rc_handler, on_rightclick = advtrains.interlocking.signal_rc_handler,
can_dig = advtrains.interlocking.signal_can_dig, can_dig = advtrains.interlocking.signal_can_dig,