Add approach callback mechanism to LuaATC

This commit is contained in:
orwell96 2019-12-09 12:18:54 +01:00
parent e935136d58
commit 74a219937d
2 changed files with 107 additions and 46 deletions

View File

@ -245,6 +245,34 @@ unset_autocouple()
set_shunt(), unset_shunt() set_shunt(), unset_shunt()
deprecated aliases for set_autocouple() and unset_autocouple(), will be removed from a later release. deprecated aliases for set_autocouple() and unset_autocouple(), will be removed from a later release.
# Approach callbacks
The LuaATC interface provides a way to hook into the approach callback system, which is for example used in the TSR rails (provided by advtrains_interlocking) or the station tracks (provided by advtrains_lines). However, for compatibility reasons, this behavior needs to be explicitly enabled.
Enabling the receiving of approach events works by setting a variable in the local environment of the ATC rail, by inserting the following code:
__approach_callback_mode = 1
-- to receive approach callbacks only in arrow direction
-- or alternatively
__approach_callback_mode = 2
-- to receive approach callbacks in both directions
The following event will be emitted when a train approaches:
{type="approach", approach=true, id="<train_id>"}
Please note these important considerations when using approach callbacks:
- Approach events might be generated multiple times for the same approaching train. If you are using atc_set_lzb_tsr(), you need to call this function on every run of the approach callback, even if you issued it before for the same train.
- A reference to the train is available while executing this event, so that functions such as atc_send() or atc_set_text_outside() can be called. On any consecutive interrupts, that reference will no longer be available until the train enters the track ("train" event)
- Unlike all other callbacks, approach callbacks are executed synchronous during the train step. This may cause unexpected side effects when performing certain actions (such as switching turnouts, setting signals/routes) from inside such a callback. I strongly encourage you to only run things that are absolutely necessary at this point in time, and defer anything else to an interrupt(). Be aware that certain things might trigger unexpected behavior.
Operations that are safe to execute in approach callbacks:
- anything related only to the global environment (setting things in S)
- digiline_send()
- atc_set_text_*()
- atc_set_lzb_tsr() (see below)
In the context of approach callbacks, one more function is available:
atc_set_lzb_tsr(speed)
Impose a Temporary Speed Restriction at the location of this rail, making the train pass this rail at the specified speed. (Causes the same behavior as the TSR rail)
# Operator panel # Operator panel
This simple node executes its actions when punched. It can be used to change a switch and update the corresponding signals or similar applications. This simple node executes its actions when punched. It can be used to change a switch and update the corresponding signals or similar applications.

View File

