diff --git a/advtrains/init.lua b/advtrains/init.lua index 228ad5d..4559dfe 100644 --- a/advtrains/init.lua +++ b/advtrains/init.lua @@ -69,6 +69,8 @@ function advtrains.print_concat_table(a) if type(t)=="table" then if t.x and t.y and t.z then str=str..minetest.pos_to_string(t) + elseif t.p and t.s then -- interlocking sigd + str=str.."("..t.p.."/"..t.s..")" else str=str..dump(t) end @@ -270,7 +272,8 @@ advtrains.avt_save = function(remove_players_from_wagons) "last_pos", "last_connid", "last_frac", "velocity", "tarvelocity", "trainparts", "recently_collided_with_env", "atc_brake_target", "atc_wait_finish", "atc_command", "atc_delay", "door_open", - "text_outside", "text_inside", "couple_lck_front", "couple_lck_back", "line" + "text_outside", "text_inside", "couple_lck_front", "couple_lck_back", "line", + "il_sections" }) --then save it tmp_trains[id]=v diff --git a/advtrains/trainlogic.lua b/advtrains/trainlogic.lua index df3ec92..6c00fa5 100644 --- a/advtrains/trainlogic.lua +++ b/advtrains/trainlogic.lua @@ -174,6 +174,7 @@ end -- Occupation Callback system -- see occupation.lua +-- signature is advtrains.te_register_on_(function(id, train) ... end) local function mkcallback(name) local callt = {} @@ -468,23 +469,49 @@ end -- (remember, train.end_index is set separately because callbacks are -- asserted to rely on this) -local function tnc_call_enter_callback(pos, train_id) +local function mknodecallback(name) + local callt = {} + advtrains["tnc_register_on_"..name] = function(func) + assertt(func, "function") + table.insert(callt, func) + end + return callt, function(pos, id, train, index) + for _,f in ipairs(callt) do + f(pos, id, train, index) + end + end +end + +-- enter/leave-node callbacks +-- signature is advtrains.tnc_register_on_enter/leave(function(pos, id, train, index) ... end) +local callbacks_enter_node, run_callbacks_enter_node = mknodecallback("enter") +local callbacks_leave_node, run_callbacks_leave_node = mknodecallback("leave") + + +local function tnc_call_enter_callback(pos, train_id, train, index) --atdebug("tnc enter",pos,train_id) local node = advtrains.ndb.get_node(pos) --this spares the check if node is nil, it has a name in any case local mregnode=minetest.registered_nodes[node.name] if mregnode and mregnode.advtrains and mregnode.advtrains.on_train_enter then - mregnode.advtrains.on_train_enter(pos, train_id) + mregnode.advtrains.on_train_enter(pos, train_id, train, index) end + + -- call other registered callbacks + run_callbacks_enter_node(pos, train_id, train, index) end -local function tnc_call_leave_callback(pos, train_id) +local function tnc_call_leave_callback(pos, train_id, train, index) --atdebug("tnc leave",pos,train_id) local node = advtrains.ndb.get_node(pos) --this spares the check if node is nil, it has a name in any case local mregnode=minetest.registered_nodes[node.name] if mregnode and mregnode.advtrains and mregnode.advtrains.on_train_leave then - mregnode.advtrains.on_train_leave(pos, train_id) - end + mregnode.advtrains.on_train_leave(pos, train_id, train, index) + end + + -- call other registered callbacks + run_callbacks_leave_node(pos, train_id, train, index) end + advtrains.te_register_on_new_path(function(id, train) train.tnc = { old_index = atround(train.index), @@ -501,11 +528,11 @@ advtrains.te_register_on_update(function(id, train) while old_index < new_index do old_index = old_index + 1 local pos = advtrains.round_vector_floor_y(advtrains.path_get(train,old_index)) - tnc_call_enter_callback(pos, id) + tnc_call_enter_callback(pos, id, train, old_index) end while old_end_index < new_end_index do local pos = advtrains.round_vector_floor_y(advtrains.path_get(train,old_end_index)) - tnc_call_leave_callback(pos, id) + tnc_call_leave_callback(pos, id, train, old_end_index) old_end_index = old_end_index + 1 end train.tnc.old_index = new_index @@ -517,7 +544,7 @@ advtrains.te_register_on_create(function(id, train) local end_index = atround(train.end_index) while end_index <= index do local pos = advtrains.round_vector_floor_y(advtrains.path_get(train,end_index)) - tnc_call_enter_callback(pos, id) + tnc_call_enter_callback(pos, id, train, end_index) end_index = end_index + 1 end --atdebug(id,"tnc create",train.index,train.end_index) @@ -528,7 +555,7 @@ advtrains.te_register_on_remove(function(id, train) local end_index = atround(train.end_index) while end_index <= index do local pos = advtrains.round_vector_floor_y(advtrains.path_get(train,end_index)) - tnc_call_leave_callback(pos, id) + tnc_call_leave_callback(pos, id, train, end_index) end_index = end_index + 1 end --atdebug(id,"tnc remove",train.index,train.end_index) diff --git a/advtrains_interlocking/database.lua b/advtrains_interlocking/database.lua index e9e484c..f4a6871 100644 --- a/advtrains_interlocking/database.lua +++ b/advtrains_interlocking/database.lua @@ -382,7 +382,7 @@ end -- Utilize the traverser to find the track section at the specified position -- Returns: --- ts_id - the first found ts +-- ts_id, origin - the first found ts and the sigd of the found tcb -- nil - there were no TCBs in TRAVERSER_MAX range of the position, or track ends were reached -- false - the first found TCB stated End-Of-Interlocking function ildb.get_ts_at_pos(pos) @@ -397,7 +397,7 @@ function ildb.get_ts_at_pos(pos) local tcbs = ildb.get_tcbs(found_tcbs[1]) local ts if tcbs.ts_id then - return tcbs.ts_id + return tcbs.ts_id, found_tcbs[1] else return false end diff --git a/advtrains_interlocking/tcb_ts_ui.lua b/advtrains_interlocking/tcb_ts_ui.lua index 7e4d904..0ebe767 100644 --- a/advtrains_interlocking/tcb_ts_ui.lua +++ b/advtrains_interlocking/tcb_ts_ui.lua @@ -244,6 +244,15 @@ function advtrains.interlocking.show_ts_form(ts_id, pname, sel_tcb) form = form.."button[5.5,5;4,1;del_tcb;Unlink selected TCB]" hint = 2 end + + -- occupying trains + if ts.trains and #ts.trains>0 then + form = form.."label[0.5,6.1;Trains on this section:]" + form = form.."textlist[0.5,6.7;3,2;trnlist;"..table.concat(ts.trains, ",").."]" + else + form = form.."label[0.5,6.1;No trains on this section.]" + end + if hint == 1 then form = form.."label[0.5,0.75;Use the 'Join' button to designate rail crosses and link not listed far-away TCBs]" elseif hint == 2 then @@ -327,7 +336,7 @@ minetest.register_entity("advtrains_interlocking:tcbmarker", { }) function advtrains.interlocking.show_tcb_marker(pos) - atdebug("showing tcb marker",pos) + --atdebug("showing tcb marker",pos) local tcb = advtrains.interlocking.db.get_tcb(pos) if not tcb then return end local node_ok, conns, rhe = advtrains.get_rail_info_at(pos, advtrains.all_tracktypes) diff --git a/advtrains_interlocking/train_related.lua b/advtrains_interlocking/train_related.lua index 3e1c05b..3d7e280 100644 --- a/advtrains_interlocking/train_related.lua +++ b/advtrains_interlocking/train_related.lua @@ -1,2 +1,152 @@ -- train_related.lua -- Occupation of track sections - mainly implementation of train callbacks + +--[[ +Track section occupation is saved as follows + +In train: +train.il_sections = { + [n] = {ts_id = <...> (origin = )} +} +-- "origin" is the TCB (signal describer) the train initially entered this section + +In track section +ts.trains = { + [n] = +} + +When any inconsistency is detected, we will assume the most restrictive setup. +It will be possible to indicate a section "free" via the GUI. +]] + +local ildb = advtrains.interlocking.db + + +local function itexist(tbl, com) + for _,item in ipairs(tbl) do + if (item==com) then + return true + end + end + return false +end +local function itkexist(tbl, ikey, com) + for _,item in ipairs(tbl) do + if item[ikey] == com then + return true + end + end + return false +end + +local function itremove(tbl, com) + local i=1 + while i <= #tbl do + if tbl[i] == com then + table.remove(tbl, i) + else + i = i + 1 + end + end +end +local function itkremove(tbl, ikey, com) + local i=1 + while i <= #tbl do + if tbl[i][ikey] == com then + table.remove(tbl, i) + else + i = i + 1 + end + end +end + +local function setsection(tid, train, ts_id, ts, origin) + -- train + if not train.il_sections then train.il_sections = {} end + if not itkexist(train.il_sections, "ts_id", ts_id) then + table.insert(train.il_sections, {ts_id = ts_id, origin = origin}) + end + + -- ts + if not ts.trains then ts.trains = {} end + if not itexist(ts.trains, tid) then + table.insert(ts.trains, tid) + end + +end + +local function freesection(tid, train, ts_id, ts) + -- train + if not train.il_sections then train.il_sections = {} end + itkremove(train.il_sections, "ts_id", ts_id) + + -- ts + if not ts.trains then ts.trains = {} end + itremove(ts.trains, tid) + +end + + +-- This is regular operation +-- The train is on a track and drives back and forth + +-- This sets the section for both directions, to be failsafe +advtrains.tnc_register_on_enter(function(pos, id, train, index) + local tcb = ildb.get_tcb(pos) + if tcb then + for connid=1,2 do + local ts = tcb[connid].ts_id and ildb.get_ts(tcb[connid].ts_id) + if ts then + setsection(id, train, tcb[connid].ts_id, ts, {p=pos, s=connid}) + end + end + end +end) + + +-- this time, of course, only clear the backside (cp connid) +advtrains.tnc_register_on_leave(function(pos, id, train, index) + local tcb = ildb.get_tcb(pos) + if tcb and train.path_cp[index] then + local connid = train.path_cp[index] + local ts = tcb[connid].ts_id and ildb.get_ts(tcb[connid].ts_id) + if ts then + freesection(id, train, tcb[connid].ts_id, ts) + end + end +end) + +-- those callbacks are needed to account for created and removed trains (also regarding coupling) + +advtrains.te_register_on_create(function(id, train) + -- let's see what track sections we find here + local index = atround(train.index) + local pos = advtrains.path_get(train, index) + local ts_id, origin = ildb.get_ts_at_pos(pos) + if ts_id then + local ts = ildb.get_ts(ts_id) + if ts then + setsection(id, train, ts_id, ts, origin) + else + atwarn("ILDB corruption: TCB",origin," has invalid TS reference") + end + elseif ts_id==nil then + atwarn("Train",id,": Unable to determine whether to block a track section!") + else + atdebug("Train",id,": Outside of interlocked area!") + end +end) + +advtrains.te_register_on_remove(function(id, train) + if train.il_sections then + for idx, item in ipairs(train.il_sections) do + + local ts = item.ts_id and ildb.get_ts(item.ts_id) + + if ts and ts.trains then + itremove(ts.trains, id) + end + end + train.il_sections = nil + end +end)