From 8b576357ef1d2346e9af112e115ac92a5f4d222d Mon Sep 17 00:00:00 2001 From: orwell96 Date: Wed, 14 Mar 2018 20:57:07 +0100 Subject: Rework the privilege system completely See privilege_guide.txt for information --- advtrains/init.lua | 3 +- advtrains/protection.lua | 148 +++++++++++++++++++++++++++++++++++++++++++++ advtrains/settingtypes.txt | 5 ++ advtrains/signals.lua | 10 +-- advtrains/tracks.lua | 3 +- advtrains/wagons.lua | 117 +++++++++++------------------------ privilege_guide.txt | 33 ++++++++++ 7 files changed, 230 insertions(+), 89 deletions(-) create mode 100644 advtrains/protection.lua create mode 100644 privilege_guide.txt diff --git a/advtrains/init.lua b/advtrains/init.lua index 38252ae..2ef2879 100644 --- a/advtrains/init.lua +++ b/advtrains/init.lua @@ -151,6 +151,7 @@ dofile(advtrains.modpath.."/trackplacer.lua") dofile(advtrains.modpath.."/tracks.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") @@ -272,7 +273,7 @@ advtrains.avt_save = function(remove_players_from_wagons) "last_pos", "last_pos_prev", "movedir", "velocity", "tarvelocity", "trainparts", "savedpos_off_track_index_offset", "recently_collided_with_env", "atc_brake_target", "atc_wait_finish", "atc_command", "atc_delay", "door_open", - "text_outside", "text_inside", "couple_lck_front", "couple_lck_back" + "text_outside", "text_inside", "couple_lck_front", "couple_lck_back", "line" }) --then invalidate if train.index then diff --git a/advtrains/protection.lua b/advtrains/protection.lua new file mode 100644 index 0000000..73b725f --- /dev/null +++ b/advtrains/protection.lua @@ -0,0 +1,148 @@ +-- advtrains +-- protection.lua: privileges and rail protection, and some helpers + + +-- Privileges to control TRAIN DRIVING/COUPLING +minetest.register_privilege("train_operator", { + description = "Without this privilege, a player can't do anything about trains, neither place or remove them nor drive or couple them (but he can build tracks if he has track_builder)", + give_to_singleplayer= true, +}); + +minetest.register_privilege("train_admin", { + description = "Player may drive, place or remove any trains from/to anywhere, regardless of owner, whitelist or protection", + give_to_singleplayer= true, +}); + +-- Privileges to control TRACK BUILDING +minetest.register_privilege("track_builder", { + description = "Player can place and/or dig rails not protected from him. If he also has protection_bypass, he can place/dig any rails", + give_to_singleplayer= true, +}); + +-- Privileges to control OPERATING TURNOUTS/SIGNALS +minetest.register_privilege("railway_operator", { + description = "Player can operate turnouts and signals not protected from him. If he also has protection_bypass, he can operate any turnouts/signals", + give_to_singleplayer= true, +}); + +-- there is a configuration option "allow_build_only_owner". If this is active, a player having track_builder can only build rails and operate signals/turnouts in an area explicitly belonging to him +-- (checked using a dummy player called "*dummy*" (which is not an allowed player name)) + +--[[ +Protection/privilege concept: +Tracks: + Protected 1 node all around a rail and 4 nodes upward (maybe make this dynamically determined by the rail...) + if track_builder privilege: + if not protected from* player: + if allow_build_only_owner: + if unprotected: + deny + else: + allow + deny +Wagons in general: + Players can only place/destroy wagons if they have train_operator +Wagon driving controls: + The former seat_access tables are unnecessary, instead there is a whitelist for the driving stands + on player trying to access a driver stand: + if is owner or is on whitelist: + allow + else: + deny +Wagon coupling: + Derived from the privileges for driving stands. The whitelist is shared (and also settable on non-driverstand wagons) + for each of the two bordering wagons: + if is owner or is on whitelist: + allow + +*"protected from" means the player is not allowed to do things, while "protected by" means that the player is (one of) the owner(s) of this area + +]]-- + +local boo = minetest.settings:get_bool("advtrains_allow_build_to_owner") + + +local nocheck +-- Check if the node we are about to check is in the range of a track that is protected from a player +--WARN: true means here that the action is forbidden! +function advtrains.check_track_protection(pos, pname) + if nocheck or pname=="" then + return false + end + nocheck=true --prevent recursive calls, never check this again if we're already in + local r, vr = 1, 3 + local nodes = minetest.find_nodes_in_area( + {x = pos.x - r, y = pos.y - vr, z = pos.z - r}, + {x = pos.x + r, y = pos.y, z = pos.z + r}, + {"group:advtrains_track"}) + for _,npos in ipairs(nodes) do + if not minetest.check_player_privs(pname, {track_builder = true}) then + if boo and not minetest.is_protected(npos, pname) and minetest.is_protected(npos, "*dummy*") then + nocheck = false + return false + else + minetest.chat_send_player(pname, "You are not allowed to dig or place nodes near tracks (missing track_builder privilege)") + minetest.log("action", pname.." tried to dig/place nodes near the track at "..minetest.pos_to_string(npos).." but does not have track_builder") + nocheck = false + return true + end + end + if not minetest.check_player_privs(pname, {protection_bypass = true}) then + if minetest.is_protected(npos, pname) then + nocheck = false + minetest.record_protection_violation(pos, pname) + return true + end + end + end + nocheck=false + return false +end + +local old_is_protected = minetest.is_protected +minetest.is_protected = function(pos, pname) + if advtrains.check_track_protection(pos, pname) then + return true + end + return old_is_protected(pos, pname) +end + +--WARN: true means here that the action is allowed! +function advtrains.check_driving_couple_protection(pname, owner, whitelist) + if minetest.check_player_privs(pname, {train_admin = true}) then + return true + end + if not minetest.check_player_privs(pname, {train_operator = true}) then + return false + end + if not owner or owner == pname then + return true + end + if whitelist and string.find(" "..whitelist.." ", " "..pname.." ", nil, true) then + return true + end + return false +end +function advtrains.check_turnout_signal_protection(pos, pname) + nocheck=true + if not minetest.check_player_privs(pname, {railway_operator = true}) then + if boo and not minetest.is_protected(pos, pname) and minetest.is_protected(pos, "*dummy*") then + nocheck=false + return true + else + minetest.chat_send_player(pname, "You are not allowed to operate turnouts and signals (missing railway_operator privilege)") + minetest.log("action", pname.." tried to operate turnout/signal at "..minetest.pos_to_string(pos).." but does not have railway_operator") + nocheck=false + return false + end + end + if not minetest.check_player_privs(pname, {protection_bypass = true}) then + if minetest.is_protected(pos, pname) then + minetest.record_protection_violation(pos, pname) + nocheck=false + return false + end + end + nocheck=false + return true +end diff --git a/advtrains/settingtypes.txt b/advtrains/settingtypes.txt index d0c27ec..3b4863d 100644 --- a/advtrains/settingtypes.txt +++ b/advtrains/settingtypes.txt @@ -10,3 +10,8 @@ advtrains_enable_debugging (Enable debugging) bool false # Logs are saved in the world directory as advtrains.log # This setting is useful for multiplayer servers advtrains_enable_logging (Enable logging) bool false +# If this is active, any player can do the following things inside (and only inside) an area that is explicitly protected by him +# (checked using a dummy player called "*dummy*" (which is not an allowed player name)): +# - build tracks and near tracks without the track_builder privilege +# - operate turnouts and signals without the railway_operator privilege +advtrains_allow_build_to_owner (Allow building/operating to privilegeless area owner) bool false diff --git a/advtrains/signals.lua b/advtrains/signals.lua index b01314e..53145f5 100644 --- a/advtrains/signals.lua +++ b/advtrains/signals.lua @@ -41,7 +41,7 @@ for r,f in pairs({on={as="off", ls="green", als="red"}, off={as="on", ls="red", end }}, on_rightclick=function(pos, node, player) - if minetest.check_player_privs(player:get_player_name(), {train_operator=true}) then + if advtrains.check_turnout_signal_protection(pos, player:get_player_name()) then advtrains.ndb.swap_node(pos, {name = "advtrains:retrosignal_"..f.as..rotation, param2 = node.param2}, true) end end, @@ -84,7 +84,7 @@ for r,f in pairs({on={as="off", ls="green", als="red"}, off={as="on", ls="red", end, }, on_rightclick=function(pos, node, player) - if minetest.check_player_privs(player:get_player_name(), {train_operator=true}) then + if advtrains.check_turnout_signal_protection(pos, player:get_player_name()) then advtrains.ndb.swap_node(pos, {name = "advtrains:signal_"..f.as..rotation, param2 = node.param2}, true) end end, @@ -133,7 +133,7 @@ for r,f in pairs({on={as="off", ls="green", als="red"}, off={as="on", ls="red", end, }, on_rightclick=function(pos, node, player) - if minetest.check_player_privs(player:get_player_name(), {train_operator=true}) then + if advtrains.check_turnout_signal_protection(pos, player:get_player_name()) then advtrains.ndb.swap_node(pos, {name = "advtrains:signal_wall_"..loc.."_"..f.as, param2 = node.param2}, true) end end, @@ -179,7 +179,7 @@ minetest.register_node("advtrains:across_off", { end, }, on_rightclick=function(pos, node, player) - if minetest.check_player_privs(player:get_player_name(), {train_operator=true}) then + if advtrains.check_turnout_signal_protection(pos, player:get_player_name()) then advtrains.ndb.swap_node(pos, {name = "advtrains:across_on", param2 = node.param2}, true) end end, @@ -220,7 +220,7 @@ minetest.register_node("advtrains:across_on", { end, }, on_rightclick=function(pos, node, player) - if minetest.check_player_privs(player:get_player_name(), {train_operator=true}) then + if advtrains.check_turnout_signal_protection(pos, player:get_player_name()) then advtrains.ndb.swap_node(pos, {name = "advtrains:across_off", param2 = node.param2}, true) end end, diff --git a/advtrains/tracks.lua b/advtrains/tracks.lua index 4b2b17c..f31fef6 100644 --- a/advtrains/tracks.lua +++ b/advtrains/tracks.lua @@ -269,6 +269,7 @@ function advtrains.register_tracks(tracktype, def, preset) groups = { attached_node=1, + advtrains_track=1, ["advtrains_track_"..tracktype]=1, save_in_at_nodedb=1, dig_immediate=2, @@ -309,7 +310,7 @@ function advtrains.register_tracks(tracktype, def, preset) end end ndef.on_rightclick = function(pos, node, player) - if minetest.check_player_privs(player:get_player_name(), {train_operator=true}) then + if advtrains.check_turnout_signal_protection(pos, player:get_player_name()) then switchfunc(pos, node) advtrains.log("Switch", player:get_player_name(), pos) end diff --git a/advtrains/wagons.lua b/advtrains/wagons.lua index 2dce41b..d9b467c 100644 --- a/advtrains/wagons.lua +++ b/advtrains/wagons.lua @@ -1,19 +1,5 @@ --atan2 counts angles clockwise, minetest does counterclockwise -minetest.register_privilege("train_place", { - description = "Player can place trains on tracks not owned by player", - give_to_singleplayer= false, -}); -minetest.register_privilege("train_remove", { - description = "Player can remove trains not owned by player", - give_to_singleplayer= false, -}); -minetest.register_privilege("train_operator", { - description = "Player may operate trains and switch signals. Given by default. Revoke to prevent players from griefing automated subway systems.", - give_to_singleplayer= true, - default= true, -}); - local wagon={ collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5}, --physical = true, @@ -161,7 +147,7 @@ function wagon:on_punch(puncher, time_from_last_punch, tool_capabilities, direct 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 + if self.owner and puncher:get_player_name()~=self.owner and (not minetest.check_player_privs(puncher, {train_admin = 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 @@ -250,8 +236,9 @@ function wagon:on_step(dtime) --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 = self.seatp[seatno] and (minetest.check_player_privs(self.seatp[seatno], {train_operator=true}) or self.owner==self.seatp[seatno]) + local pname=self.seatp[seatno] + local driver=pname and minetest.get_player_by_name(pname) + local has_driverstand = pname and advtrains.check_driving_couple_protection(pname, self.owner, self.whitelist) if self.seat_groups then has_driverstand = has_driverstand and (seat.driving_ctrl_access or self.seat_groups[seat.group].driving_ctrl_access) else @@ -577,13 +564,6 @@ function wagon:on_rightclick(clicker) 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 @@ -598,7 +578,7 @@ function wagon:on_rightclick(clicker) if self.has_inventory and self.get_inventory_formspec then poss[#poss+1]={name=attrans("Show Inventory"), key="inv"} end - if self.seat_groups[sgr].driving_ctrl_access and minetest.check_player_privs(pname, "train_operator") then + if self.seat_groups[sgr].driving_ctrl_access and advtrains.check_driving_couple_protection(pname, self.owner, self.whitelist) then poss[#poss+1]={name=attrans("Bord Computer"), key="bordcom"} end if self.owner==pname then @@ -779,28 +759,15 @@ function wagon:show_get_on_form(pname) minetest.show_formspec(pname, "advtrains_geton_"..self.unique_id, form) end function wagon:show_wagon_properties(pname) - local numsgr=0 - if self.seat_groups then - numsgr=#self.seat_groups - end - if not self.seat_access then - self.seat_access={} - end --[[ - fields: seat access: empty: everyone - checkbox: lock couples + fields: + field: driving/couple whitelist button: save ]] - local form="size[5,"..(numsgr*1.5+4).."]" - local at=0 - if self.seat_groups then - for sgr,sgrdef in pairs(self.seat_groups) do - local text = attrans("Access to @1",sgrdef.name) - form=form.."field[0.5,"..(0.5+at*1.5)..";4,1;sgr_"..sgr..";"..text..";"..(self.seat_access[sgr] or "").."]" - at=at+1 - end - end - form=form.."button_exit[0.5,"..(at*1.5)..";4,1;save;"..attrans("Save wagon properties").."]" + local form="size[5,5]" + form = form .. "field[0.5,1;4,1;whitelist;Allow these players to drive your wagon:;"..(self.whitelist or "").."]" + --seat groups access lists were here + form=form.."button_exit[0.5,3;4,1;save;"..attrans("Save wagon properties").."]" minetest.show_formspec(pname, "advtrains_prop_"..self.unique_id, form) end @@ -819,9 +786,9 @@ local function checkcouple(eid) end return le end -local function checklock(pname, own1, own2) - return minetest.check_player_privs(pname, "train_remove") or - ((not own1 or own1==pname) or (not own2 or own2==pname)) +local function checklock(pname, own1, own2, wl1, wl2) + return advtrains.check_driving_couple_protection(pname, own1, wl1) + or advtrains.check_driving_couple_protection(pname, own2, wl2) end function wagon:show_bordcom(pname) if not self:train() then return end @@ -834,7 +801,7 @@ function wagon:show_bordcom(pname) if train.velocity==0 then form=form.."label[0.5,4.5;Train overview /coupling control:]" linhei=5 - local pre_own, owns_any = nil, minetest.check_player_privs(pname, "train_remove") + local pre_own, pre_wl, owns_any = nil, nil, minetest.check_player_privs(pname, "train_admin") for i, tpid in ipairs(train.trainparts) do local ent = advtrains.wagon_save[tpid] if ent then @@ -843,7 +810,7 @@ function wagon:show_bordcom(pname) if i~=1 then if not ent.dcpl_lock then form = form .. "image_button["..(i-0.5)..","..(linhei+1)..";1,1;advtrains_discouple.png;dcpl_"..i..";]" - if checklock(pname, ent.owner, pre_own) then + if checklock(pname, ent.owner, pre_own, ent.whitelist, pre_wl) then form = form .. "image_button["..(i-0.5)..","..(linhei+2)..";1,1;advtrains_cpl_unlock.png;dcpl_lck_"..i..";]" end else @@ -854,6 +821,7 @@ function wagon:show_bordcom(pname) form = form .. "box["..(i-0.1)..","..(linhei-0.1)..";1,1;green]" end pre_own = ent.owner + pre_wl = ent.whitelist owns_any = owns_any or (not ent.owner or ent.owner==pname) end end @@ -894,7 +862,7 @@ function wagon:show_bordcom(pname) end function wagon:handle_bordcom_fields(pname, formname, fields) local seatno=self:get_seatno(pname) - if not seatno or not self.seat_groups[self.seats[seatno].group].driving_ctrl_access or not minetest.check_player_privs(pname, "train_operator") then + if not seatno or not self.seat_groups[self.seats[seatno].group].driving_ctrl_access or not advtrains.check_driving_couple_protection(pname, self.owner, self.whitelist) then return end local train = self:train() @@ -925,7 +893,7 @@ function wagon:handle_bordcom_fields(pname, formname, fields) local ent = advtrains.wagon_save[tpid] local pent = advtrains.wagon_save[train.trainparts[i-1]] if ent and pent then - if checklock(pname, ent.owner, pent.owner) then + if checklock(pname, ent.owner, pent.owner, ent.whitelist, pent.whitelist) then for _,wagon in pairs(minetest.luaentities) do if wagon.is_wagon and wagon.initialized and wagon.unique_id==tpid then wagon.dcpl_lock=true @@ -939,7 +907,7 @@ function wagon:handle_bordcom_fields(pname, formname, fields) local ent = advtrains.wagon_save[tpid] local pent = advtrains.wagon_save[train.trainparts[i-1]] if ent and pent then - if checklock(pname, ent.owner, pent.owner) then + if checklock(pname, ent.owner, pent.owner, ent.whitelist, pent.whitelist) then for _,wagon in pairs(minetest.luaentities) do if wagon.is_wagon and wagon.initialized and wagon.unique_id==tpid then wagon.dcpl_lock=false @@ -962,11 +930,11 @@ function wagon:handle_bordcom_fields(pname, formname, fields) end local function chkownsany() - local owns_any = minetest.check_player_privs(pname, "train_remove") + local owns_any = minetest.check_player_privs(pname, "train_admin") for i, tpid in ipairs(train.trainparts) do local ent = advtrains.wagon_save[tpid] if ent then - owns_any = owns_any or (not ent.owner or ent.owner==pname) + owns_any = owns_any or advtrains.check_driving_couple_protection(pname, ent.owner, ent.whitelist) end end return owns_any @@ -1031,14 +999,13 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) 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 + if pname~=wagon.owner and not minetest.check_player_privs(pname, {train_admin = true}) 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 + if fields.whitelist then + wagon.whitelist = fields.whitelist end end end @@ -1073,7 +1040,7 @@ function wagon:seating_from_key_helper(pname, fields, no) if fields.prop and self.owner==pname then self:show_wagon_properties(pname) end - if fields.bordcom and self.seat_groups[sgr].driving_ctrl_access and minetest.check_player_privs(pname, "train_operator") then + if fields.bordcom and self.seat_groups[sgr].driving_ctrl_access and advtrains.check_driving_couple_protection(pname, self.owner, self.whitelist) then self:show_bordcom(pname) end if fields.dcwarn then @@ -1084,25 +1051,13 @@ function wagon:seating_from_key_helper(pname, fields, no) end end function wagon:check_seat_group_access(pname, sgr) - if self.seat_groups[sgr].driving_ctrl_access and not (minetest.check_player_privs(pname, "train_operator") or self.owner==pname) then - return false, "Missing train_operator privilege." + if self.seat_groups[sgr].driving_ctrl_access and not (advtrains.check_driving_couple_protection(pname, self.owner, self.whitelist)) then + return false, "Not allowed to access a driver stand!" end if self.seat_groups[sgr].driving_ctrl_access then advtrains.log("Drive", pname, self.object:getpos(), self:train().text_outside) end - if not self.seat_access then - return true - end - local sae=self.seat_access[sgr] - if not sae or sae=="" then - return true - end - for name in string.gmatch(sae, "%S+") do - if name==pname then - return true - end - end - return false, "Blacklisted by owner." + return true end function wagon:reattach_all() if not self.seatp then self.seatp={} end @@ -1158,9 +1113,12 @@ function advtrains.register_wagon(sysname_p, prototype, desc, inv_img) 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 + if not minetest.check_player_privs(placer, {train_operator = true }) then + minetest.chat_send_player(placer:get_player_name(), "You don't have the train_operator privilege.") + return itemstack + end + if not minetest.check_player_privs(placer, {train_admin = true }) and minetest.is_protected(pointed_thing.under, placer:get_player_name()) then + return itemstack end local tconns=advtrains.get_track_connections(node.name, node.param2) local yaw = placer:get_look_horizontal() + (math.pi/2) @@ -1191,9 +1149,4 @@ function advtrains.register_wagon(sysname_p, prototype, desc, inv_img) }) end ---[[ - wagons can define update_animation(self, velocity) if they have a speed-dependent animation - this function will be called when the velocity vector changes or every 2 seconds. -]] - diff --git a/privilege_guide.txt b/privilege_guide.txt new file mode 100644 index 0000000..7c4952f --- /dev/null +++ b/privilege_guide.txt @@ -0,0 +1,33 @@ + +### Advtrains Privilege Guide +All privileges are automatically granted to singleplayer, but for +multiplayer servers this might be interesting. +There are 3 groups of privileges introduced by advtrains: + +## Trains +For a player to build his own trains and driving them, the player needs +the 'train_operator' privilege. This privilege no longer allows him to +control any train, only the ones that he owns or that he is whitelisted on. +The owner of a wagon can write a whitelist which players are allowed to +operate his wagon using the wagon properties dialog. +Players having the 'train_admin' privilege can always drive, build and +destroy any train. + +## Tracks* +The area 1 node around and 4 nodes up from each track node is protected. +Players that don't have the 'track_builder' privilege can not build or +dig (or modify) anything inside this area. +If any player tries to modify anything that is in the area of a track +node and this track node is protected from him, he also can not do this. +(that said, while checking protection, the area around a track is +treated as the track node itself) + +## Turnouts and Signals* +Players without the 'railway_operator' privilege can not operate signals +and turnouts. + +* If the configuration option 'advtrains_allow_build_to_owner' is set, +an exception applies to players missing the required privileges when +they are in a protected area that they have access to. Whether the +area is protected from others is checked by checking for protection +against a dummy player called '*dummy*' -- cgit v1.2.3 From 1feae7a1ea35fd8f4f3495d1bf65f8180bb0f720 Mon Sep 17 00:00:00 2001 From: orwell96 Date: Wed, 25 Apr 2018 17:14:03 +0200 Subject: Add digiline interface and is_passive function to LuaATC --- advtrains_luaautomation/README.txt | 13 ++++++++++++- advtrains_luaautomation/active_common.lua | 14 ++++++++++++++ advtrains_luaautomation/atc_rail.lua | 10 ++++++++-- advtrains_luaautomation/environment.lua | 5 +++-- advtrains_luaautomation/operation_panel.lua | 9 +++++++-- advtrains_luaautomation/passive.lua | 15 ++++++++++++++- 6 files changed, 58 insertions(+), 8 deletions(-) diff --git a/advtrains_luaautomation/README.txt b/advtrains_luaautomation/README.txt index ce4d45d..faf3cbb 100644 --- a/advtrains_luaautomation/README.txt +++ b/advtrains_luaautomation/README.txt @@ -63,6 +63,10 @@ setstate(pos, newstate) Set the state of the passive component at position 'pos'. pos can be either a position vector (created by POS()) or a string, the name of this passive component. +is_passive(pos) +Checks whether there is a passive component at the position pos (and/or whether a passive component with this name exists) +pos can be either a position vector (created by POS()) or a string, the name of this passive component. + interrupt(time, message) Cause LuaAutomation to trigger an 'int' event on this component after the given time in seconds with the specified 'message' field. 'message' can be of any Lua data type. Not available in init code! @@ -71,6 +75,10 @@ interrupt_pos(pos, message) Immediately trigger an 'ext_int' event on the active component at position pos. 'message' is like in interrupt(). USE WITH CARE, or better don't use! Incorrect use can result in expotential growth of interrupts. +digiline_send(channel, message) +Make this active component send a digiline message on the specified channel. +Not available in init code! + ## Components and events The event table is a table of the following format: @@ -110,6 +118,9 @@ Fired when an interrupt set by the 'interrupt' function runs out. 'message' is t {type="ext_int", ext_int=true, message=} Fired when another node called 'interrupt_pos' on this position. 'message' is the message passed to the interrupt_pos function. +{type="digiline", digiline=true, channel=, msg=} +Fired when the controller receives a digiline message. + In addition to the default environment functions, the following functions are available: atc_send() @@ -132,7 +143,7 @@ set_line(number) # Operator panel This simple node executes its actions when punched. It can be used to change a switch and update the corresponding signals or similar applications. -The event fired is {type="punch", punch=true} by default. In case of an interrupt, the events are similar to the ones of the ATC rail. +The event fired is {type="punch", punch=true} by default. In case of an interrupt or a digiline message, the events are similar to the ones of the ATC rail. ### Passive components diff --git a/advtrains_luaautomation/active_common.lua b/advtrains_luaautomation/active_common.lua index 8c910c6..62dc83c 100644 --- a/advtrains_luaautomation/active_common.lua +++ b/advtrains_luaautomation/active_common.lua @@ -111,9 +111,19 @@ function ac.run_in_env(pos, evtdata, customfct_p) end local customfct=customfct_p or {} + -- add interrupt function customfct.interrupt=function(t, imesg) + assertt(t, "number") + assert(t >= 0) atlatc.interrupt.add(t, pos, {type="int", int=true, message=imesg}) end + -- add digiline_send function, if digiline is loaded + if digiline then + customfct.digiline_send=function(channel, msg) + assertt(channel, "string") + digiline:receptor_send(pos, digiline.rules.default, channel, msg) + end + end local datain=nodetbl.data or {} local succ, dataout = atlatc.envs[nodetbl.env]:execute_code(datain, nodetbl.code, evtdata, customfct) @@ -131,4 +141,8 @@ function ac.run_in_env(pos, evtdata, customfct_p) end end +function ac.on_digiline_receive(pos, node, channel, msg) + atlatc.interrupt.add(0, pos, {type="digiline", digiline=true, channel = channel, msg = msg}) +end + atlatc.active=ac diff --git a/advtrains_luaautomation/atc_rail.lua b/advtrains_luaautomation/atc_rail.lua index 3313401..88c9ee9 100644 --- a/advtrains_luaautomation/atc_rail.lua +++ b/advtrains_luaautomation/atc_rail.lua @@ -119,9 +119,15 @@ advtrains.register_tracks("default", { }, luaautomation = { fire_event=r.fire_event - } + }, + digiline = { + receptor = {}, + effector = { + action = atlatc.active.on_digiline_receive + }, + }, } - end + end, }, advtrains.trackpresets.t_30deg_straightonly) diff --git a/advtrains_luaautomation/environment.lua b/advtrains_luaautomation/environment.lua index 0b25e87..52d36a4 100644 --- a/advtrains_luaautomation/environment.lua +++ b/advtrains_luaautomation/environment.lua @@ -86,7 +86,7 @@ local function safe_string_find(...) end local mp=minetest.get_modpath("advtrains_luaautomation") -local p_api_getstate, p_api_setstate = dofile(mp.."/passive.lua") +local p_api_getstate, p_api_setstate, p_api_is_passive = dofile(mp.."/passive.lua") local static_env = { --core LUA functions @@ -150,7 +150,8 @@ local static_env = { POS = function(x,y,z) return {x=x, y=y, z=z} end, getstate = p_api_getstate, setstate = p_api_setstate, - --interrupts are handled per node, position unknown. + is_passive = p_api_is_passive, + --interrupts are handled per node, position unknown. (same goes for digilines) --however external interrupts can be set here. interrupt_pos = function(pos, imesg) if not type(pos)=="table" or not pos.x or not pos.y or not pos.z then diff --git a/advtrains_luaautomation/operation_panel.lua b/advtrains_luaautomation/operation_panel.lua index 1d585f7..eb7201c 100644 --- a/advtrains_luaautomation/operation_panel.lua +++ b/advtrains_luaautomation/operation_panel.lua @@ -18,6 +18,11 @@ minetest.register_node("advtrains_luaautomation:oppanel", { on_punch = on_punch, luaautomation = { fire_event=atlatc.active.run_in_env - } - + }, + digiline = { + receptor = {}, + effector = { + action = atlatc.active.on_digiline_receive + }, + }, }) diff --git a/advtrains_luaautomation/passive.lua b/advtrains_luaautomation/passive.lua index 774df8a..e0902f5 100644 --- a/advtrains_luaautomation/passive.lua +++ b/advtrains_luaautomation/passive.lua @@ -34,6 +34,19 @@ local function setstate(parpos, newstate) end end +local function is_passive(parpos) + local pos=atlatc.pcnaming.resolve_pos(parpos) + if type(pos)~="table" or (not pos.x or not pos.y or not pos.z) then + return false + end + local node=advtrains.ndb.get_node(pos) + local ndef=minetest.registered_nodes[node.name] + if ndef and ndef.luaautomation and ndef.luaautomation.getstate then + return true + end + return false +end + -- gets called from environment.lua -- return the values here to keep them local -return getstate, setstate +return getstate, setstate, is_passive -- cgit v1.2.3 From 8d8e8c1553bc631b8f357005b506bc651de9a138 Mon Sep 17 00:00:00 2001 From: orwell96 Date: Fri, 25 May 2018 18:25:24 +0200 Subject: Write something into description.txt --- description.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/description.txt b/description.txt index 4fa50ae..1cbd195 100644 --- a/description.txt +++ b/description.txt @@ -1 +1,2 @@ -advtrains 1.3.8 +This modpack adds realistic, good-looking trains and a rail system based on 30-degree turns. +It also provides means to automate train operation. -- cgit v1.2.3