@ -5,7 +5,10 @@
--Using subtable --Using subtable
local r={} local r={}
function r.fire_event(pos, evtdata) -- Note on appr_internal:
-- The Approach callback is a special corner case: the train is not on the node, and it is executed synchronized
-- (in the train step right during LZB traversal). We therefore need access to the train id and the lzbdata table
function r.fire_event(pos, evtdata, appr_internal)
local ph=minetest.pos_to_string(pos) local ph=minetest.pos_to_string(pos)
local railtbl = atlatc.active.nodes[ph] local railtbl = atlatc.active.nodes[ph]
@ -27,6 +30,13 @@ function r.fire_event(pos, evtdata)
local train_id=advtrains.get_train_at_pos(pos) local train_id=advtrains.get_train_at_pos(pos)
local train, atc_arrow, tvel local train, atc_arrow, tvel
if train_id then train=advtrains.trains[train_id] end if train_id then train=advtrains.trains[train_id] end
if not train and appr_internal then
-- also use the train when called as an approach callback
train_id = appr_internal.train_id
train = appr_internal.train
end
if train then if train then
if not train.path then if not train.path then
--we happened to get in between an invalidation step --we happened to get in between an invalidation step
@ -92,20 +102,9 @@ function r.fire_event(pos, evtdata)
advtrains.train_step_fc(train) advtrains.train_step_fc(train)
end, end,
set_shunt = function() set_shunt = function()
-- enable shunting mode
if not train_id then return false end if not train_id then return false end
train.autocouple = true train.is_shunt = true
end,
unset_shunt = function()
if not train_id then return false end
train.autocouple = nil
end,
set_autocouple = function ()
if not train_id then return false end
train.autocouple = true
end,
unset_autocouple = function ()
if not train_id then return false end
train.autocouple = nil
end, end,
set_line = function(line) set_line = function(line)
if type(line)~="string" and type(line)~="number" then if type(line)~="string" and type(line)~="number" then
@ -150,13 +149,22 @@ function r.fire_event(pos, evtdata)
advtrains.trains[train_id].text_inside=text advtrains.trains[train_id].text_inside=text
return true return true
end, end,
atc_set_lzb_tsr = function(speed)
if not appr_internal then
error("atc_set_lzb_tsr() can only be used during 'approach' events!")
end
local index = appr_internal.index
advtrains.lzb_add_checkpoint(train, index, speed, nil)
return true
end,
} }
atlatc.active.run_in_env(pos, evtdata, customfct) atlatc.active.run_in_env(pos, evtdata, customfct)
end end
if minetest.get_modpath("advtrains_train_track") ~= nil then
advtrains.register_tracks("default", { advtrains.register_tracks("default", {
nodename_prefix="advtrains_luaautomation:dtrack", nodename_prefix="advtrains_luaautomation:dtrack",
texture_prefix="advtrains_dtrack_atc", texture_prefix="advtrains_dtrack_atc",
@ -169,14 +177,39 @@ if minetest.get_modpath("advtrains_train_track") ~= nil then
return { return {
after_place_node = atlatc.active.after_place_node, after_place_node = atlatc.active.after_place_node,
after_dig_node = atlatc.active.after_dig_node, after_dig_node = atlatc.active.after_dig_node,
on_receive_fields = function(pos, ...) on_receive_fields = function(pos, ...)
atlatc.active.on_receive_fields(pos, ...) atlatc.active.on_receive_fields(pos, ...)
--set arrowconn (for ATC) --set arrowconn (for ATC)
local ph=minetest.pos_to_string(pos) local ph=minetest.pos_to_string(pos)
local _, conns=advtrains.get_rail_info_at(pos, advtrains.all_tracktypes) local _, conns=advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
atlatc.active.nodes[ph].arrowconn=conns[1].c atlatc.active.nodes[ph].arrowconn=conns[1].c
end, end,
advtrains = atlatc.active.trackdef_advtrains_defs,
advtrains = {
on_train_enter = function(pos, train_id)
--do async. Event is fired in train steps
atlatc.interrupt.add(0, pos, {type="train", train=true, id=train_id})
end,
on_train_approach = function(pos, train_id, train, index, lzbdata)
-- Insert an event only if the rail indicated that it supports approach callbacks
local ph=minetest.pos_to_string(pos)
local railtbl = atlatc.active.nodes[ph]
-- uses a "magic variable" in the local environment of the node
-- This hack is necessary because code might not be prepared to get approach events...
if railtbl and railtbl.data and railtbl.data.__approach_callback_mode then
local acm = railtbl.data.__approach_callback_mode
if acm==2 or (acm==1 and train.path_cn[index] == 1) then
local evtdata = {type="approach", approach=true, id=train_id}
-- This event is *required* to run synchronously, because it might set the ars_disable flag on the train and add LZB checkpoints,
-- although this is generally discouraged because this happens right in a train step
-- At this moment, I am not aware whether this may cause side effects, and I must encourage users not to do expensive calculations here.
r.fire_event(pos, evtdata, {train_id = train_id, train = train, index = index, lzbdata = lzbdata})
end
end
end,
},
luaautomation = { luaautomation = {
fire_event=r.fire_event fire_event=r.fire_event
}, },
@ -189,6 +222,6 @@ if minetest.get_modpath("advtrains_train_track") ~= nil then
} }
end, end,
}, advtrains.trackpresets.t_30deg_straightonly) }, advtrains.trackpresets.t_30deg_straightonly)
end
atlatc.rail = r atlatc.rail = r