advtrains/advtrains/init.lua

401 lines
11 KiB
Lua

-- Boilerplate to support localized strings if intllib mod is installed.
if minetest.get_modpath("intllib") then
attrans = intllib.Getter()
else
attrans = function(s,a,...)a={a,...}return s:gsub("@(%d+)",function(n)return a[tonumber(n)]end)end
end
--advtrains
advtrains = {trains={}, wagon_save={}, player_to_train_mapping={}}
--pcall
local no_action=false
local function reload_saves()
atwarn("Restoring saved state in 1 second...")
no_action=true
--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
function advtrains.pcall(fun)
if no_action then return end
local succ, return1, return2, return3, return4=xpcall(fun, function(err)
if advtrains.atprint_context_tid then
local train=advtrains.trains[advtrains.atprint_context_tid_full]
advtrains.dumppath(train.path)
atwarn("Dumping last debug outputs: ", err)
atprint("Train state: index",train.index,"end_index", train.end_index,"| max_iot", train.max_index_on_track, "min_iot", train.min_index_on_track, "<> pe_min", train.path_extent_min,"pe_max", train.path_extent_max)
advtrains.drb_dump(advtrains.atprint_context_tid)
end
atwarn("Lua Error occured: ", err)
atwarn(debug.traceback())
end)
if not succ then
reload_saves()
else
return return1, return2, return3, return4
end
end
advtrains.modpath = minetest.get_modpath("advtrains")
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=""
if type(t)=="table" then
if t.x and t.y and t.z then
str=str..minetest.pos_to_string(t)
else
str=str..dump(t)
end
elseif type(t)=="boolean" then
if t then
str=str.."true"
else
str=str.."false"
end
else
str=str..t
end
str=str.." "
end
end
return str
end
atprint=function() end
atdebug=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
if minetest.settings:get_bool("advtrains_enable_debugging") then
atprint=function(t, ...)
local context=advtrains.atprint_context_tid
if not context then return end
local text=advtrains.print_concat_table({t, ...})
advtrains.drb_record(context, text)
end
atdebug=function(t, ...)
local text=advtrains.print_concat_table({t, ...})
minetest.log("action", "[advtrains]"..text)
minetest.chat_send_all("[advtrains]"..text)
end
dofile(advtrains.modpath.."/debugringbuffer.lua")
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}}
dofile(advtrains.modpath.."/trainlogic.lua")
dofile(advtrains.modpath.."/trainhud.lua")
dofile(advtrains.modpath.."/trackplacer.lua")
dofile(advtrains.modpath.."/tracks.lua")
dofile(advtrains.modpath.."/atc.lua")
dofile(advtrains.modpath.."/wagons.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.."/loading.lua")
if minetest.global_exists("digtron") then
dofile(advtrains.modpath.."/digtron.lua")
end
--load/save
advtrains.fpath=minetest.get_worldpath().."/advtrains"
function advtrains.avt_load()
local file, err = io.open(advtrains.fpath, "r")
if not file then
minetest.log("error", " Failed to read advtrains save data from file "..advtrains.fpath..": "..(err or "Unknown Error"))
else
local tbl = minetest.deserialize(file:read("*a"))
if type(tbl) == "table" then
if tbl.version then
--congrats, we have the new save format.
advtrains.trains = tbl.trains
advtrains.wagon_save = tbl.wagon_save
advtrains.player_to_train_mapping = tbl.ptmap or {}
advtrains.ndb.load_data(tbl.ndb)
advtrains.atc.load_data(tbl.atc)
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
file:close()
end
end
advtrains.avt_save = function(remove_players_from_wagons)
--atprint("saving")
--No more invalidating.
--Instead, remove path a.s.o from the saved table manually
-- update wagon saves
for _,wagon in pairs(minetest.luaentities) do
if wagon.is_wagon and wagon.initialized then
wagon:get_staticdata()
end
end
--cross out userdata
for w_id, data in pairs(advtrains.wagon_save) do
data.name=nil
data.object=nil
if data.driver then
data.driver_name=data.driver:get_player_name()
data.driver=nil
else
data.driver_name=nil
end
if data.discouple then
data.discouple.object:remove()
data.discouple=nil
end
if remove_players_from_wagons then
data.seatp={}
end
end
if remove_players_from_wagons then
advtrains.player_to_train_mapping={}
end
local tmp_trains={}
for id, train in pairs(advtrains.trains) do
--first, deep_copy the train
local v=advtrains.merge_tables(train)
--then invalidate
if v.index then
v.restore_add_index=v.index-math.floor(v.index+0.5)
end
v.path=nil
v.path_dist=nil
v.index=nil
v.end_index=nil
v.min_index_on_track=nil
v.max_index_on_track=nil
v.path_extent_min=nil
v.path_extent_max=nil
v.detector_old_index=nil
v.detector_old_end_index=nil
--then save it
tmp_trains[id]=v
end
--versions:
-- 1 - Initial new save format.
local save_tbl={
trains = tmp_trains,
wagon_save = advtrains.wagon_save,
ptmap = advtrains.player_to_train_mapping,
atc = advtrains.atc.save_data(),
ndb = advtrains.ndb.save_data(),
version = 1,
}
local datastr = minetest.serialize(save_tbl)
if not datastr then
minetest.log("error", " Failed to serialize advtrains save data!")
return
end
local file, err = io.open(advtrains.fpath, "w")
if err then
minetest.log("error", " Failed to write advtrains save data to file "..advtrains.fpath..": "..(err or "Unknown Error"))
return
end
file:write(datastr)
file:close()
end
--## MAIN LOOP ##--
--Calls all subsequent main tasks of both advtrains and atlatc
local init_load=false
local save_interval=20
local save_timer=save_interval
advtrains.mainloop_runcnt=0
minetest.register_globalstep(function(dtime_mt)
return advtrains.pcall(function()
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
--limit dtime: if trains move too far in one step, automation may cause stuck and wrongly braking trains
local dtime=dtime_mt
if dtime>0.2 then
atprint("Limiting dtime to 0.2!")
dtime=0.2
end
advtrains.mainloop_trainlogic(dtime)
if advtrains_itm_mainloop then
advtrains_itm_mainloop(dtime)
end
if atlatc then
atlatc.mainloop_stepcode(dtime)
atlatc.interrupt.mainloop(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=save_interval
atprintbm("saving", t)
end
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
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
advtrains.avt_save(remove_players_from_wagons) --saving advtrains. includes ndb at advtrains.ndb.save_data()
if atlatc then
atlatc.save()
end
atprint("[save_all]Saved advtrains save files")
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)
return advtrains.pcall(function()
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)
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)
return advtrains.pcall(function()
atwarn("Train routes will be recalculated now")
advtrains.save(false)
reload_saves()
end)
end,
})