aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--advtrains/init.lua3
-rw-r--r--advtrains/protection.lua148
-rw-r--r--advtrains/settingtypes.txt5
-rw-r--r--advtrains/signals.lua10
-rw-r--r--advtrains/tracks.lua3
-rw-r--r--advtrains/wagons.lua117
-rw-r--r--advtrains_luaautomation/README.txt13
-rw-r--r--advtrains_luaautomation/active_common.lua14
-rw-r--r--advtrains_luaautomation/atc_rail.lua10
-rw-r--r--advtrains_luaautomation/environment.lua5
-rw-r--r--advtrains_luaautomation/operation_panel.lua9
-rw-r--r--advtrains_luaautomation/passive.lua15
-rw-r--r--description.txt3
-rw-r--r--privilege_guide.txt33
14 files changed, 290 insertions, 98 deletions
diff --git a/advtrains/init.lua b/advtrains/init.lua
index d7bff4d..65e5048 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")
@@ -275,7 +276,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/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=<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=<channel>, msg=<message>}
+Fired when the controller receives a digiline message.
+
In addition to the default environment functions, the following functions are available:
atc_send(<atc_command>)
@@ -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
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.
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*'