aboutsummaryrefslogtreecommitdiff
path: root/advtrains/init.lua
diff options
context:
space:
mode:
Diffstat (limited to 'advtrains/init.lua')
-rw-r--r--advtrains/init.lua737
1 files changed, 737 insertions, 0 deletions
diff --git a/advtrains/init.lua b/advtrains/init.lua
new file mode 100644
index 0000000..7cc0ccd
--- /dev/null
+++ b/advtrains/init.lua
@@ -0,0 +1,737 @@
+
+--[[
+Advanced Trains - Minetest Mod
+
+Copyright (C) 2016-2020 Moritz Blei (orwell96) and contributors
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+]]
+
+local lot = os.clock()
+minetest.log("action", "[advtrains] Loading...")
+
+-- There is no need to support 0.4.x anymore given that the compatitability with it is already broken by 1bb1d825f46af3562554c12fba35a31b9f7973ff
+attrans = minetest.get_translator ("advtrains")
+
+--advtrains
+advtrains = {trains={}, player_to_train_mapping={}}
+
+-- =======================Development/debugging settings=====================
+-- DO NOT USE FOR NORMAL OPERATION
+local DUMP_DEBUG_SAVE = false
+-- dump the save files in human-readable format into advtrains_DUMP
+
+local GENERATE_ATRICIFIAL_LAG = false
+local HOW_MANY_LAG = 1.0
+-- Simulate a higher server step interval, as it occurs when the server is on high load
+
+advtrains.IGNORE_WORLD = false
+-- Run advtrains without respecting the world map
+-- - No world collision checks occur
+-- - The NDB forcibly places all nodes stored in it into the world regardless of the world's content.
+-- - Rails do not set the 'attached_node' group
+-- This mode can be useful for debugging/testing a world without the map data available
+-- In this case, choose 'singlenode' as mapgen
+
+local NO_SAVE = false
+-- Do not save any data to advtrains save files
+
+-- ==========================================================================
+
+-- Use a global slowdown factor to slow down train movements. Now a setting
+advtrains.DTIME_LIMIT = tonumber(minetest.settings:get("advtrains_dtime_limit")) or 0.2
+advtrains.SAVE_INTERVAL = tonumber(minetest.settings:get("advtrains_save_interval")) or 60
+
+--Constant for maximum connection value/division of the circle
+AT_CMAX = 16
+
+-- get wagon loading range
+advtrains.wagon_load_range = tonumber(minetest.settings:get("advtrains_wagon_load_range"))
+if not advtrains.wagon_load_range then
+ advtrains.wagon_load_range = tonumber(minetest.settings:get("active_block_range"))*16
+end
+
+--pcall
+local no_action=false
+
+local function reload_saves()
+ atwarn("Restoring saved state in 1 second...")
+ no_action=true
+ advtrains.lock_path_inval = false
+ --read last save state and continue, as if server was restarted
+ for aoi, le in pairs(minetest.luaentities) do
+ if le.is_wagon then
+ le.object:remove()
+ end
+ end
+ minetest.after(1, function()
+ advtrains.load()
+ atwarn("Reload successful!")
+ advtrains.ndb.restore_all()
+ end)
+end
+
+advtrains.modpath = minetest.get_modpath("advtrains")
+
+--Advtrains dump (special treatment of pos and sigd)
+function atdump(t, intend)
+ local str
+ if type(t)=="table" then
+ if t.x and t.y and t.z then
+ str=minetest.pos_to_string(t)
+ elseif t.p and t.s then -- interlocking sigd
+ str="S["..minetest.pos_to_string(t.p).."/"..t.s.."]"
+ elseif advtrains.lines and t.s and t.m then -- RwT
+ str=advtrains.lines.rwt.to_string(t)
+ else
+ str="{"
+ local intd = (intend or "") .. " "
+ for k,v in pairs(t) do
+ if type(k)~="string" or not string.match(k, "^path[_]?") then
+ -- do not print anything path-related
+ str = str .. "\n" .. intd .. atdump(k, intd) .. " = " ..atdump(v, intd)
+ end
+ end
+ str = str .. "\n" .. (intend or "") .. "}"
+ end
+ elseif type(t)=="boolean" then
+ if t then
+ str="true"
+ else
+ str="false"
+ end
+ elseif type(t)=="function" then
+ str="<function>"
+ elseif type(t)=="userdata" then
+ str="<userdata>"
+ else
+ str=""..t
+ end
+ return str
+end
+
+function advtrains.print_concat_table(a)
+ local str=""
+ local stra=""
+ local t
+ for i=1,20 do
+ t=a[i]
+ if t==nil then
+ stra=stra.."nil "
+ else
+ str=str..stra
+ stra=""
+ str=str..atdump(t).." "
+ end
+ end
+ return str
+end
+
+atprint=function() end
+atlog=function(t, ...)
+ local text=advtrains.print_concat_table({t, ...})
+ minetest.log("action", "[advtrains]"..text)
+end
+atwarn=function(t, ...)
+ local text=advtrains.print_concat_table({t, ...})
+ minetest.log("warning", "[advtrains]"..text)
+ minetest.chat_send_all("[advtrains] -!- "..text)
+end
+sid=function(id) if id then return string.sub(id, -6) end end
+
+
+--ONLY use this function for temporary debugging. for consistent debug prints use atprint
+atdebug=function(t, ...)
+ local text=advtrains.print_concat_table({t, ...})
+ minetest.log("action", "[advtrains]"..text)
+ minetest.chat_send_all("[advtrains]"..text)
+end
+
+if minetest.settings:get_bool("advtrains_enable_debugging") then
+ atprint=function(t, ...)
+ local context=advtrains.atprint_context_tid or ""
+ if not context then return end
+ local text=advtrains.print_concat_table({t, ...})
+ advtrains.drb_record(context, text)
+
+ --atlog("@@",advtrains.atprint_context_tid,t,...)
+ end
+ dofile(advtrains.modpath.."/debugringbuffer.lua")
+
+end
+
+function assertt(var, typ)
+ if type(var)~=typ then
+ error("Assertion failed, variable has to be of type "..typ)
+ end
+end
+
+dofile(advtrains.modpath.."/helpers.lua");
+--dofile(advtrains.modpath.."/debugitems.lua");
+
+advtrains.meseconrules =
+{{x=0, y=0, z=-1},
+ {x=1, y=0, z=0},
+ {x=-1, y=0, z=0},
+ {x=0, y=0, z=1},
+ {x=1, y=1, z=0},
+ {x=1, y=-1, z=0},
+ {x=-1, y=1, z=0},
+ {x=-1, y=-1, z=0},
+ {x=0, y=1, z=1},
+ {x=0, y=-1, z=1},
+ {x=0, y=1, z=-1},
+ {x=0, y=-1, z=-1},
+ {x=0, y=-2, z=0}}
+
+advtrains.fpath=minetest.get_worldpath().."/advtrains"
+
+dofile(advtrains.modpath.."/path.lua")
+dofile(advtrains.modpath.."/trainlogic.lua")
+dofile(advtrains.modpath.."/trainhud.lua")
+dofile(advtrains.modpath.."/trackplacer.lua")
+dofile(advtrains.modpath.."/copytool.lua")
+dofile(advtrains.modpath.."/tracks.lua")
+dofile(advtrains.modpath.."/occupation.lua")
+dofile(advtrains.modpath.."/atc.lua")
+dofile(advtrains.modpath.."/wagons.lua")
+dofile(advtrains.modpath.."/protection.lua")
+
+dofile(advtrains.modpath.."/trackdb_legacy.lua")
+dofile(advtrains.modpath.."/nodedb.lua")
+dofile(advtrains.modpath.."/couple.lua")
+
+dofile(advtrains.modpath.."/signals.lua")
+dofile(advtrains.modpath.."/misc_nodes.lua")
+dofile(advtrains.modpath.."/crafting.lua")
+dofile(advtrains.modpath.."/craft_items.lua")
+
+dofile(advtrains.modpath.."/log.lua")
+dofile(advtrains.modpath.."/passive.lua")
+if mesecon then
+ dofile(advtrains.modpath.."/p_mesecon_iface.lua")
+end
+
+
+dofile(advtrains.modpath.."/lzb.lua")
+
+
+--load/save
+
+-- backup variables, used if someone should accidentally delete a sub-mod
+-- As of version 4, only used once during migration from version 3 to 4
+-- Since version 4, each of the mods stores a separate save file.
+local MDS_interlocking, MDS_lines
+
+
+advtrains.fpath=minetest.get_worldpath().."/advtrains"
+dofile(advtrains.modpath.."/log.lua")
+function advtrains.read_component(name)
+ local path = advtrains.fpath.."_"..name
+ minetest.log("action", "[advtrains] loading "..path)
+ local file, err = io.open(path, "r")
+ if not file then
+ minetest.log("warning", " Failed to read advtrains save data from file "..path..": "..(err or "Unknown Error"))
+ minetest.log("warning", " (this is normal when first enabling advtrains on this world)")
+ return
+ end
+ local tbl = minetest.deserialize(file:read("*a"))
+ file:close()
+ return tbl
+end
+
+function advtrains.avt_load()
+ -- check for new, split advtrains save file
+
+ local version = advtrains.read_component("version")
+ local tbl
+ if version and version == 4 then
+ advtrains.load_version_4()
+ return
+ -- NOTE: From here, legacy loading code!
+ elseif version and version == 3 then
+ -- we are dealing with the split-up system
+ minetest.log("action", "[advtrains] loading savefiles version 3")
+ local il_save = {
+ tcbs = true,
+ ts = true,
+ signalass = true,
+ rs_locks = true,
+ rs_callbacks = true,
+ influence_points = true,
+ npr_rails = true,
+ }
+ tbl={
+ trains = true,
+ wagon_save = true,
+ ptmap = true,
+ atc = true,
+ ndb = true,
+ lines = true,
+ version = 2,
+ }
+ for i,k in pairs(il_save) do
+ il_save[i] = advtrains.read_component("interlocking_"..i)
+ end
+ for i,k in pairs(tbl) do
+ tbl[i] = advtrains.read_component(i)
+ end
+ tbl["interlocking"] = il_save
+ else
+ local file, err = io.open(advtrains.fpath, "r")
+ if not file then
+ minetest.log("warning", " Failed to read advtrains save data from file "..advtrains.fpath..": "..(err or "Unknown Error"))
+ minetest.log("warning", " (this is normal when first enabling advtrains on this world)")
+ return
+ else
+ tbl = minetest.deserialize(file:read("*a"))
+ file:close()
+ end
+ end
+ if type(tbl) == "table" then
+ if tbl.version then
+ --congrats, we have the new save format.
+ advtrains.trains = tbl.trains
+ --Save the train id into the train table to avoid having to pass id around
+ for id, train in pairs(advtrains.trains) do
+ train.id = id
+ end
+ advtrains.wagons = tbl.wagon_save
+ advtrains.player_to_train_mapping = tbl.ptmap or {}
+ advtrains.ndb.load_data_pre_v4(tbl.ndb)
+ advtrains.atc.load_data(tbl.atc)
+ if advtrains.interlocking then
+ advtrains.interlocking.db.load(tbl.interlocking)
+ else
+ MDS_interlocking = tbl.interlocking
+ end
+ if advtrains.lines then
+ advtrains.lines.load(tbl.lines)
+ else
+ MDS_lines = tbl.lines
+ end
+ --remove wagon_save entries that are not part of a train
+ local todel=advtrains.merge_tables(advtrains.wagon_save)
+ for tid, train in pairs(advtrains.trains) do
+ train.id = tid
+ for _, wid in ipairs(train.trainparts) do
+ todel[wid]=nil
+ end
+ end
+ for wid, _ in pairs(todel) do
+ atwarn("Removing unused wagon", wid, "from wagon_save table.")
+ advtrains.wagon_save[wid]=nil
+ end
+ else
+ --oh no, its the old one...
+ advtrains.trains=tbl
+ --load ATC
+ advtrains.fpath_atc=minetest.get_worldpath().."/advtrains_atc"
+ local file, err = io.open(advtrains.fpath_atc, "r")
+ if not file then
+ local er=err or "Unknown Error"
+ atprint("Failed loading advtrains atc save file "..er)
+ else
+ local tbl = minetest.deserialize(file:read("*a"))
+ if type(tbl) == "table" then
+ advtrains.atc.controllers=tbl.controllers
+ end
+ file:close()
+ end
+ --load wagon saves
+ advtrains.fpath_ws=minetest.get_worldpath().."/advtrains_wagon_save"
+ local file, err = io.open(advtrains.fpath_ws, "r")
+ if not file then
+ local er=err or "Unknown Error"
+ atprint("Failed loading advtrains save file "..er)
+ else
+ local tbl = minetest.deserialize(file:read("*a"))
+ if type(tbl) == "table" then
+ advtrains.wagon_save=tbl
+ end
+ file:close()
+ end
+ end
+ else
+ minetest.log("error", " Failed to deserialize advtrains save data: Not a table!")
+ end
+ -- moved from advtrains.load()
+ atlatc.load_pre_v4()
+ -- end of legacy loading code
+end
+
+function advtrains.load_version_4()
+ minetest.log("action", "[advtrains] loading savefiles version 4 (serialize_lib)")
+
+ --== load core ==
+ local at_save = serialize_lib.load_atomic(advtrains.fpath.."_core.ls")
+ if at_save then
+ advtrains.trains = at_save.trains
+ --Save the train id into the train table to avoid having to pass id around
+ for id, train in pairs(advtrains.trains) do
+ train.id = id
+ end
+ advtrains.wagons = at_save.wagons
+ advtrains.player_to_train_mapping = at_save.ptmap or {}
+ advtrains.atc.load_data(at_save.atc)
+
+ --remove wagon_save entries that are not part of a train
+ local todel=advtrains.merge_tables(advtrains.wagon_save)
+ for tid, train in pairs(advtrains.trains) do
+ train.id = tid
+ for _, wid in ipairs(train.trainparts) do
+ todel[wid]=nil
+ end
+ end
+ for wid, _ in pairs(todel) do
+ atwarn("Removing unused wagon", wid, "from wagon_save table.")
+ advtrains.wagon_save[wid]=nil
+ end
+ end
+ --== load ndb
+ serialize_lib.load_atomic(advtrains.fpath.."_ndb4.ls", advtrains.ndb.load_callback)
+
+ --== load interlocking ==
+ if advtrains.interlocking then
+ local il_save = serialize_lib.load_atomic(advtrains.fpath.."_interlocking.ls")
+ if il_save then
+ advtrains.interlocking.db.load(il_save)
+ end
+ end
+
+ --== load lines ==
+ if advtrains.lines then
+ local ln_save = serialize_lib.load_atomic(advtrains.fpath.."_lines.ls")
+ if ln_save then
+ advtrains.lines.load(ln_save)
+ end
+ end
+
+ --== load luaatc ==
+ if atlatc then
+ local la_save = serialize_lib.load_atomic(advtrains.fpath.."_atlatc.ls")
+ if la_save then
+ atlatc.load(la_save)
+ end
+ end
+end
+
+advtrains.save_component = function (tbl, name)
+ -- Saves each component of the advtrains file separately
+ --
+ -- required for now to shrink the advtrains db to overcome lua
+ -- limitations.
+ -- Note: as of version 4, only used for the "advtrains_version" file
+ local datastr = minetest.serialize(tbl)
+ if not datastr then
+ minetest.log("error", " Failed to serialize advtrains save data!")
+ return
+ end
+ local path = advtrains.fpath.."_"..name
+ local success = minetest.safe_file_write(path, datastr)
+
+ if not success then
+ minetest.log("error", " Failed to write advtrains save data to file "..path)
+ end
+
+end
+
+advtrains.avt_save = function(remove_players_from_wagons)
+ --atdebug("Saving advtrains files (version 4)")
+
+ if remove_players_from_wagons then
+ for w_id, data in pairs(advtrains.wagons) do
+ data.seatp={}
+ end
+ advtrains.player_to_train_mapping={}
+ end
+
+ local tmp_trains={}
+ for id, train in pairs(advtrains.trains) do
+ --first, deep_copy the train
+ if #train.trainparts > 0 then
+ local v=advtrains.save_keys(train, {
+ "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", "line", "routingcode",
+ "il_sections", "speed_restriction", "is_shunt",
+ "points_split", "autocouple", "ars_disable",
+ })
+ --then save it
+ tmp_trains[id]=v
+ else
+ atwarn("Train",id,"had no wagons left because of some bug. It is being deleted. Wave it goodbye!")
+ advtrains.remove_train(id)
+ end
+ end
+
+ for id, wdata in pairs(advtrains.wagons) do
+ local _,proto = advtrains.get_wagon_prototype(wdata)
+ if proto.has_inventory then
+ local inv=minetest.get_inventory({type="detached", name="advtrains_wgn_"..id})
+ if inv then -- inventory is not initialized when wagon was never loaded
+ -- TOOD: What happens with unloading rails when they don't find the inventory?
+ wdata.ser_inv=advtrains.serialize_inventory(inv)
+ end
+ end
+ -- TODO apply save-keys here too
+ -- TODO temp
+ wdata.dcpl_lock = nil
+ end
+
+ --versions:
+ -- 1 - Initial new save format.
+ -- 2 - version as of tss branch 11-2018+
+ -- 3 - split-up savefile system by gabriel
+ -- 4 - serialize_lib
+
+ -- save of core advtrains
+ local at_save={
+ trains = tmp_trains,
+ wagons = advtrains.wagons,
+ ptmap = advtrains.player_to_train_mapping,
+ atc = advtrains.atc.save_data(),
+ }
+
+ --save of interlocking
+ local il_save
+ if advtrains.interlocking then
+ il_save = advtrains.interlocking.db.save()
+ else
+ il_save = MDS_interlocking
+ end
+
+ -- save of lines
+ local ln_save
+ if advtrains.lines then
+ ln_save = advtrains.lines.save()
+ else
+ ln_save = MDS_lines
+ end
+
+ -- save of luaatc
+ local la_save
+ if atlatc then
+ la_save = atlatc.save()
+ end
+
+ -- parts table for serialize_lib API:
+ -- any table that is nil will not be included and thus not be overwritten
+ local parts_table = {
+ ["core.ls"] = at_save,
+ ["interlocking.ls"] = il_save,
+ ["lines.ls"] = ln_save,
+ ["atlatc.ls"] = la_save,
+ ["ndb4.ls"] = true, -- data not used
+ }
+ local callbacks_table = {
+ ["ndb4.ls"] = advtrains.ndb.save_callback
+ }
+
+ if DUMP_DEBUG_SAVE then
+ local file, err = io.open(advtrains.fpath.."_DUMP", "w")
+ if err then
+ return
+ end
+ file:write(dump(parts_table))
+ file:close()
+ end
+
+ --THE MAGIC HAPPENS HERE
+ local succ, err = serialize_lib.save_atomic_multiple(parts_table, advtrains.fpath.."_", callbacks_table)
+
+ if not succ then
+ atwarn("Saving failed: "..err)
+ else
+ -- store version
+ advtrains.save_component(4, "version")
+ end
+end
+
+--## MAIN LOOP ##--
+--Calls all subsequent main tasks of both advtrains and atlatc
+local init_load=false
+local save_timer = advtrains.SAVE_INTERVAL
+advtrains.mainloop_runcnt=0
+advtrains.global_slowdown = 1
+
+local t = 0
+minetest.register_globalstep(function(dtime_mt)
+ if no_action then
+ -- the advtrains globalstep is skipped by command. Return immediately
+ return
+ end
+
+ advtrains.mainloop_runcnt=advtrains.mainloop_runcnt+1
+ --atprint("Running the main loop, runcnt",advtrains.mainloop_runcnt)
+ --call load once. see advtrains.load() comment
+ if not init_load then
+ advtrains.load()
+ end
+
+ local dtime = dtime_mt * advtrains.global_slowdown
+ if GENERATE_ATRICIFIAL_LAG then
+ dtime = HOW_MANY_LAG
+ if os.clock()<t then
+ return
+ end
+
+ t = os.clock()+HOW_MANY_LAG
+ end
+ -- if dtime is too high, decrease global slowdown
+ if advtrains.DTIME_LIMIT~=0 then
+ if dtime > advtrains.DTIME_LIMIT then
+ if advtrains.global_slowdown > 0.1 then
+ advtrains.global_slowdown = advtrains.global_slowdown - 0.05
+ else
+ advtrains.global_slowdown = advtrains.global_slowdown / 2
+ end
+ dtime = advtrains.DTIME_LIMIT
+ end
+ -- recover global slowdown slowly over time
+ advtrains.global_slowdown = math.min(advtrains.global_slowdown*1.02, 1)
+ end
+
+ advtrains.mainloop_trainlogic(dtime,advtrains.mainloop_runcnt)
+ if advtrains_itm_mainloop then
+ advtrains_itm_mainloop(dtime)
+ end
+ if atlatc then
+ --atlatc.mainloop_stepcode(dtime)
+ atlatc.interrupt.mainloop(dtime)
+ end
+ if advtrains.lines then
+ advtrains.lines.step(dtime)
+ end
+
+ --trigger a save when necessary
+ save_timer=save_timer-dtime
+ if save_timer<=0 then
+ local t=os.clock()
+ --save
+ advtrains.save()
+ save_timer = advtrains.SAVE_INTERVAL
+ atprintbm("saving", t)
+ end
+end)
+
+--if something goes wrong in these functions, there is no help. no pcall here.
+
+--## MAIN LOAD ROUTINE ##
+-- Causes the loading of everything
+-- first time called in main loop (after the init phase) because luaautomation has to initialize first.
+function advtrains.load()
+ advtrains.avt_load() --loading advtrains. includes ndb at advtrains.ndb.load_data()
+ --if atlatc then
+ -- atlatc.load() --includes interrupts
+ --end == No longer loading here. Now part of avt_save() legacy loading.
+ if advtrains_itm_init then
+ advtrains_itm_init()
+ end
+ init_load=true
+ no_action=false
+ atlog("[load_all]Loaded advtrains save files")
+end
+
+--## MAIN SAVE ROUTINE ##
+-- Causes the saving of everything
+function advtrains.save(remove_players_from_wagons)
+ if not init_load then
+ --wait... we haven't loaded yet?!
+ atwarn("Instructed to save() but load() was never called!")
+ return
+ end
+
+ if advtrains.IGNORE_WORLD then
+ advtrains.ndb.restore_all()
+ end
+
+ if NO_SAVE then
+ return
+ end
+ if no_action then
+ atlog("[save] Saving requested externally, but Advtrains step is disabled. Not saving any data as state may be inconsistent.")
+ return
+ end
+
+ local t1 = os.clock()
+ advtrains.avt_save(remove_players_from_wagons) --saving advtrains. includes ndb at advtrains.ndb.save_data()
+ if atlatc then
+ atlatc.save()
+ end
+ atlog("Saved advtrains save files, took",math.floor((os.clock()-t1) * 1000),"ms")
+
+ -- Cleanup actions
+ --TODO very simple yet hacky workaround for the "green signals" bug
+ advtrains.invalidate_all_paths()
+end
+minetest.register_on_shutdown(advtrains.save)
+
+-- This chat command provides a solution to the problem known on the LinuxWorks server
+-- There are many players that joined a single time, got on a train and then left forever
+-- These players still occupy seats in the trains.
+minetest.register_chatcommand("at_empty_seats",
+ {
+ params = "", -- Short parameter description
+ description = "Detach all players, especially the offline ones, from all trains. Use only when no one serious is on a train.", -- Full description
+ privs = {train_operator=true, server=true}, -- Require the "privs" privilege to run
+ func = function(name, param)
+ atwarn("Data is being saved. While saving, advtrains will remove the players from trains. Save files will be reloaded afterwards!")
+ advtrains.save(true)
+ reload_saves()
+ end,
+})
+-- This chat command solves another problem: Trains getting randomly stuck.
+minetest.register_chatcommand("at_reroute",
+ {
+ params = "",
+ description = "Delete all train routes, force them to recalculate",
+ privs = {train_operator=true}, -- Only train operator is required, since this is relatively safe.
+ func = function(name, param)
+ advtrains.invalidate_all_paths()
+ return true, "Successfully invalidated train routes"
+ end,
+})
+
+minetest.register_chatcommand("at_disable_step",
+ {
+ params = "<yes/no>",
+ description = "Disable the advtrains globalstep temporarily",
+ privs = {server=true},
+ func = function(name, param)
+ if minetest.is_yes(param) then
+ -- disable everything, and turn off saving
+ no_action = true;
+ atwarn("The advtrains globalstep has been disabled. Trains are not moving, and no data is saved! Run '/at_disable_step no' to enable again!")
+ return true, "Disabled advtrains successfully"
+ elseif no_action then
+ atwarn("Re-enabling advtrains globalstep...")
+ reload_saves()
+ return true
+ else
+ return false, "Advtrains is already running normally!"
+ end
+ end,
+})
+
+advtrains.is_no_action = function()
+ return no_action
+end
+
+
+local tot=(os.clock()-lot)*1000
+minetest.log("action", "[advtrains] Loaded in "..tot.."ms")
+