diff --git a/advtrains.zip b/advtrains.zip index 02ab8b1..2144e28 100644 Binary files a/advtrains.zip and b/advtrains.zip differ diff --git a/advtrains/advtrains/atc.lua b/advtrains/advtrains/atc.lua index 3925ffd..ed631a3 100644 --- a/advtrains/advtrains/atc.lua +++ b/advtrains/advtrains/atc.lua @@ -92,49 +92,53 @@ advtrains.register_tracks("default", { return { after_place_node=apn_func, after_dig_node=function(pos) - advtrains.invalidate_all_paths() - advtrains.ndb.clear(pos) - local pts=minetest.pos_to_string(pos) - atc.controllers[pts]=nil + return advtrains.pcall(function() + advtrains.invalidate_all_paths() + advtrains.ndb.clear(pos) + local pts=minetest.pos_to_string(pos) + atc.controllers[pts]=nil + end) end, on_receive_fields = function(pos, formname, fields, player) - if advtrains.is_protected(pos, player:get_player_name()) then - minetest.record_protection_violation(pos, player:get_player_name()) - return - end - local meta=minetest.get_meta(pos) - if meta then - if not fields.save then - --maybe only the dropdown changed - if fields.mode then - meta:set_string("mode", idxtrans[fields.mode]) - if fields.mode=="digiline" then - meta:set_string("infotext", attrans("ATC controller, mode @1\nChannel: @2", fields.mode, meta:get_string("command")) ) - else - meta:set_string("infotext", attrans("ATC controller, mode @1\nCommand: @2", fields.mode, meta:get_string("command")) ) - end - meta:set_string("formspec", atc.get_atc_controller_formspec(pos, meta)) - end + return advtrains.pcall(function() + if advtrains.is_protected(pos, player:get_player_name()) then + minetest.record_protection_violation(pos, player:get_player_name()) return end - meta:set_string("mode", idxtrans[fields.mode]) - meta:set_string("command", fields.command) - meta:set_string("command_on", fields.command_on) - meta:set_string("channel", fields.channel) - if fields.mode=="digiline" then - meta:set_string("infotext", attrans("ATC controller, mode @1\nChannel: @2", fields.mode, meta:get_string("command")) ) - else - meta:set_string("infotext", attrans("ATC controller, mode @1\nCommand: @2", fields.mode, meta:get_string("command")) ) + local meta=minetest.get_meta(pos) + if meta then + if not fields.save then + --maybe only the dropdown changed + if fields.mode then + meta:set_string("mode", idxtrans[fields.mode]) + if fields.mode=="digiline" then + meta:set_string("infotext", attrans("ATC controller, mode @1\nChannel: @2", fields.mode, meta:get_string("command")) ) + else + meta:set_string("infotext", attrans("ATC controller, mode @1\nCommand: @2", fields.mode, meta:get_string("command")) ) + end + meta:set_string("formspec", atc.get_atc_controller_formspec(pos, meta)) + end + return + end + meta:set_string("mode", idxtrans[fields.mode]) + meta:set_string("command", fields.command) + meta:set_string("command_on", fields.command_on) + meta:set_string("channel", fields.channel) + if fields.mode=="digiline" then + meta:set_string("infotext", attrans("ATC controller, mode @1\nChannel: @2", fields.mode, meta:get_string("command")) ) + else + meta:set_string("infotext", attrans("ATC controller, mode @1\nCommand: @2", fields.mode, meta:get_string("command")) ) + end + meta:set_string("formspec", atc.get_atc_controller_formspec(pos, meta)) + + local pts=minetest.pos_to_string(pos) + local _, conn1=advtrains.get_rail_info_at(pos, advtrains.all_tracktypes) + atc.controllers[pts]={command=fields.command, arrowconn=conn1} + if advtrains.detector.on_node[pts] then + atc.send_command(pos) + end end - meta:set_string("formspec", atc.get_atc_controller_formspec(pos, meta)) - - local pts=minetest.pos_to_string(pos) - local _, conn1=advtrains.get_rail_info_at(pos, advtrains.all_tracktypes) - atc.controllers[pts]={command=fields.command, arrowconn=conn1} - if advtrains.detector.on_node[pts] then - atc.send_command(pos) - end - end + end) end, advtrains = { on_train_enter = function(pos, train_id) diff --git a/advtrains/advtrains/couple.lua b/advtrains/advtrains/couple.lua index eb1bc72..ac4dfce 100644 --- a/advtrains/advtrains/couple.lua +++ b/advtrains/advtrains/couple.lua @@ -31,52 +31,56 @@ minetest.register_entity("advtrains:discouple", { end, get_staticdata=function() return "DISCOUPLE" end, on_punch=function(self, player) - --only if player owns at least one wagon next to this - local own=player:get_player_name() - if self.wagon.owner and self.wagon.owner==own and not self.wagon.lock_couples then - local train=advtrains.trains[self.wagon.train_id] - local nextwgn_id=train.trainparts[self.wagon.pos_in_trainparts-1] - for aoi, le in pairs(minetest.luaentities) do - if le and le.is_wagon then - if le.unique_id==nextwgn_id then - if le.owner and le.owner~=own then - minetest.chat_send_player(own, attrans("You need to own at least one neighboring wagon to destroy this couple.")) - return + return advtrains.pcall(function() + --only if player owns at least one wagon next to this + local own=player:get_player_name() + if self.wagon.owner and self.wagon.owner==own and not self.wagon.lock_couples then + local train=advtrains.trains[self.wagon.train_id] + local nextwgn_id=train.trainparts[self.wagon.pos_in_trainparts-1] + for aoi, le in pairs(minetest.luaentities) do + if le and le.is_wagon then + if le.unique_id==nextwgn_id then + if le.owner and le.owner~=own then + minetest.chat_send_player(own, attrans("You need to own at least one neighboring wagon to destroy this couple.")) + return + end end end end + atprint("Discouple punched. Splitting train", self.wagon.train_id) + advtrains.split_train_at_wagon(self.wagon)--found in trainlogic.lua + self.object:remove() + elseif self.wagon.lock_couples then + minetest.chat_send_player(own, "Couples of one of the wagons are locked, can't discouple!") + else + minetest.chat_send_player(own, attrans("You need to own at least one neighboring wagon to destroy this couple.")) end - atprint("Discouple punched. Splitting train", self.wagon.train_id) - advtrains.split_train_at_wagon(self.wagon)--found in trainlogic.lua - self.object:remove() - elseif self.wagon.lock_couples then - minetest.chat_send_player(own, "Couples of one of the wagons are locked, can't discouple!") - else - minetest.chat_send_player(own, attrans("You need to own at least one neighboring wagon to destroy this couple.")) - end + end) end, on_step=function(self, dtime) - local t=os.clock() - if not self.wagon then - self.object:remove() - atprint("Discouple: no wagon, destroying") - return - end - --getyaw seems to be a reliable method to check if an object is loaded...if it returns nil, it is not. - if not self.wagon.object:getyaw() then - atprint("Discouple: wagon no longer loaded, destroying") - self.object:remove() - return - end - local velocityvec=self.wagon.object:getvelocity() - self.updatepct_timer=(self.updatepct_timer or 0)-dtime - if not self.old_velocity_vector or not vector.equals(velocityvec, self.old_velocity_vector) or self.updatepct_timer<=0 then--only send update packet if something changed - local flipsign=self.wagon.wagon_flipped and -1 or 1 - self.object:setpos(vector.add(self.wagon.object:getpos(), {y=0, x=-math.sin(self.wagon.object:getyaw())*self.wagon.wagon_span*flipsign, z=math.cos(self.wagon.object:getyaw())*self.wagon.wagon_span*flipsign})) - self.object:setvelocity(velocityvec) - self.updatepct_timer=2 - end - atprintbm("discouple_step", t) + return advtrains.pcall(function() + local t=os.clock() + if not self.wagon then + self.object:remove() + atprint("Discouple: no wagon, destroying") + return + end + --getyaw seems to be a reliable method to check if an object is loaded...if it returns nil, it is not. + if not self.wagon.object:getyaw() then + atprint("Discouple: wagon no longer loaded, destroying") + self.object:remove() + return + end + local velocityvec=self.wagon.object:getvelocity() + self.updatepct_timer=(self.updatepct_timer or 0)-dtime + if not self.old_velocity_vector or not vector.equals(velocityvec, self.old_velocity_vector) or self.updatepct_timer<=0 then--only send update packet if something changed + local flipsign=self.wagon.wagon_flipped and -1 or 1 + self.object:setpos(vector.add(self.wagon.object:getpos(), {y=0, x=-math.sin(self.wagon.object:getyaw())*self.wagon.wagon_span*flipsign, z=math.cos(self.wagon.object:getyaw())*self.wagon.wagon_span*flipsign})) + self.object:setvelocity(velocityvec) + self.updatepct_timer=2 + end + atprintbm("discouple_step", t) + end) end, }) @@ -98,74 +102,79 @@ minetest.register_entity("advtrains:couple", { initial_sprite_basepos = {x=0, y=0}, is_couple=true, - on_activate=function(self, staticdata) - if staticdata=="COUPLE" then - --couple entities have no right to exist further... - atprint("Couple loaded from staticdata, destroying") - self.object:remove() - return - end + on_activate=function(self, staticdata) + return advtrains.pcall(function() + if staticdata=="COUPLE" then + --couple entities have no right to exist further... + atprint("Couple loaded from staticdata, destroying") + self.object:remove() + return + end + end) end, get_staticdata=function(self) return "COUPLE" end, on_rightclick=function(self, clicker) - if not self.train_id_1 or not self.train_id_2 then return end - - local id1, id2=self.train_id_1, self.train_id_2 - - if self.train1_is_backpos and not self.train2_is_backpos then - advtrains.do_connect_trains(id1, id2, clicker) - --case 2 (second train is front) - elseif self.train2_is_backpos and not self.train1_is_backpos then - advtrains.do_connect_trains(id2, id1, clicker) - --case 3 - elseif self.train1_is_backpos and self.train2_is_backpos then - advtrains.invert_train(id2) - advtrains.do_connect_trains(id1, id2, clicker) - --case 4 - elseif not self.train1_is_backpos and not self.train2_is_backpos then - advtrains.invert_train(id1) - advtrains.do_connect_trains(id1, id2, clicker) - end - atprint("Coupled trains", id1, id2) - self.object:remove() + return advtrains.pcall(function() + if not self.train_id_1 or not self.train_id_2 then return end + + local id1, id2=self.train_id_1, self.train_id_2 + if self.train1_is_backpos and not self.train2_is_backpos then + advtrains.do_connect_trains(id1, id2, clicker) + --case 2 (second train is front) + elseif self.train2_is_backpos and not self.train1_is_backpos then + advtrains.do_connect_trains(id2, id1, clicker) + --case 3 + elseif self.train1_is_backpos and self.train2_is_backpos then + advtrains.invert_train(id2) + advtrains.do_connect_trains(id1, id2, clicker) + --case 4 + elseif not self.train1_is_backpos and not self.train2_is_backpos then + advtrains.invert_train(id1) + advtrains.do_connect_trains(id1, id2, clicker) + end + atprint("Coupled trains", id1, id2) + self.object:remove() + end) end, on_step=function(self, dtime) - local t=os.clock() - if not self.train_id_1 or not self.train_id_2 then atprint("Couple: train ids not set!") self.object:remove() return end - local train1=advtrains.trains[self.train_id_1] - local train2=advtrains.trains[self.train_id_2] - if not train1 or not train2 then - atprint("Couple: trains missing, destroying") - self.object:remove() - return - end - if not train1.path or not train2.path or not train1.index or not train2.index or not train1.end_index or not train2.end_index then - atprint("Couple: paths or end_index missing. Might happen when paths got cleared") - return - end - - local tp1 - if not self.train1_is_backpos then - tp1=advtrains.get_real_index_position(train1.path, train1.index) - else - tp1=advtrains.get_real_index_position(train1.path, train1.end_index) - end - local tp2 - if not self.train2_is_backpos then - tp2=advtrains.get_real_index_position(train2.path, train2.index) - else - tp2=advtrains.get_real_index_position(train2.path, train2.end_index) - end - if not tp1 or not tp2 or not (vector.distance(tp1,tp2)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 - + return advtrains.pcall(function() + --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. @@ -247,6 +274,8 @@ function advtrains.load() advtrains_itm_init() end init_load=true + no_action=false + atprint("[load_all]Loaded advtrains save files") end --## MAIN SAVE ROUTINE ## @@ -261,5 +290,6 @@ function advtrains.save() if atlatc then atlatc.save() end + atprint("[save_all]Saved advtrains save files") end minetest.register_on_shutdown(advtrains.save) diff --git a/advtrains/advtrains/nocrash.lua b/advtrains/advtrains/nocrash.lua deleted file mode 100644 index e69de29..0000000 diff --git a/advtrains/advtrains/nodedb.lua b/advtrains/advtrains/nodedb.lua index ddd1a67..55adea8 100644 --- a/advtrains/advtrains/nodedb.lua +++ b/advtrains/advtrains/nodedb.lua @@ -203,37 +203,41 @@ minetest.register_abm({ nodenames = {"group:save_in_nodedb"}, run_at_every_load = true, action = function(pos, node) - local cid=ndbget(pos.x, pos.y, pos.z) - if cid then - --if in database, detect changes and apply. - local nodeid = ndb_nodeids[u14b(cid)] - local param2 = l2b(cid) - if not nodeid then - --something went wrong - atprint("nodedb: lbm nid not found", pos, "with nid", u14b(cid), "param2", param2, "cid is", cid) - ndb.update(pos, node) - else - if (nodeid~=node.name or param2~=node.param2) then - atprint("nodedb: lbm replaced", pos, "with nodeid", nodeid, "param2", param2, "cid is", cid) - minetest.swap_node(pos, {name=nodeid, param2 = param2}) - local ndef=minetest.registered_nodes[nodeid] - if ndef and ndef.on_updated_from_nodedb then - ndef.on_updated_from_nodedb(pos, node) + return advtrains.pcall(function() + local cid=ndbget(pos.x, pos.y, pos.z) + if cid then + --if in database, detect changes and apply. + local nodeid = ndb_nodeids[u14b(cid)] + local param2 = l2b(cid) + if not nodeid then + --something went wrong + atprint("nodedb: lbm nid not found", pos, "with nid", u14b(cid), "param2", param2, "cid is", cid) + ndb.update(pos, node) + else + if (nodeid~=node.name or param2~=node.param2) then + atprint("nodedb: lbm replaced", pos, "with nodeid", nodeid, "param2", param2, "cid is", cid) + minetest.swap_node(pos, {name=nodeid, param2 = param2}) + local ndef=minetest.registered_nodes[nodeid] + if ndef and ndef.on_updated_from_nodedb then + ndef.on_updated_from_nodedb(pos, node) + end end end + else + --if not in database, take it. + atprint("nodedb: ", pos, "not in database") + ndb.update(pos, node) end - else - --if not in database, take it. - atprint("nodedb: ", pos, "not in database") - ndb.update(pos, node) - end + end) end, interval=10, chance=1, }) minetest.register_on_dignode(function(pos, oldnode, digger) - ndb.clear(pos) + return advtrains.pcall(function() + ndb.clear(pos) + end) end) function ndb.get_nodes() diff --git a/advtrains/advtrains/trackplacer.lua b/advtrains/advtrains/trackplacer.lua index e039800..105b77e 100644 --- a/advtrains/advtrains/trackplacer.lua +++ b/advtrains/advtrains/trackplacer.lua @@ -187,26 +187,28 @@ function tp.register_track_placer(nnprefix, imgprefix, dispname) wield_image = imgprefix.."_placer.png", groups={}, on_place = function(itemstack, placer, pointed_thing) - local name = placer:get_player_name() - if not name then - return itemstack - end - if pointed_thing.type=="node" then - local pos=pointed_thing.above - local upos=vector.subtract(pointed_thing.above, {x=0, y=1, z=0}) - if advtrains.is_protected(pos,name) then - minetest.record_protection_violation(pos, name) - return itemstack + return advtrains.pcall(function() + local name = placer:get_player_name() + if not name then + return itemstack end - if minetest.registered_nodes[minetest.get_node(pos).name] and minetest.registered_nodes[minetest.get_node(pos).name].buildable_to - and minetest.registered_nodes[minetest.get_node(upos).name] and minetest.registered_nodes[minetest.get_node(upos).name].walkable then - tp.placetrack(pos, nnprefix, placer, itemstack, pointed_thing) - if not minetest.setting_getbool("creative_mode") then - itemstack:take_item() + if pointed_thing.type=="node" then + local pos=pointed_thing.above + local upos=vector.subtract(pointed_thing.above, {x=0, y=1, z=0}) + if advtrains.is_protected(pos,name) then + minetest.record_protection_violation(pos, name) + return itemstack + end + if minetest.registered_nodes[minetest.get_node(pos).name] and minetest.registered_nodes[minetest.get_node(pos).name].buildable_to + and minetest.registered_nodes[minetest.get_node(upos).name] and minetest.registered_nodes[minetest.get_node(upos).name].walkable then + tp.placetrack(pos, nnprefix, placer, itemstack, pointed_thing) + if not minetest.setting_getbool("creative_mode") then + itemstack:take_item() + end end end - end - return itemstack + return itemstack + end) end, }) end @@ -220,78 +222,82 @@ minetest.register_craftitem("advtrains:trackworker",{ wield_image = "advtrains_trackworker.png", stack_max = 1, on_place = function(itemstack, placer, pointed_thing) - local name = placer:get_player_name() - if not name then - return - end - if pointed_thing.type=="node" then - local pos=pointed_thing.under - if advtrains.is_protected(pos, name) then - minetest.record_protection_violation(pos, name) + return advtrains.pcall(function() + local name = placer:get_player_name() + if not name then return end - local node=minetest.get_node(pos) + if pointed_thing.type=="node" then + local pos=pointed_thing.under + if advtrains.is_protected(pos, name) then + minetest.record_protection_violation(pos, name) + return + end + local node=minetest.get_node(pos) - --if not advtrains.is_track_and_drives_on(minetest.get_node(pos).name, advtrains.all_tracktypes) then return end - if advtrains.get_train_at_pos(pos) then return end + --if not advtrains.is_track_and_drives_on(minetest.get_node(pos).name, advtrains.all_tracktypes) then return end + if advtrains.get_train_at_pos(pos) then return end - local nnprefix, suffix, rotation=string.match(node.name, "^(.+)_([^_]+)(_[^_]+)$") - --atprint(node.name.."\npattern recognizes:"..nodeprefix.." / "..railtype.." / "..rotation) - if not tp.tracks[nnprefix] or not tp.tracks[nnprefix].twrotate[suffix] then - nnprefix, suffix=string.match(node.name, "^(.+)_([^_]+)$") - rotation = "" + local nnprefix, suffix, rotation=string.match(node.name, "^(.+)_([^_]+)(_[^_]+)$") + --atprint(node.name.."\npattern recognizes:"..nodeprefix.." / "..railtype.." / "..rotation) if not tp.tracks[nnprefix] or not tp.tracks[nnprefix].twrotate[suffix] then - minetest.chat_send_player(placer:get_player_name(), attrans("This node can't be rotated using the trackworker!")) - return - end - end - local modext=tp.tracks[nnprefix].twrotate[suffix] - - if rotation==modext[#modext] then --increase param2 - advtrains.ndb.swap_node(pos, {name=nnprefix.."_"..suffix..modext[1], param2=(node.param2+1)%4}) - return - else - local modpos - for k,v in pairs(modext) do if v==rotation then modpos=k end end - if not modpos then + nnprefix, suffix=string.match(node.name, "^(.+)_([^_]+)$") + rotation = "" + if not tp.tracks[nnprefix] or not tp.tracks[nnprefix].twrotate[suffix] then minetest.chat_send_player(placer:get_player_name(), attrans("This node can't be rotated using the trackworker!")) - return + return + end + end + local modext=tp.tracks[nnprefix].twrotate[suffix] + + if rotation==modext[#modext] then --increase param2 + advtrains.ndb.swap_node(pos, {name=nnprefix.."_"..suffix..modext[1], param2=(node.param2+1)%4}) + return + else + local modpos + for k,v in pairs(modext) do if v==rotation then modpos=k end end + if not modpos then + minetest.chat_send_player(placer:get_player_name(), attrans("This node can't be rotated using the trackworker!")) + return + end + advtrains.ndb.swap_node(pos, {name=nnprefix.."_"..suffix..modext[modpos+1], param2=node.param2}) end - advtrains.ndb.swap_node(pos, {name=nnprefix.."_"..suffix..modext[modpos+1], param2=node.param2}) end - end + end) end, on_use=function(itemstack, user, pointed_thing) - local name = user:get_player_name() - if not name then - return - end - if pointed_thing.type=="node" then - local pos=pointed_thing.under - local node=minetest.get_node(pos) - if advtrains.is_protected(pos, name) then - minetest.record_protection_violation(pos, name) - return + return advtrains.pcall(function() + local name = user:get_player_name() + if not name then + return end - - --if not advtrains.is_track_and_drives_on(minetest.get_node(pos).name, advtrains.all_tracktypes) then return end - if advtrains.get_train_at_pos(pos) then return end - local nnprefix, suffix, rotation=string.match(node.name, "^(.+)_([^_]+)(_[^_]+)$") - --atprint(node.name.."\npattern recognizes:"..nodeprefix.." / "..railtype.." / "..rotation) - if not tp.tracks[nnprefix] or not tp.tracks[nnprefix].twcycle[suffix] then - nnprefix, suffix=string.match(node.name, "^(.+)_([^_]+)$") - rotation = "" - if not tp.tracks[nnprefix] or not tp.tracks[nnprefix].twcycle[suffix] then - minetest.chat_send_player(user:get_player_name(), attrans("This node can't be changed using the trackworker!")) - return - end - end - local nextsuffix=tp.tracks[nnprefix].twcycle[suffix] - advtrains.ndb.swap_node(pos, {name=nnprefix.."_"..nextsuffix..rotation, param2=node.param2}) - - else - atprint(name, dump(tp.tracks)) - end + if pointed_thing.type=="node" then + local pos=pointed_thing.under + local node=minetest.get_node(pos) + if advtrains.is_protected(pos, name) then + minetest.record_protection_violation(pos, name) + return + end + + --if not advtrains.is_track_and_drives_on(minetest.get_node(pos).name, advtrains.all_tracktypes) then return end + if advtrains.get_train_at_pos(pos) then return end + local nnprefix, suffix, rotation=string.match(node.name, "^(.+)_([^_]+)(_[^_]+)$") + --atprint(node.name.."\npattern recognizes:"..nodeprefix.." / "..railtype.." / "..rotation) + if not tp.tracks[nnprefix] or not tp.tracks[nnprefix].twcycle[suffix] then + nnprefix, suffix=string.match(node.name, "^(.+)_([^_]+)$") + rotation = "" + if not tp.tracks[nnprefix] or not tp.tracks[nnprefix].twcycle[suffix] then + minetest.chat_send_player(user:get_player_name(), attrans("This node can't be changed using the trackworker!")) + return + end + end + local nextsuffix=tp.tracks[nnprefix].twcycle[suffix] + advtrains.ndb.swap_node(pos, {name=nnprefix.."_"..nextsuffix..rotation, param2=node.param2}) + + else + atprint(name, dump(tp.tracks)) + end + end) end, }) diff --git a/advtrains/advtrains/trainlogic.lua b/advtrains/advtrains/trainlogic.lua index 98a0e2b..7dad80c 100644 --- a/advtrains/advtrains/trainlogic.lua +++ b/advtrains/advtrains/trainlogic.lua @@ -37,8 +37,7 @@ advtrains.train_roll_force=0.5--per second, not divided by number of wagons, acc advtrains.train_emerg_force=10--for emergency brakes(when going off track) -advtrains.mainloop_trainlogic(function(dtime) - +advtrains.mainloop_trainlogic=function(dtime) --build a table of all players indexed by pts. used by damage and door system. advtrains.playersbypts={} for _, player in pairs(minetest.get_connected_players()) do @@ -67,39 +66,43 @@ advtrains.mainloop_trainlogic(function(dtime) end minetest.register_on_joinplayer(function(player) - local pname=player:get_player_name() - local id=advtrains.player_to_train_mapping[pname] - if id then - local train=advtrains.trains[id] - if not train then advtrains.player_to_train_mapping[pname]=nil return end - --set the player to the train position. - --minetest will emerge the area and load the objects, which then will call reattach_all(). - --because player is in mapping, it will not be subject to dying. - player:setpos(train.last_pos_prev) - --independent of this, cause all wagons of the train which are loaded to reattach their players - --needed because already loaded wagons won't call reattach_all() - for _,wagon in pairs(minetest.luaentities) do - if wagon.is_wagon and wagon.initialized and wagon.train_id==id then - wagon:reattach_all() + return advtrains.pcall(function() + local pname=player:get_player_name() + local id=advtrains.player_to_train_mapping[pname] + if id then + local train=advtrains.trains[id] + if not train then advtrains.player_to_train_mapping[pname]=nil return end + --set the player to the train position. + --minetest will emerge the area and load the objects, which then will call reattach_all(). + --because player is in mapping, it will not be subject to dying. + player:setpos(train.last_pos_prev) + --independent of this, cause all wagons of the train which are loaded to reattach their players + --needed because already loaded wagons won't call reattach_all() + for _,wagon in pairs(minetest.luaentities) do + if wagon.is_wagon and wagon.initialized and wagon.train_id==id then + wagon:reattach_all() + end end end - end + end) end) minetest.register_on_dieplayer(function(player) - local pname=player:get_player_name() - local id=advtrains.player_to_train_mapping[pname] - if id then - local train=advtrains.trains[id] - if not train then advtrains.player_to_train_mapping[pname]=nil return end - for _,wagon in pairs(minetest.luaentities) do - if wagon.is_wagon and wagon.initialized and wagon.train_id==id then - --when player dies, detach him from the train - --call get_off_plr on every wagon since we don't know which one he's on. - wagon:get_off_plr(pname) + return advtrains.pcall(function() + local pname=player:get_player_name() + local id=advtrains.player_to_train_mapping[pname] + if id then + local train=advtrains.trains[id] + if not train then advtrains.player_to_train_mapping[pname]=nil return end + for _,wagon in pairs(minetest.luaentities) do + if wagon.is_wagon and wagon.initialized and wagon.train_id==id then + --when player dies, detach him from the train + --call get_off_plr on every wagon since we don't know which one he's on. + wagon:get_off_plr(pname) + end end end - end + end) end) --[[ train step structure: @@ -797,6 +800,10 @@ end function advtrains.do_connect_trains(first_id, second_id, player) local first, second=advtrains.trains[first_id], advtrains.trains[second_id] + if not first or not second or not first.index or not second.index or not first.end_index or not second.end_index then + return false + end + if first.couple_lock_back or second.couple_lock_front then -- trains are ordered correctly! if player then @@ -817,6 +824,7 @@ function advtrains.do_connect_trains(first_id, second_id, player) advtrains.update_trainpart_properties(first_id) advtrains.trains[first_id].velocity=new_velocity advtrains.trains[first_id].tarvelocity=0 + return true end function advtrains.invert_train(train_id) diff --git a/advtrains/advtrains/wagons.lua b/advtrains/advtrains/wagons.lua index 15a0ec7..3325879 100644 --- a/advtrains/advtrains/wagons.lua +++ b/advtrains/advtrains/wagons.lua @@ -50,19 +50,21 @@ function wagon:on_activate(sd_uid, dtime_s) end function wagon:get_staticdata() - if not self:ensure_init() then return end - atprint("[wagon "..((self.unique_id and self.unique_id~="" and self.unique_id) or "no-id").."]: saving to wagon_save") - --serialize inventory, if it has one - if self.has_inventory then - local inv=minetest.get_inventory({type="detached", name="advtrains_wgn_"..self.unique_id}) - self.ser_inv=advtrains.serialize_inventory(inv) - end - --save to table before being unloaded - advtrains.wagon_save[self.unique_id]=advtrains.merge_tables(self) - advtrains.wagon_save[self.unique_id].entity_name=self.name - advtrains.wagon_save[self.unique_id].name=nil - advtrains.wagon_save[self.unique_id].object=nil - return self.unique_id + return advtrains.pcall(function() + if not self:ensure_init() then return end + atprint("[wagon "..((self.unique_id and self.unique_id~="" and self.unique_id) or "no-id").."]: saving to wagon_save") + --serialize inventory, if it has one + if self.has_inventory then + local inv=minetest.get_inventory({type="detached", name="advtrains_wgn_"..self.unique_id}) + self.ser_inv=advtrains.serialize_inventory(inv) + end + --save to table before being unloaded + advtrains.wagon_save[self.unique_id]=advtrains.merge_tables(self) + advtrains.wagon_save[self.unique_id].entity_name=self.name + advtrains.wagon_save[self.unique_id].name=nil + advtrains.wagon_save[self.unique_id].object=nil + return self.unique_id + end) end --returns: uid of wagon function wagon:init_new_instance(train_id, properties) @@ -147,36 +149,38 @@ end -- Remove the wagon function wagon:on_punch(puncher, time_from_last_punch, tool_capabilities, direction) - if not self:ensure_init() then return end - if not puncher or not puncher:is_player() then - return - end - if self.owner and puncher:get_player_name()~=self.owner and (not minetest.check_player_privs(puncher, {train_remove = true })) then - minetest.chat_send_player(puncher:get_player_name(), attrans("This wagon is owned by @1, you can't destroy it.", self.owner)); - return - end - - if minetest.setting_getbool("creative_mode") then - if not self:destroy() then return end - - local inv = puncher:get_inventory() - if not inv:contains_item("main", self.name) then - inv:add_item("main", self.name) - end - else - local pc=puncher:get_player_control() - if not pc.sneak then - minetest.chat_send_player(puncher:get_player_name(), attrans("Warning: If you destroy this wagon, you only get some steel back! If you are sure, hold Sneak and left-click the wagon.")) + return advtrains.pcall(function() + if not self:ensure_init() then return end + if not puncher or not puncher:is_player() then return end - - if not self:destroy() then return end - - local inv = puncher:get_inventory() - for _,item in ipairs(self.drops or {self.name}) do - inv:add_item("main", item) + if self.owner and puncher:get_player_name()~=self.owner and (not minetest.check_player_privs(puncher, {train_remove = true })) then + minetest.chat_send_player(puncher:get_player_name(), attrans("This wagon is owned by @1, you can't destroy it.", self.owner)); + return end - end + + if minetest.setting_getbool("creative_mode") then + if not self:destroy() then return end + + local inv = puncher:get_inventory() + if not inv:contains_item("main", self.name) then + inv:add_item("main", self.name) + end + else + local pc=puncher:get_player_control() + if not pc.sneak then + minetest.chat_send_player(puncher:get_player_name(), attrans("Warning: If you destroy this wagon, you only get some steel back! If you are sure, hold Sneak and left-click the wagon.")) + return + end + + if not self:destroy() then return end + + local inv = puncher:get_inventory() + for _,item in ipairs(self.drops or {self.name}) do + inv:add_item("main", item) + end + end + end) end function wagon:destroy() --some rules: @@ -211,260 +215,262 @@ end function wagon:on_step(dtime) - if not self:ensure_init() then return end - - local t=os.clock() - local pos = self.object:getpos() - - if not pos then - atprint("["..self.unique_id.."][fatal] missing position (object:getpos() returned nil)") - return - end - - self.entity_name=self.name - - --is my train still here - if not self.train_id or not self:train() then - atprint("[wagon "..self.unique_id.."] missing train_id, destroying") - self.object:remove() - return - end - if not self.seatp then - self.seatp={} - end - if not self.seatpc then - self.seatpc={} - end - - --Legacy: remove infotext since it does not work this way anyways - self.infotext=nil - - --custom on_step function - if self.custom_on_step then - self:custom_on_step(self, dtime) - end - - --driver control - for seatno, seat in ipairs(self.seats) do - local driver=self.seatp[seatno] and minetest.get_player_by_name(self.seatp[seatno]) - local has_driverstand=seat.driving_ctrl_access and self.seatp[seatno] and minetest.check_player_privs(self.seatp[seatno], {train_operator=true}) - if has_driverstand and driver then - advtrains.update_driver_hud(driver:get_player_name(), self:train(), self.wagon_flipped) - elseif driver then - --only show the inside text - local inside=self:train().text_inside or "" - advtrains.set_trainhud(driver:get_player_name(), inside) + return advtrains.pcall(function() + if not self:ensure_init() then return end + + local t=os.clock() + local pos = self.object:getpos() + + if not pos then + atprint("["..self.unique_id.."][fatal] missing position (object:getpos() returned nil)") + return end - if driver and driver:get_player_control_bits()~=self.seatpc[seatno] then - local pc=driver:get_player_control() - self.seatpc[seatno]=driver:get_player_control_bits() - - if has_driverstand then - --regular driver stand controls - advtrains.on_control_change(pc, self:train(), self.wagon_flipped) - else - -- If on a passenger seat and doors are open, get off when W or D pressed. - local pass = self.seatp[seatno] and minetest.get_player_by_name(self.seatp[seatno]) - if pass and self:train().door_open~=0 then - local pc=pass:get_player_control() - if pc.up or pc.down then - self:get_off(seatno) - end - end - end - if pc.aux1 and pc.sneak then - self:get_off(seatno) - end + + self.entity_name=self.name + + --is my train still here + if not self.train_id or not self:train() then + atprint("[wagon "..self.unique_id.."] missing train_id, destroying") + self.object:remove() + return end - end - - --check infotext - local outside=self:train().text_outside or "" - if self.object:get_properties().infotext~=outside then - self.object:set_properties({infotext=outside}) - end - - local gp=self:train() - local fct=self.wagon_flipped and -1 or 1 - --door animation - if self.doors then - if (self.door_anim_timer or 0)<=0 then - local dstate = (gp.door_open or 0) * fct - if dstate ~= self.door_state then - local at - --meaning of the train.door_open field: - -- -1: left doors (rel. to train orientation) - -- 0: closed - -- 1: right doors - --this code produces the following behavior: - -- if changed from 0 to +-1, play open anim. if changed from +-1 to 0, play close. - -- if changed from +-1 to -+1, first close and set 0, then it will detect state change again and run open. - if self.door_state == 0 then - at=self.doors.open[dstate] - self.object:set_animation(at.frames, at.speed or 15, at.blend or 0, false) - self.door_state = dstate + if not self.seatp then + self.seatp={} + end + if not self.seatpc then + self.seatpc={} + end + + --Legacy: remove infotext since it does not work this way anyways + self.infotext=nil + + --custom on_step function + if self.custom_on_step then + self:custom_on_step(self, dtime) + end + + --driver control + for seatno, seat in ipairs(self.seats) do + local driver=self.seatp[seatno] and minetest.get_player_by_name(self.seatp[seatno]) + local has_driverstand=seat.driving_ctrl_access and self.seatp[seatno] and minetest.check_player_privs(self.seatp[seatno], {train_operator=true}) + if has_driverstand and driver then + advtrains.update_driver_hud(driver:get_player_name(), self:train(), self.wagon_flipped) + elseif driver then + --only show the inside text + local inside=self:train().text_inside or "" + advtrains.set_trainhud(driver:get_player_name(), inside) + end + if driver and driver:get_player_control_bits()~=self.seatpc[seatno] then + local pc=driver:get_player_control() + self.seatpc[seatno]=driver:get_player_control_bits() + + if has_driverstand then + --regular driver stand controls + advtrains.on_control_change(pc, self:train(), self.wagon_flipped) else - at=self.doors.close[self.door_state or 1]--in case it has not been set yet - self.object:set_animation(at.frames, at.speed or 15, at.blend or 0, false) - self.door_state = 0 + -- If on a passenger seat and doors are open, get off when W or D pressed. + local pass = self.seatp[seatno] and minetest.get_player_by_name(self.seatp[seatno]) + if pass and self:train().door_open~=0 then + local pc=pass:get_player_control() + if pc.up or pc.down then + self:get_off(seatno) + end + end end - self.door_anim_timer = at.time - end - else - self.door_anim_timer = (self.door_anim_timer or 0) - dtime - end - end - - --DisCouple - if self.pos_in_trainparts and self.pos_in_trainparts>1 then - if gp.velocity==0 and not self.lock_couples then - if not self.discouple or not self.discouple.object:getyaw() then - local object=minetest.add_entity(pos, "advtrains:discouple") - if object then - local le=object:get_luaentity() - le.wagon=self - --box is hidden when attached, so unuseful. - --object:set_attach(self.object, "", {x=0, y=0, z=self.wagon_span*10}, {x=0, y=0, z=0}) - self.discouple=le - else - atprint("Couldn't spawn DisCouple") + if pc.aux1 and pc.sneak then + self:get_off(seatno) end end - else - if self.discouple and self.discouple.object:getyaw() then - self.discouple.object:remove() - end end - end - --for path to be available. if not, skip step - if not gp.path then - self.object:setvelocity({x=0, y=0, z=0}) - return - end - if not self.pos_in_train then - --why ever. but better continue next step... - advtrains.update_trainpart_properties(self.train_id) - return - end - - local index=advtrains.get_real_path_index(self:train(), self.pos_in_train) - --atprint("trainindex "..gp.index.." wagonindex "..index) - - --automatic get_on - --needs to know index and path - if self.door_entry and gp.door_open and gp.door_open~=0 and gp.velocity==0 then - --using the mapping created by the trainlogic globalstep - for i, ino in ipairs(self.door_entry) do - --fct is the flipstate flag from door animation above - local aci = index + ino*fct - local ix1=gp.path[math.floor(aci)] - local ix2=gp.path[math.floor(aci+1)] - -- the two wanted positions are ix1 and ix2 + (2nd-1st rotated by 90deg) - -- (x z) rotated by 90deg is (-z x) (http://stackoverflow.com/a/4780141) - local add = { x = (ix2.z-ix1.z)*gp.door_open, y = 0, z = (ix1.x-ix2.x)*gp.door_open } - local pts1=vector.round(vector.add(ix1, add)) - local pts2=vector.round(vector.add(ix2, add)) - if minetest.get_item_group(minetest.get_node(pts1).name, "platform")>0 then - local ckpts={ - pts1, - pts2, - vector.add(pts1, {x=0, y=1, z=0}), - vector.add(pts2, {x=0, y=1, z=0}), - } - for _,ckpos in ipairs(ckpts) do - local cpp=minetest.pos_to_string(ckpos) - if advtrains.playersbypts[cpp] then - self:on_rightclick(advtrains.playersbypts[cpp]) + + --check infotext + local outside=self:train().text_outside or "" + if self.object:get_properties().infotext~=outside then + self.object:set_properties({infotext=outside}) + end + + local gp=self:train() + local fct=self.wagon_flipped and -1 or 1 + --door animation + if self.doors then + if (self.door_anim_timer or 0)<=0 then + local dstate = (gp.door_open or 0) * fct + if dstate ~= self.door_state then + local at + --meaning of the train.door_open field: + -- -1: left doors (rel. to train orientation) + -- 0: closed + -- 1: right doors + --this code produces the following behavior: + -- if changed from 0 to +-1, play open anim. if changed from +-1 to 0, play close. + -- if changed from +-1 to -+1, first close and set 0, then it will detect state change again and run open. + if self.door_state == 0 then + at=self.doors.open[dstate] + self.object:set_animation(at.frames, at.speed or 15, at.blend or 0, false) + self.door_state = dstate + else + at=self.doors.close[self.door_state or 1]--in case it has not been set yet + self.object:set_animation(at.frames, at.speed or 15, at.blend or 0, false) + self.door_state = 0 end + self.door_anim_timer = at.time end - end - end - end - - --position recalculation - local first_pos=gp.path[math.floor(index)] - local second_pos=gp.path[math.floor(index)+1] - if not first_pos or not second_pos then - --atprint(" object "..self.unique_id.." path end reached!") - self.object:setvelocity({x=0,y=0,z=0}) - return - end - - --checking for environment collisions(a 3x3 cube around the center) - if not gp.recently_collided_with_env then - local collides=false - for x=-1,1 do - for y=0,2 do - for z=-1,1 do - local node=minetest.get_node_or_nil(vector.add(first_pos, {x=x, y=y, z=z})) - if (advtrains.train_collides(node)) then - collides=true - end - end - end - end - if collides then - if self.collision_count and self.collision_count>10 then - --enable collision mercy to get trains stuck in walls out of walls - --actually do nothing except limiting the velocity to 1 - gp.velocity=math.min(gp.velocity, 1) - gp.tarvelocity=math.min(gp.tarvelocity, 1) else - gp.recently_collided_with_env=true - gp.velocity=2*gp.velocity - gp.movedir=-gp.movedir - gp.tarvelocity=0 - self.collision_count=(self.collision_count or 0)+1 + self.door_anim_timer = (self.door_anim_timer or 0) - dtime end + end + + --DisCouple + if self.pos_in_trainparts and self.pos_in_trainparts>1 then + if gp.velocity==0 and not self.lock_couples then + if not self.discouple or not self.discouple.object:getyaw() then + local object=minetest.add_entity(pos, "advtrains:discouple") + if object then + local le=object:get_luaentity() + le.wagon=self + --box is hidden when attached, so unuseful. + --object:set_attach(self.object, "", {x=0, y=0, z=self.wagon_span*10}, {x=0, y=0, z=0}) + self.discouple=le + else + atprint("Couldn't spawn DisCouple") + end + end + else + if self.discouple and self.discouple.object:getyaw() then + self.discouple.object:remove() + end + end + end + --for path to be available. if not, skip step + if not gp.path then + self.object:setvelocity({x=0, y=0, z=0}) + return + end + if not self.pos_in_train then + --why ever. but better continue next step... + advtrains.update_trainpart_properties(self.train_id) + return + end + + local index=advtrains.get_real_path_index(self:train(), self.pos_in_train) + --atprint("trainindex "..gp.index.." wagonindex "..index) + + --automatic get_on + --needs to know index and path + if self.door_entry and gp.door_open and gp.door_open~=0 and gp.velocity==0 then + --using the mapping created by the trainlogic globalstep + for i, ino in ipairs(self.door_entry) do + --fct is the flipstate flag from door animation above + local aci = index + ino*fct + local ix1=gp.path[math.floor(aci)] + local ix2=gp.path[math.floor(aci+1)] + -- the two wanted positions are ix1 and ix2 + (2nd-1st rotated by 90deg) + -- (x z) rotated by 90deg is (-z x) (http://stackoverflow.com/a/4780141) + local add = { x = (ix2.z-ix1.z)*gp.door_open, y = 0, z = (ix1.x-ix2.x)*gp.door_open } + local pts1=vector.round(vector.add(ix1, add)) + local pts2=vector.round(vector.add(ix2, add)) + if minetest.get_item_group(minetest.get_node(pts1).name, "platform")>0 then + local ckpts={ + pts1, + pts2, + vector.add(pts1, {x=0, y=1, z=0}), + vector.add(pts2, {x=0, y=1, z=0}), + } + for _,ckpos in ipairs(ckpts) do + local cpp=minetest.pos_to_string(ckpos) + if advtrains.playersbypts[cpp] then + self:on_rightclick(advtrains.playersbypts[cpp]) + end + end + end + end + end + + --position recalculation + local first_pos=gp.path[math.floor(index)] + local second_pos=gp.path[math.floor(index)+1] + if not first_pos or not second_pos then + --atprint(" object "..self.unique_id.." path end reached!") + self.object:setvelocity({x=0,y=0,z=0}) + return + end + + --checking for environment collisions(a 3x3 cube around the center) + if not gp.recently_collided_with_env then + local collides=false + for x=-1,1 do + for y=0,2 do + for z=-1,1 do + local node=minetest.get_node_or_nil(vector.add(first_pos, {x=x, y=y, z=z})) + if (advtrains.train_collides(node)) then + collides=true + end + end + end + end + if collides then + if self.collision_count and self.collision_count>10 then + --enable collision mercy to get trains stuck in walls out of walls + --actually do nothing except limiting the velocity to 1 + gp.velocity=math.min(gp.velocity, 1) + gp.tarvelocity=math.min(gp.tarvelocity, 1) + else + gp.recently_collided_with_env=true + gp.velocity=2*gp.velocity + gp.movedir=-gp.movedir + gp.tarvelocity=0 + self.collision_count=(self.collision_count or 0)+1 + end + else + self.collision_count=nil + end + end + + --FIX: use index of the wagon, not of the train. + local velocity=(gp.velocity*gp.movedir)/(gp.path_dist[math.floor(index)] or 1) + local acceleration=(gp.last_accel or 0)/(gp.path_dist[math.floor(index)] or 1) + local factor=index-math.floor(index) + local actual_pos={x=first_pos.x-(first_pos.x-second_pos.x)*factor, y=first_pos.y-(first_pos.y-second_pos.y)*factor, z=first_pos.z-(first_pos.z-second_pos.z)*factor,} + local velocityvec={x=(first_pos.x-second_pos.x)*velocity*-1, z=(first_pos.z-second_pos.z)*velocity*-1, y=(first_pos.y-second_pos.y)*velocity*-1} + local accelerationvec={x=(first_pos.x-second_pos.x)*acceleration*-1, z=(first_pos.z-second_pos.z)*acceleration*-1, y=(first_pos.y-second_pos.y)*acceleration*-1} + + --some additional positions to determine orientation + local aposfwd=gp.path[math.floor(index+2)] + local aposbwd=gp.path[math.floor(index-1)] + + local yaw + if aposfwd and aposbwd then + yaw=advtrains.get_wagon_yaw(aposfwd, second_pos, first_pos, aposbwd, factor)+math.pi--TODO remove when cleaning up else - self.collision_count=nil + yaw=math.atan2((first_pos.x-second_pos.x), (second_pos.z-first_pos.z)) end - end - - --FIX: use index of the wagon, not of the train. - local velocity=(gp.velocity*gp.movedir)/(gp.path_dist[math.floor(index)] or 1) - local acceleration=(gp.last_accel or 0)/(gp.path_dist[math.floor(index)] or 1) - local factor=index-math.floor(index) - local actual_pos={x=first_pos.x-(first_pos.x-second_pos.x)*factor, y=first_pos.y-(first_pos.y-second_pos.y)*factor, z=first_pos.z-(first_pos.z-second_pos.z)*factor,} - local velocityvec={x=(first_pos.x-second_pos.x)*velocity*-1, z=(first_pos.z-second_pos.z)*velocity*-1, y=(first_pos.y-second_pos.y)*velocity*-1} - local accelerationvec={x=(first_pos.x-second_pos.x)*acceleration*-1, z=(first_pos.z-second_pos.z)*acceleration*-1, y=(first_pos.y-second_pos.y)*acceleration*-1} - - --some additional positions to determine orientation - local aposfwd=gp.path[math.floor(index+2)] - local aposbwd=gp.path[math.floor(index-1)] - - local yaw - if aposfwd and aposbwd then - yaw=advtrains.get_wagon_yaw(aposfwd, second_pos, first_pos, aposbwd, factor)+math.pi--TODO remove when cleaning up - else - yaw=math.atan2((first_pos.x-second_pos.x), (second_pos.z-first_pos.z)) - end - if self.wagon_flipped then - yaw=yaw+math.pi - end - - self.updatepct_timer=(self.updatepct_timer or 0)-dtime - if not self.old_velocity_vector - or not vector.equals(velocityvec, self.old_velocity_vector) - or not self.old_acceleration_vector - or not vector.equals(accelerationvec, self.old_acceleration_vector) - or self.old_yaw~=yaw - or self.updatepct_timer<=0 then--only send update packet if something changed - self.object:setpos(actual_pos) - self.object:setvelocity(velocityvec) - self.object:setacceleration(accelerationvec) - self.object:setyaw(yaw) - self.updatepct_timer=2 - if self.update_animation then - self:update_animation(gp.velocity) + if self.wagon_flipped then + yaw=yaw+math.pi end - end - - - self.old_velocity_vector=velocityvec - self.old_acceleration_vector=accelerationvec - self.old_yaw=yaw - atprintbm("wagon step", t) + + self.updatepct_timer=(self.updatepct_timer or 0)-dtime + if not self.old_velocity_vector + or not vector.equals(velocityvec, self.old_velocity_vector) + or not self.old_acceleration_vector + or not vector.equals(accelerationvec, self.old_acceleration_vector) + or self.old_yaw~=yaw + or self.updatepct_timer<=0 then--only send update packet if something changed + self.object:setpos(actual_pos) + self.object:setvelocity(velocityvec) + self.object:setacceleration(accelerationvec) + self.object:setyaw(yaw) + self.updatepct_timer=2 + if self.update_animation then + self:update_animation(gp.velocity) + end + end + + + self.old_velocity_vector=velocityvec + self.old_acceleration_vector=accelerationvec + self.old_yaw=yaw + atprintbm("wagon step", t) + end) end function advtrains.get_real_path_index(train, pit) @@ -485,85 +491,87 @@ function advtrains.get_real_path_index(train, pit) end function wagon:on_rightclick(clicker) - if not self:ensure_init() then return end - if not clicker or not clicker:is_player() then - return - end - if clicker:get_player_control().aux1 then - --advtrains.dumppath(self:train().path) - --minetest.chat_send_all("at index "..(self:train().index or "nil")) - --advtrains.invert_train(self.train_id) - atprint(dump(self)) - return - end - local pname=clicker:get_player_name() - local no=self:get_seatno(pname) - if no then - if self.seat_groups then - local poss={} - local sgr=self.seats[no].group - for _,access in ipairs(self.seat_groups[sgr].access_to) do - if self:check_seat_group_access(pname, access) then - poss[#poss+1]={name=self.seat_groups[access].name, key="sgr_"..access} + return advtrains.pcall(function() + if not self:ensure_init() then return end + if not clicker or not clicker:is_player() then + return + end + if clicker:get_player_control().aux1 then + --advtrains.dumppath(self:train().path) + --minetest.chat_send_all("at index "..(self:train().index or "nil")) + --advtrains.invert_train(self.train_id) + atprint(dump(self)) + return + end + local pname=clicker:get_player_name() + local no=self:get_seatno(pname) + if no then + if self.seat_groups then + local poss={} + local sgr=self.seats[no].group + for _,access in ipairs(self.seat_groups[sgr].access_to) do + if self:check_seat_group_access(pname, access) then + poss[#poss+1]={name=self.seat_groups[access].name, key="sgr_"..access} + end end - end - if self.has_inventory and self.get_inventory_formspec then - poss[#poss+1]={name=attrans("Show Inventory"), key="inv"} - end - if self.owner==pname then - poss[#poss+1]={name=attrans("Wagon properties"), key="prop"} - end - if not self.seat_groups[sgr].require_doors_open or self:train().door_open~=0 then - poss[#poss+1]={name=attrans("Get off"), key="off"} - else - if clicker:get_player_control().sneak then - poss[#poss+1]={name=attrans("Get off (forced)"), key="off"} + if self.has_inventory and self.get_inventory_formspec then + poss[#poss+1]={name=attrans("Show Inventory"), key="inv"} + end + if self.owner==pname then + poss[#poss+1]={name=attrans("Wagon properties"), key="prop"} + end + if not self.seat_groups[sgr].require_doors_open or self:train().door_open~=0 then + poss[#poss+1]={name=attrans("Get off"), key="off"} else - poss[#poss+1]={name=attrans("(Doors closed)"), key="dcwarn"} + if clicker:get_player_control().sneak then + poss[#poss+1]={name=attrans("Get off (forced)"), key="off"} + else + poss[#poss+1]={name=attrans("(Doors closed)"), key="dcwarn"} + end + end + if #poss==0 then + --can't do anything. + elseif #poss==1 then + self:seating_from_key_helper(pname, {[poss[1].key]=true}, no) + else + local form = "size[5,"..1+(#poss).."]" + for pos,ent in ipairs(poss) do + form = form .. "button_exit[0.5,"..(pos-0.5)..";4,1;"..ent.key..";"..ent.name.."]" + end + minetest.show_formspec(pname, "advtrains_seating_"..self.unique_id, form) end - end - if #poss==0 then - --can't do anything. - elseif #poss==1 then - self:seating_from_key_helper(pname, {[poss[1].key]=true}, no) else - local form = "size[5,"..1+(#poss).."]" - for pos,ent in ipairs(poss) do - form = form .. "button_exit[0.5,"..(pos-0.5)..";4,1;"..ent.key..";"..ent.name.."]" - end - minetest.show_formspec(pname, "advtrains_seating_"..self.unique_id, form) + self:get_off(no) end else - self:get_off(no) - end - else - --do not attach if already on a train - if advtrains.player_to_train_mapping[pname] then return end - if self.seat_groups then - if #self.seats==0 then - if self.has_inventory and self.get_inventory_formspec then - minetest.show_formspec(pname, "advtrains_inv_"..self.unique_id, self:get_inventory_formspec(pname)) + --do not attach if already on a train + if advtrains.player_to_train_mapping[pname] then return end + if self.seat_groups then + if #self.seats==0 then + if self.has_inventory and self.get_inventory_formspec then + minetest.show_formspec(pname, "advtrains_inv_"..self.unique_id, self:get_inventory_formspec(pname)) + end + return end - return - end - - local doors_open = self:train().door_open~=0 or clicker:get_player_control().sneak - for _,sgr in ipairs(self.assign_to_seat_group) do - if self:check_seat_group_access(pname, sgr) then - for seatid, seatdef in ipairs(self.seats) do - if seatdef.group==sgr and not self.seatp[seatid] and (not self.seat_groups[sgr].require_doors_open or doors_open) then - self:get_on(clicker, seatid) - return + + local doors_open = self:train().door_open~=0 or clicker:get_player_control().sneak + for _,sgr in ipairs(self.assign_to_seat_group) do + if self:check_seat_group_access(pname, sgr) then + for seatid, seatdef in ipairs(self.seats) do + if seatdef.group==sgr and not self.seatp[seatid] and (not self.seat_groups[sgr].require_doors_open or doors_open) then + self:get_on(clicker, seatid) + return + end end end end + minetest.chat_send_player(pname, attrans("Can't get on: wagon full or doors closed!")) + minetest.chat_send_player(pname, attrans("Use Sneak+rightclick to bypass closed doors!")) + else + self:show_get_on_form(pname) end - minetest.chat_send_player(pname, attrans("Can't get on: wagon full or doors closed!")) - minetest.chat_send_player(pname, attrans("Use Sneak+rightclick to bypass closed doors!")) - else - self:show_get_on_form(pname) end - end + end) end function wagon:get_on(clicker, seatno) @@ -708,77 +716,79 @@ function wagon:show_wagon_properties(pname) minetest.show_formspec(pname, "advtrains_prop_"..self.unique_id, form) end minetest.register_on_player_receive_fields(function(player, formname, fields) - local uid=string.match(formname, "^advtrains_geton_(.+)$") - if uid then - for _,wagon in pairs(minetest.luaentities) do - if wagon.is_wagon and wagon.initialized and wagon.unique_id==uid then - if fields.inv then - if wagon.has_inventory and wagon.get_inventory_formspec then - minetest.show_formspec(player:get_player_name(), "advtrains_inv_"..uid, wagon:get_inventory_formspec(player:get_player_name())) - end - elseif fields.seat then - local val=minetest.explode_textlist_event(fields.seat) - if val and val.type~="INV" and not wagon.seatp[player:get_player_name()] then - --get on - wagon:get_on(player, val.index) - --will work with the new close_formspec functionality. close exactly this formspec. - minetest.show_formspec(player:get_player_name(), formname, "") + return advtrains.pcall(function() + local uid=string.match(formname, "^advtrains_geton_(.+)$") + if uid then + for _,wagon in pairs(minetest.luaentities) do + if wagon.is_wagon and wagon.initialized and wagon.unique_id==uid then + if fields.inv then + if wagon.has_inventory and wagon.get_inventory_formspec then + minetest.show_formspec(player:get_player_name(), "advtrains_inv_"..uid, wagon:get_inventory_formspec(player:get_player_name())) + end + elseif fields.seat then + local val=minetest.explode_textlist_event(fields.seat) + if val and val.type~="INV" and not wagon.seatp[player:get_player_name()] then + --get on + wagon:get_on(player, val.index) + --will work with the new close_formspec functionality. close exactly this formspec. + minetest.show_formspec(player:get_player_name(), formname, "") + end end end end end - end - uid=string.match(formname, "^advtrains_seating_(.+)$") - if uid then - for _,wagon in pairs(minetest.luaentities) do - if wagon.is_wagon and wagon.initialized and wagon.unique_id==uid then - local pname=player:get_player_name() - local no=wagon:get_seatno(pname) - if no then - if wagon.seat_groups then - wagon:seating_from_key_helper(pname, fields, no) + uid=string.match(formname, "^advtrains_seating_(.+)$") + if uid then + for _,wagon in pairs(minetest.luaentities) do + if wagon.is_wagon and wagon.initialized and wagon.unique_id==uid then + local pname=player:get_player_name() + local no=wagon:get_seatno(pname) + if no then + if wagon.seat_groups then + wagon:seating_from_key_helper(pname, fields, no) + end end end end end - end - uid=string.match(formname, "^advtrains_prop_(.+)$") - if uid then - for _,wagon in pairs(minetest.luaentities) do - if wagon.is_wagon and wagon.initialized and wagon.unique_id==uid then - local pname=player:get_player_name() - if pname~=wagon.owner then - return true - end - if fields.save or not fields.quit then - for sgr,sgrdef in pairs(wagon.seat_groups) do - if fields["sgr_"..sgr] then - local fcont = fields["sgr_"..sgr] - wagon.seat_access[sgr] = fcont~="" and fcont or nil + uid=string.match(formname, "^advtrains_prop_(.+)$") + if uid then + for _,wagon in pairs(minetest.luaentities) do + if wagon.is_wagon and wagon.initialized and wagon.unique_id==uid then + local pname=player:get_player_name() + if pname~=wagon.owner then + return true + end + if fields.save or not fields.quit then + for sgr,sgrdef in pairs(wagon.seat_groups) do + if fields["sgr_"..sgr] then + local fcont = fields["sgr_"..sgr] + wagon.seat_access[sgr] = fcont~="" and fcont or nil + end end - end - if fields.lock_couples then - wagon.lock_couples = fields.lock_couples == "true" - end - if fields.text_outside then - if fields.text_outside~="" then - wagon:train().text_outside=fields.text_outside - else - wagon:train().text_outside=nil + if fields.lock_couples then + wagon.lock_couples = fields.lock_couples == "true" end - end - if fields.text_inside then - if fields.text_inside~="" then - wagon:train().text_inside=fields.text_inside - else - wagon:train().text_inside=nil + if fields.text_outside then + if fields.text_outside~="" then + wagon:train().text_outside=fields.text_outside + else + wagon:train().text_outside=nil + end end + if fields.text_inside then + if fields.text_inside~="" then + wagon:train().text_inside=fields.text_inside + else + wagon:train().text_inside=nil + end + end + end - end end end - end + end) end) function wagon:seating_from_key_helper(pname, fields, no) local sgr=self.seats[no].group @@ -841,41 +851,43 @@ function advtrains.register_wagon(sysname, prototype, desc, inv_img) stack_max = 1, on_place = function(itemstack, placer, pointed_thing) - if not pointed_thing.type == "node" then - return - end - + return advtrains.pcall(function() + if not pointed_thing.type == "node" then + return + end + - local node=minetest.get_node_or_nil(pointed_thing.under) - if not node then atprint("[advtrains]Ignore at placer position") return itemstack end - local nodename=node.name - if(not advtrains.is_track_and_drives_on(nodename, prototype.drives_on)) then - atprint("no track here, not placing.") + local node=minetest.get_node_or_nil(pointed_thing.under) + if not node then atprint("[advtrains]Ignore at placer position") return itemstack end + local nodename=node.name + if(not advtrains.is_track_and_drives_on(nodename, prototype.drives_on)) then + atprint("no track here, not placing.") + return itemstack + end + if not minetest.check_player_privs(placer, {train_place = true }) and minetest.is_protected(pointed_thing.under, placer:get_player_name()) then + minetest.record_protection_violation(pointed_thing.under, placer:get_player_name()) + return + end + local conn1=advtrains.get_track_connections(node.name, node.param2) + local id=advtrains.create_new_train_at(pointed_thing.under, advtrains.dirCoordSet(pointed_thing.under, conn1)) + + local ob=minetest.add_entity(pointed_thing.under, "advtrains:"..sysname) + if not ob then + atprint("couldn't add_entity, aborting") + end + local le=ob:get_luaentity() + + le.owner=placer:get_player_name() + + local wagon_uid=le:init_new_instance(id, {}) + + advtrains.add_wagon_to_train(le, id) + if not minetest.setting_getbool("creative_mode") then + itemstack:take_item() + end return itemstack - end - if not minetest.check_player_privs(placer, {train_place = true }) and minetest.is_protected(pointed_thing.under, placer:get_player_name()) then - minetest.record_protection_violation(pointed_thing.under, placer:get_player_name()) - return - end - local conn1=advtrains.get_track_connections(node.name, node.param2) - local id=advtrains.create_new_train_at(pointed_thing.under, advtrains.dirCoordSet(pointed_thing.under, conn1)) - - local ob=minetest.add_entity(pointed_thing.under, "advtrains:"..sysname) - if not ob then - atprint("couldn't add_entity, aborting") - end - local le=ob:get_luaentity() - - le.owner=placer:get_player_name() - - local wagon_uid=le:init_new_instance(id, {}) - - advtrains.add_wagon_to_train(le, id) - if not minetest.setting_getbool("creative_mode") then - itemstack:take_item() - end - return itemstack - + + end) end, }) end diff --git a/advtrains/advtrains_itrainmap/init.lua b/advtrains/advtrains_itrainmap/init.lua index 85a3709..756f1da 100644 --- a/advtrains/advtrains_itrainmap/init.lua +++ b/advtrains/advtrains_itrainmap/init.lua @@ -136,7 +136,7 @@ function advtrains_itm_mainloop(dtime) end timer=2 end -end) +end minetest.register_on_player_receive_fields(function(player, formname, fields) if formname=="itrainmap" and fields.quit then itm_pdata[player:get_player_name()]=nil diff --git a/advtrains/advtrains_luaautomation/init.lua b/advtrains/advtrains_luaautomation/init.lua index b24bc01..0257aef 100644 --- a/advtrains/advtrains_luaautomation/init.lua +++ b/advtrains/advtrains_luaautomation/init.lua @@ -62,10 +62,10 @@ function atlatc.load() end file:close() end + -- run init code of all environments + atlatc.run_initcode() end --- run init code of all environments -atlatc.run_initcode() atlatc.save = function() --versions: @@ -107,4 +107,4 @@ function atlatc.mainloop_stepcode(dtime) timer=0 atlatc.run_stepcode() end -end) +end diff --git a/advtrains/advtrains_luaautomation/interrupt.lua b/advtrains/advtrains_luaautomation/interrupt.lua index fa340b8..718b8c7 100644 --- a/advtrains/advtrains_luaautomation/interrupt.lua +++ b/advtrains/advtrains_luaautomation/interrupt.lua @@ -22,26 +22,24 @@ function iq.add(t, pos, evtdata) end function iq.mainloop(dtime) - if run then - timer=timer + math.min(dtime, 0.2) - for i=1,#queue do - local qe=queue[i] - if not qe then - table.remove(queue, i) - i=i-1 - elseif timer>qe.t then - local pos, evtdata=queue[i].p, queue[i].e - local node=advtrains.ndb.get_node(pos) - local ndef=minetest.registered_nodes[node.name] - if ndef and ndef.luaautomation and ndef.luaautomation.fire_event then - ndef.luaautomation.fire_event(pos, evtdata) - end - table.remove(queue, i) - i=i-1 + timer=timer + math.min(dtime, 0.2) + for i=1,#queue do + local qe=queue[i] + if not qe then + table.remove(queue, i) + i=i-1 + elseif timer>qe.t then + local pos, evtdata=queue[i].p, queue[i].e + local node=advtrains.ndb.get_node(pos) + local ndef=minetest.registered_nodes[node.name] + if ndef and ndef.luaautomation and ndef.luaautomation.fire_event then + ndef.luaautomation.fire_event(pos, evtdata) end + table.remove(queue, i) + i=i-1 end end -end) +end