diff options
Diffstat (limited to 'advtrains/advtrains_train_steam')
0 files changed, 0 insertions, 0 deletions
![]() |
index : advtrains.git | |
Advtrains mod for minetest. | orwell |
aboutsummaryrefslogtreecommitdiff |
--trainlogic.lua
--controls train entities stuff about connecting/disconnecting/colliding trains and other things
local print=function(t, ...) minetest.log("action", table.concat({t, ...}, " ")) minetest.chat_send_all(table.concat({t, ...}, " ")) end
local benchmark=false
--printbm=function(str, t) print("[advtrains]"..str.." "..((os.clock()-t)*1000).."ms") end
local bm={}
local bmlt=0
local bmsteps=0
local bmstepint=200
printbm=function(action, ta)
if not benchmark then return end
local t=(os.clock()-ta)*1000
if not bm[action] then
bm[action]=t
else
bm[action]=bm[action]+t
end
bmlt=bmlt+t
end
function endstep()
if not benchmark then return end
bmsteps=bmsteps-1
if bmsteps<=0 then
bmsteps=bmstepint
for key, value in pairs(bm) do
minetest.chat_send_all(key.." "..(value/bmstepint).." ms avg.")
end
minetest.chat_send_all("Total time consumed by all advtrains actions per step: "..(bmlt/bmstepint).." ms avg.")
bm={}
bmlt=0
end
end
advtrains.train_accel_force=2--per second and divided by number of wagons
advtrains.train_brake_force=3--per second, not divided by number of wagons
advtrains.train_emerg_force=10--for emergency brakes(when going off track)
advtrains.audit_interval=30
advtrains.all_traintypes={}
function advtrains.register_train_type(name, drives_on, max_speed)
advtrains.all_traintypes[name]={}
advtrains.all_traintypes[name].drives_on=drives_on
advtrains.all_traintypes[name].max_speed=max_speed or 10
end
advtrains.trains={}
advtrains.wagon_save={}
--load initially
advtrains.fpath=minetest.get_worldpath().."/advtrains"
local file, err = io.open(advtrains.fpath, "r")
if not file then
local er=err or "Unknown Error"
print("[advtrains]Failed loading advtrains save file "..er)
else
local tbl = minetest.deserialize(file:read("*a"))
if type(tbl) == "table" then
advtrains.trains=tbl
end
file:close()
end
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"
print("[advtrains]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
advtrains.save = function()
--print("[advtrains]saving")
advtrains.invalidate_all_paths()
local datastr = minetest.serialize(advtrains.trains)
if not datastr then
minetest.log("error", "[advtrains] Failed to serialize train data!")
return
end
local file, err = io.open(advtrains.fpath, "w")
if err then
return err
end
file:write(datastr)
file:close()
-- update wagon saves
for _,wagon in pairs(minetest.luaentities) do
if wagon.is_wagon and wagon.initialized then
advtrains.wagon_save[wagon.unique_id]=advtrains.merge_tables(wagon)--so, will only copy non_metatable elements
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
end
--print(dump(advtrains.wagon_save))
datastr = minetest.serialize(advtrains.wagon_save)
if not datastr then
minetest.log("error", "[advtrains] Failed to serialize train data!")
return
end
file, err = io.open(advtrains.fpath_ws, "w")
if err then
return err
end
file:write(datastr)
file:close()
advtrains.save_trackdb()
end
minetest.register_on_shutdown(advtrains.save)
advtrains.save_and_audit_timer=advtrains.audit_interval
minetest.register_globalstep(function(dtime)
advtrains.save_and_audit_timer=advtrains.save_and_audit_timer-dtime
if advtrains.save_and_audit_timer<=0 then
local t=os.clock()
--print("[advtrains] audit step")
--clean up orphaned trains
for k,v in pairs(advtrains.trains) do
--advtrains.update_trainpart_properties(k)
if #v.trainparts==0 then
advtrains.trains[k]=nil
end
end
--save
advtrains.save()
advtrains.save_and_audit_timer=advtrains.audit_interval
printbm("saving", t)
end
--regular train step
local t=os.clock()
for k,v in pairs(advtrains.trains) do
advtrains.train_step(k, v, dtime)
end
printbm("trainsteps", t)
endstep()
end)
function advtrains.train_step(id, train, dtime)
--TODO check for all vars to be present
if not train.velocity then
train.velocity=0
end
--very unimportant thing: check if couple is here
if train.couple_eid_front and (not minetest.luaentities[train.couple_eid_front] or not minetest.luaentities[train.couple_eid_front].is_couple) then train.couple_eid_front=nil end
if train.couple_eid_back and (not minetest.luaentities[train.couple_eid_back] or not minetest.luaentities[train.couple_eid_back].is_couple) then train.couple_eid_back=nil end
--skip certain things (esp. collision) when not moving
local train_moves=(train.velocity~=0)
--if not train.last_pos then advtrains.trains[id]=nil return end
if not advtrains.pathpredict(id, train) then
print("pathpredict failed(returned false)")
train.velocity=0
train.tarvelocity=0
return
end
local path=advtrains.get_or_create_path(id, train)
if not path then
train.velocity=0
train.tarvelocity=0
print("train has no path for whatever reason")
return
end
local train_end_index=advtrains.get_train_end_index(train)
--apply off-track handling:
local front_off_track=train.max_index_on_track and train.index>train.max_index_on_track
local back_off_track=train.min_index_on_track and train_end_index<train.min_index_on_track
if front_off_track and back_off_track then--allow movement in both directions
if train.tarvelocity>1 then train.tarvelocity=1 end
if train.tarvelocity<-1 then train.tarvelocity=-1 end
elseif front_off_track then--allow movement only backward
if train.tarvelocity>0 then train.tarvelocity=0 end
if train.tarvelocity<-1 then train.tarvelocity=-1 end
elseif back_off_track then--allow movement only forward
if train.tarvelocity>1 then train.tarvelocity=1 end
if train.tarvelocity<0 then train.tarvelocity=0 end
end
if train_moves then
--check for collisions by finding objects
--front
local search_radius=4
--coupling
local couple_outward=1
local posfront=advtrains.get_real_index_position(path, train.index+couple_outward)
local posback=advtrains.get_real_index_position(path, train_end_index-couple_outward)
for _,pos in ipairs({posfront, posback}) do
if pos then
local objrefs=minetest.get_objects_inside_radius(pos, search_radius)
for _,v in pairs(objrefs) do
local le=v:get_luaentity()
if le and le.is_wagon and le.initialized and le.train_id~=id then
advtrains.try_connect_trains(id, le.train_id)
end
end
end
end
--new train collisions (only search in the direction of the driving train)
local coll_search_radius=2
local coll_grace=0
local collpos
if train.velocity>0 then
collpos=advtrains.get_real_index_position(path, train.index-coll_grace)
elseif train.velocity<0 then
collpos=advtrains.get_real_index_position(path, train_end_index+coll_grace)
end
if collpos then
local objrefs=minetest.get_objects_inside_radius(collpos, coll_search_radius)
for _,v in pairs(objrefs) do
local le=v:get_luaentity()
if le and le.is_wagon and le.initialized and le.train_id~=id then
train.recently_collided_with_env=true
train.velocity=-0.5*train.velocity
train.tarvelocity=0
end
end
end
end
--check for any trainpart entities if they have been unloaded. do this only if train is near a player, to not spawn entities into unloaded areas
train.check_trainpartload=(train.check_trainpartload or 0)-dtime
local node_range=(math.max((minetest.setting_get("active_block_range") or 0),1)*16)
if train.check_trainpartload<=0 and posfront and posback then
--print(minetest.pos_to_string(posfront))
local should_check=false
for _,p in ipairs(minetest.get_connected_players()) do
should_check=should_check or ((vector.distance(posfront, p:getpos())<node_range) and (vector.distance(posback, p:getpos())<node_range))
end
if should_check then
--it is better to iterate luaentites only once
--print("check_trainpartload")
local found_uids={}
for _,wagon in pairs(minetest.luaentities) do
if wagon.is_wagon and wagon.initialized and wagon.train_id==id then
if found_uids[wagon.unique_id] then
--duplicate found, delete it
if wagon.object then wagon.object:remove() end
else
found_uids[wagon.unique_id]=true
end
end
end
--print("found_uids: "..dump(found_uids))
--now iterate trainparts and check. then cross them out to see if there are wagons over for any reason
for pit, w_id in ipairs(train.trainparts) do
if found_uids[w_id] then
--print(w_id.." still loaded")
elseif advtrains.wagon_save[w_id] then
--print(w_id.." not loaded, but save available")
--spawn a new and initialize it with the properties from wagon_save
local le=minetest.env:add_entity(posfront, advtrains.wagon_save[w_id].entity_name):get_luaentity()
for k,v in pairs(advtrains.wagon_save[w_id]) do
le[k]=v
end
advtrains.wagon_save[w_id].name=nil
advtrains.wagon_save[w_id].object=nil
else
print(w_id.." not loaded and no save available")
--what the hell...
table.remove(train.trainparts, pit)
end
end
end
train.check_trainpartload=10
end
--handle collided_with_env
if train.recently_collided_with_env then
train.tarvelocity=0
if not train_moves then
train.recently_collided_with_env=false--reset status when stopped
end
end
if train.locomotives_in_train==0 then
train.tarvelocity=0
end
--apply tarvel(but with physics in mind!)
if train.velocity~=train.tarvelocity then
local applydiff=0
local mass=#train.trainparts
local diff=math.abs(train.tarvelocity)-math.abs(train.velocity)
if diff>0 then--accelerating, force will be brought on only by locomotives.
--print("accelerating with default force")
applydiff=(math.min((advtrains.train_accel_force*train.locomotives_in_train*dtime)/mass, math.abs(diff)))
else--decelerating
if front_off_track or back_off_track or train.recently_collided_with_env then --every wagon has a brake, so not divided by mass.
--print("braking with emergency force")
applydiff=(math.min((advtrains.train_emerg_force*dtime), math.abs(diff)))
else
--print("braking with default force")
applydiff=(math.min((advtrains.train_brake_force*dtime), math.abs(diff)))
end
end
train.velocity=train.velocity+(applydiff*math.sign(train.tarvelocity-train.velocity))
end
--move
train.index=train.index and train.index+((train.velocity/(train.path_dist[math.floor(train.index)] or 1))*dtime) or 0
end
--the 'leader' concept has been overthrown, we won't rely on MT's "buggy object management"
--structure of train table:
--[[
trains={
[train_id]={
trainparts={
[n]=wagon_id
}
path={path}
velocity
tarvelocity
index
trainlen
path_inv_level
last_pos |
last_dir | for pathpredicting.
}
}
--a wagon itself has the following properties:
wagon={
unique_id
train_id
pos_in_train (is index difference, including train_span stuff)
pos_in_trainparts (is index in trainparts tabel of trains)
}
inherited by metatable:
wagon_proto={
wagon_span
}
]]
--returns new id
function advtrains.create_new_train_at(pos, pos_prev, traintype)
local newtrain_id=os.time()..os.clock()
while advtrains.trains[newtrain_id] do newtrain_id=os.time()..os.clock() end--ensure uniqueness(will be unneccessary)