aboutsummaryrefslogtreecommitdiff
path: root/advtrains/advtrains_luaautomation
diff options
context:
space:
mode:
Diffstat (limited to 'advtrains/advtrains_luaautomation')
-rw-r--r--advtrains/advtrains_luaautomation/active_common.lua118
-rw-r--r--advtrains/advtrains_luaautomation/atc_rail.lua92
-rw-r--r--advtrains/advtrains_luaautomation/chatcmds.lua0
-rw-r--r--advtrains/advtrains_luaautomation/depends.txt2
-rw-r--r--advtrains/advtrains_luaautomation/environment.lua253
-rw-r--r--advtrains/advtrains_luaautomation/init.lua98
-rw-r--r--advtrains/advtrains_luaautomation/interrupt.lua48
-rw-r--r--advtrains/advtrains_luaautomation/operation_panel.lua23
-rw-r--r--advtrains/advtrains_luaautomation/p_display.lua0
-rw-r--r--advtrains/advtrains_luaautomation/p_mesecon_iface.lua60
-rw-r--r--advtrains/advtrains_luaautomation/passive.lua29
-rw-r--r--advtrains/advtrains_luaautomation/passive_api.txt23
-rw-r--r--advtrains/advtrains_luaautomation/textures/atlatc_oppanel.pngbin0 -> 631 bytes
13 files changed, 746 insertions, 0 deletions
diff --git a/advtrains/advtrains_luaautomation/active_common.lua b/advtrains/advtrains_luaautomation/active_common.lua
new file mode 100644
index 0000000..b94a260
--- /dev/null
+++ b/advtrains/advtrains_luaautomation/active_common.lua
@@ -0,0 +1,118 @@
+
+
+local ac = {nodes={}}
+
+function ac.load(data)
+ ac.nodes=data and data.nodes or {}
+end
+function ac.save()
+ return {nodes = ac.nodes}
+end
+
+function ac.after_place_node(pos, player)
+ advtrains.ndb.update(pos)
+ local meta=minetest.get_meta(pos)
+ meta:set_string("formspec", ac.getform(pos, meta))
+ meta:set_string("infotext", "LuaAutomation component, unconfigured.")
+ local ph=minetest.hash_node_position(pos)
+ --just get first available key!
+ for en,_ in pairs(atlatc.envs) do
+ ac.nodes[ph]={env=en}
+ return
+ end
+end
+function ac.getform(pos, meta_p)
+ local meta = meta_p or minetest.get_meta(pos)
+ local envs_asvalues={}
+
+ local ph=minetest.hash_node_position(pos)
+ local nodetbl = ac.nodes[ph]
+ local env, code, err = nil, "", ""
+ if nodetbl then
+ code=nodetbl.code or ""
+ err=nodetbl.err or ""
+ env=nodetbl.env or ""
+ end
+ local sel = 1
+ for n,_ in pairs(atlatc.envs) do
+ envs_asvalues[#envs_asvalues+1]=n
+ if n==env then
+ sel=#envs_asvalues
+ end
+ end
+ local form = "size[10,10]dropdown[0,0;3;env;"..table.concat(envs_asvalues, ",")..";"..sel.."]"
+ .."button[4,0;2,1;save;Save]button[7,0;2,1;cle;Clear local env] textarea[0.2,1;10,10;code;Code;"..minetest.formspec_escape(code).."]"
+ .."label[0,9.8;"..err.."]"
+ return form
+end
+
+function ac.after_dig_node(pos, node, player)
+ advtrains.invalidate_all_paths()
+ advtrains.ndb.clear(pos)
+ local ph=minetest.hash_node_position(pos)
+ ac.nodes[ph]=nil
+end
+
+function ac.on_receive_fields(pos, formname, fields, player)
+ if not minetest.check_player_privs(player:get_player_name(), {atlatc=true}) then
+ minetest.chat_send_player(player:get_player_name(), "Missing privilege: atlatc - Operation cancelled!")
+ end
+
+ local meta=minetest.get_meta(pos)
+ local ph=minetest.hash_node_position(pos)
+ local nodetbl = ac.nodes[ph] or {}
+ --if fields.quit then return end
+ if fields.env then
+ nodetbl.env=fields.env
+ end
+ if fields.code then
+ nodetbl.code=fields.code
+ end
+ if fields.save then
+ nodetbl.err=nil
+ end
+ if fields.cle then
+ nodetbl.data={}
+ end
+ meta:set_string("formspec", ac.getform(pos, meta))
+
+ ac.nodes[ph]=nodetbl
+ if nodetbl.env then
+ meta:set_string("infotext", "LuaAutomation component, assigned to environment '"..nodetbl.env.."'")
+ else
+ meta:set_string("infotext", "LuaAutomation component, invalid enviroment set!")
+ end
+end
+
+function ac.run_in_env(pos, evtdata, customfct)
+ local ph=minetest.hash_node_position(pos)
+ local nodetbl = ac.nodes[ph] or {}
+
+ local meta
+ if minetest.get_node(pos) then
+ meta=minetest.get_meta(pos)
+ end
+
+ if not nodetbl.env or not atlatc.envs[nodetbl.env] then
+ return false, "Not an existing environment: "..(nodetbl.env or "<nil>")
+ end
+ if not nodetbl.code or nodetbl.code=="" then
+ return false, "No code to run!"
+ end
+
+ local datain=nodetbl.data or {}
+ local succ, dataout = atlatc.envs[nodetbl.env]:execute_code(datain, nodetbl.code, evtdata, customfct)
+ if succ then
+ atlatc.active.nodes[ph].data=atlatc.remove_invalid_data(dataout)
+ else
+ atlatc.active.nodes[ph].err=dataout
+ if meta then
+ meta:set_string("infotext", "LuaAutomation ATC interface rail, ERROR:"..dataout)
+ end
+ end
+ if meta then
+ meta:set_string("formspec", ac.getform(pos, meta))
+ end
+end
+
+atlatc.active=ac
diff --git a/advtrains/advtrains_luaautomation/atc_rail.lua b/advtrains/advtrains_luaautomation/atc_rail.lua
new file mode 100644
index 0000000..2af03cf
--- /dev/null
+++ b/advtrains/advtrains_luaautomation/atc_rail.lua
@@ -0,0 +1,92 @@
+-- atc_rail.lua
+-- registers and handles the ATC rail. Active component.
+-- This is the only component that can interface with trains, so train interface goes here too.
+
+--Using subtable
+local r={}
+
+function r.fire_event(pos, evtdata)
+
+ local ph=minetest.hash_node_position(pos)
+ local railtbl = atlatc.active.nodes[ph] or {}
+
+ local arrowconn = railtbl.arrowconn
+
+ --prepare ingame API for ATC. Regenerate each time since pos needs to be known
+ local atc_valid, atc_arrow
+ local train_id=advtrains.detector.on_node[ph]
+ local train=advtrains.trains[train_id]
+ if not train then return false end
+ if not train.path then
+ --we happened to get in between an invalidation step
+ --delay
+ atlatc.interrupt.add(0,pos,evtdata)
+ return
+ end
+ for index, ppos in pairs(train.path) do
+ if vector.equals(advtrains.round_vector_floor_y(ppos), pos) then
+ atc_arrow =
+ vector.equals(
+ advtrains.dirCoordSet(pos, arrowconn),
+ advtrains.round_vector_floor_y(train.path[index+train.movedir])
+ )
+ atc_valid = true
+ end
+ end
+ local customfct={
+ atc_send = function(cmd)
+ advtrains.atc.train_reset_command(train_id)
+ if atc_valid then
+ train.atc_command=cmd
+ train.atc_arrow=atc_arrow
+ return atc_valid
+ end
+ end,
+ atc_reset = function(cmd)
+ advtrains.atc.train_reset_command(train_id)
+ return true
+ end,
+ atc_arrow = atc_arrow
+ }
+
+ atlatc.active.run_in_env(pos, evtdata, customfct)
+
+end
+
+advtrains.register_tracks("default", {
+ nodename_prefix="advtrains_luaautomation:dtrack",
+ texture_prefix="advtrains_dtrack_atc",
+ models_prefix="advtrains_dtrack_detector",
+ models_suffix=".b3d",
+ shared_texture="advtrains_dtrack_rail_atc.png",
+ description=atltrans("LuaAutomation ATC Rail"),
+ formats={},
+ get_additional_definiton = function(def, preset, suffix, rotation)
+ return {
+ after_place_node = atlatc.active.after_place_node,
+ after_dig_node = atlatc.active.after_dig_node,
+
+ on_receive_fields = function(pos, ...)
+ atlatc.active.on_receive_fields(pos, ...)
+
+ --set arrowconn (for ATC)
+ local ph=minetest.hash_node_position(pos)
+ local _, conn1=advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
+ atlatc.active.nodes[ph].arrowconn=conn1
+ end,
+
+ advtrains = {
+ on_train_enter = function(pos, train_id)
+ --do async. Event is fired in train steps
+ atlatc.interrupt.add(0, pos, {type="train", id=train_id})
+ end,
+ },
+ luaautomation = {
+ fire_event=r.fire_event
+ }
+ }
+ end
+}, advtrains.trackpresets.t_30deg_straightonly)
+
+
+atlatc.rail = r
diff --git a/advtrains/advtrains_luaautomation/chatcmds.lua b/advtrains/advtrains_luaautomation/chatcmds.lua
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/advtrains/advtrains_luaautomation/chatcmds.lua
diff --git a/advtrains/advtrains_luaautomation/depends.txt b/advtrains/advtrains_luaautomation/depends.txt
new file mode 100644
index 0000000..366cad5
--- /dev/null
+++ b/advtrains/advtrains_luaautomation/depends.txt
@@ -0,0 +1,2 @@
+advtrains
+mesecons? \ No newline at end of file
diff --git a/advtrains/advtrains_luaautomation/environment.lua b/advtrains/advtrains_luaautomation/environment.lua
new file mode 100644
index 0000000..d04563a
--- /dev/null
+++ b/advtrains/advtrains_luaautomation/environment.lua
@@ -0,0 +1,253 @@
+-------------
+-- lua sandboxed environment
+
+-- function to cross out functions and userdata.
+-- modified from dump()
+function atlatc.remove_invalid_data(o, nested)
+ if o==nil then return nil end
+ local valid_dt={["nil"]=true, boolean=true, number=true, string=true}
+ if type(o) ~= "table" then
+ --check valid data type
+ if not valid_dt[type(o)] then
+ return nil
+ end
+ return o
+ end
+ -- Contains table -> true/nil of currently nested tables
+ nested = nested or {}
+ if nested[o] then
+ return nil
+ end
+ nested[o] = true
+ for k, v in pairs(o) do
+ v = atlatc.remove_invalid_data(v, nested)
+ end
+ nested[o] = nil
+ return o
+end
+
+
+local env_proto={
+ load = function(self, envname, data)
+ self.name=envname
+ self.sdata=data.sdata and atlatc.remove_invalid_data(data.sdata) or {}
+ self.fdata={}
+ self.init_code=data.init_code or ""
+ self.step_code=data.step_code or ""
+ end,
+ save = function(self)
+ -- throw any function values out of the sdata table
+ self.sdata = atlatc.remove_invalid_data(self.sdata)
+ return {sdata = self.sdata, init_code=self.init_code, step_code=self.step_code}
+ end,
+}
+
+--Environment
+--Code modified from mesecons_luacontroller (credit goes to Jeija and mesecons contributors)
+
+local safe_globals = {
+ "assert", "error", "ipairs", "next", "pairs", "select",
+ "tonumber", "tostring", "type", "unpack", "_VERSION"
+}
+
+--print is actually minetest.chat_send_all()
+--using advtrains.print_concat_table because it's cool
+local function safe_print(t, ...)
+ local str=advtrains.print_concat_table({t, ...})
+ minetest.log("action", "[atlatc] "..str)
+ minetest.chat_send_all(str)
+end
+
+local function safe_date()
+ return(os.date("*t",os.time()))
+end
+
+-- string.rep(str, n) with a high value for n can be used to DoS
+-- the server. Therefore, limit max. length of generated string.
+local function safe_string_rep(str, n)
+ if #str * n > 2000 then
+ debug.sethook() -- Clear hook
+ error("string.rep: string length overflow", 2)
+ end
+
+ return string.rep(str, n)
+end
+
+-- string.find with a pattern can be used to DoS the server.
+-- Therefore, limit string.find to patternless matching.
+local function safe_string_find(...)
+ if (select(4, ...)) ~= true then
+ debug.sethook() -- Clear hook
+ error("string.find: 'plain' (fourth parameter) must always be true for security reasons.")
+ end
+
+ return string.find(...)
+end
+
+local mp=minetest.get_modpath("advtrains_luaautomation")
+local p_api_getstate, p_api_setstate = dofile(mp.."/passive.lua")
+
+local static_env = {
+ --core LUA functions
+ print = safe_print,
+ string = {
+ byte = string.byte,
+ char = string.char,
+ format = string.format,
+ len = string.len,
+ lower = string.lower,
+ upper = string.upper,
+ rep = safe_string_rep,
+ reverse = string.reverse,
+ sub = string.sub,
+ find = safe_string_find,
+ },
+ math = {
+ abs = math.abs,
+ acos = math.acos,
+ asin = math.asin,
+ atan = math.atan,
+ atan2 = math.atan2,
+ ceil = math.ceil,
+ cos = math.cos,
+ cosh = math.cosh,
+ deg = math.deg,
+ exp = math.exp,
+ floor = math.floor,
+ fmod = math.fmod,
+ frexp = math.frexp,
+ huge = math.huge,
+ ldexp = math.ldexp,
+ log = math.log,
+ log10 = math.log10,
+ max = math.max,
+ min = math.min,
+ modf = math.modf,
+ pi = math.pi,
+ pow = math.pow,
+ rad = math.rad,
+ random = math.random,
+ sin = math.sin,
+ sinh = math.sinh,
+ sqrt = math.sqrt,
+ tan = math.tan,
+ tanh = math.tanh,
+ },
+ table = {
+ concat = table.concat,
+ insert = table.insert,
+ maxn = table.maxn,
+ remove = table.remove,
+ sort = table.sort,
+ },
+ os = {
+ clock = os.clock,
+ difftime = os.difftime,
+ time = os.time,
+ date = safe_date,
+ },
+ POS = function(x,y,z) return {x=x, y=y, z=z} end,
+ getstate = p_api_getstate,
+ setstate = p_api_setstate,
+
+}
+
+for _, name in pairs(safe_globals) do
+ static_env[name] = _G[name]
+end
+
+
+--The environment all code calls get is a table that has set static_env as metatable.
+--In general, every variable is local to a single code chunk, but kept persistent over code re-runs. Data is also saved, but functions and userdata and circular references are removed
+--Init code and step code's environments are not saved
+-- S - Table that can contain any save data global to the environment. Will be saved statically. Can't contain functions or userdata or circular references.
+-- F - Table global to the environment, can contain volatile data that is deleted when server quits.
+-- The init code should populate this table with functions and other definitions.
+
+-- returns: true, fenv if successful; nil, error if error
+function env_proto:execute_code(fenv, code, evtdata, customfct)
+ local metatbl ={
+ __index = function(t, i)
+ print("index metamethod:",i)
+ if i=="S" then
+ return self.sdata
+ elseif i=="F" then
+ return self.fdata
+ elseif i=="event" then
+ return evtdata
+ elseif customfct and customfct[i] then
+ return customfct[i]
+ end
+ return static_env[i]
+ end,
+ __newindex = function(t, i, v)
+ if i=="S" or i=="F" or i=="event" or (customfct and customfct[i]) or static_env[i] then
+ debug.sethook()
+ error("Trying to overwrite environment contents")
+ end
+ rawset(t,i,v)
+ end,
+ }
+ setmetatable(fenv, metatbl)
+ local fun, err=loadstring(code)
+ if not fun then
+ return false, err
+ end
+ setfenv(fun, fenv)
+ local succ, data = pcall(fun)
+ if succ then
+ data=fenv
+ end
+ return succ, data
+end
+
+function env_proto:run_initcode()
+ if self.init_code and self.init_code~="" then
+ local succ, err = self:execute_code(self.init_code, nil, {}, "Global init code")
+ if not succ then
+ --TODO
+ end
+ end
+end
+function env_proto:run_stepcode()
+ if self.step_code and self.step_code~="" then
+ local succ, err = self:execute_code({}, self.step_code, nil, {})
+ if not succ then
+ --TODO
+ end
+ end
+end
+
+--- class interface
+
+function atlatc.env_new(name)
+ local newenv={
+ name=name,
+ init_code="",
+ step_code="",
+ sdata={}
+ }
+ setmetatable(newenv, {__index=env_proto})
+ return newenv
+end
+function atlatc.env_load(name, data)
+ local newenv={}
+ setmetatable(newenv, {__index=env_proto})
+ newenv:load(name, data)
+ return newenv
+end
+
+function atlatc.run_initcode()
+ for envname, env in pairs(atlatc.envs) do
+ env:run_initcode()
+ end
+end
+function atlatc.run_stepcode()
+ for envname, env in pairs(atlatc.envs) do
+ env:run_stepcode()
+ end
+end
+
+
+
+
diff --git a/advtrains/advtrains_luaautomation/init.lua b/advtrains/advtrains_luaautomation/init.lua
new file mode 100644
index 0000000..12e1b1d
--- /dev/null
+++ b/advtrains/advtrains_luaautomation/init.lua
@@ -0,0 +1,98 @@
+-- advtrains_luaautomation/init.lua
+-- Lua automation features for advtrains
+-- Uses global table 'atlatc' (AdvTrains_LuaATC)
+
+-- Boilerplate to support localized strings if intllib mod is installed.
+if minetest.get_modpath("intllib") then
+ atltrans = intllib.Getter()
+else
+ atltrans = function(s,a,...)a={a,...}return s:gsub("@(%d+)",function(n)return a[tonumber(n)]end)end
+end
+
+--Privilege
+--Only trusted players should be enabled to build stuff which can break the server.
+
+atlatc = { envs = {}}
+
+minetest.register_privilege("atlatc", { description = "Player can place and modify LUA ATC components. Grant with care! Allows to execute bad LUA code.", give_to_singleplayer = false, default= false })
+
+local mp=minetest.get_modpath("advtrains_luaautomation")
+if not mp then
+ error("Mod name error: Mod folder is not named 'advtrains_luaautomation'!")
+end
+dofile(mp.."/environment.lua")
+dofile(mp.."/interrupt.lua")
+dofile(mp.."/active_common.lua")
+dofile(mp.."/atc_rail.lua")
+dofile(mp.."/operation_panel.lua")
+dofile(mp.."/p_mesecon_iface.lua")
+
+local filename=minetest.get_worldpath().."/advtrains_luaautomation"
+local file, err = io.open(filename, "r")
+if not file then
+ minetest.log("error", " Failed to read advtrains_luaautomation save data from file "..filename..": "..(err or "Unknown Error"))
+else
+ local tbl = minetest.deserialize(file:read("*a"))
+ if type(tbl) == "table" then
+ if tbl.version==1 then
+ for envname, data in pairs(tbl.envs) do
+ atlatc.envs[envname]=atlatc.env_load(envname, data)
+ end
+ atlatc.active.load(tbl.active)
+ atlatc.interrupt.load(tbl.interrupt)
+ end
+ else
+ minetest.log("error", " Failed to read advtrains_luaautomation save data from file "..filename..": Not a table!")
+ end
+ file:close()
+end
+
+-- run init code of all environments
+atlatc.run_initcode()
+
+atlatc.save = function()
+ --versions:
+ -- 1 - Initial save format.
+
+ local envdata={}
+ for envname, env in pairs(atlatc.envs) do
+ envdata[envname]=env:save()
+ end
+ local save_tbl={
+ version = 1,
+ envs=envdata,
+ active = atlatc.active.save(),
+ interrupt = atlatc.interrupt.save(),
+ }
+
+ local datastr = minetest.serialize(save_tbl)
+ if not datastr then
+ minetest.log("error", " Failed to save advtrains_luaautomation save data to file "..filename..": Can't serialize!")
+ return
+ end
+ local file, err = io.open(filename, "w")
+ if err then
+ minetest.log("error", " Failed to save advtrains_luaautomation save data to file "..filename..": "..(err or "Unknown Error"))
+ return
+ end
+ file:write(datastr)
+ file:close()
+end
+
+-- globalstep for step code
+local timer, step_int=0, 2
+local stimer, sstep_int=0, 10
+
+minetest.register_globalstep(function(dtime)
+ timer=timer+dtime
+ if timer>step_int then
+ timer=0
+ atlatc.run_stepcode()
+ end
+ stimer=stimer+dtime
+ if stimer>sstep_int then
+ stimer=0
+ atlatc.save()
+ end
+end)
+minetest.register_on_shutdown(atlatc.save)
diff --git a/advtrains/advtrains_luaautomation/interrupt.lua b/advtrains/advtrains_luaautomation/interrupt.lua
new file mode 100644
index 0000000..e9ad443
--- /dev/null
+++ b/advtrains/advtrains_luaautomation/interrupt.lua
@@ -0,0 +1,48 @@
+-- interrupt.lua
+-- implements interrupt queue
+
+--to be saved: pos and evtdata
+local iq={}
+local queue={}
+local timer=0
+local run=false
+
+function iq.load(data)
+ local d=data or {}
+ queue = d.queue or {}
+ timer = d.timer or 0
+end
+function iq.save()
+ return {queue = queue}
+end
+
+function iq.add(t, pos, evtdata)
+ queue[#queue+1]={t=t+timer, p=pos, e=evtdata}
+ run=true
+end
+
+minetest.register_globalstep(function(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
+ end
+ end
+ end
+end)
+
+
+
+atlatc.interrupt=iq
diff --git a/advtrains/advtrains_luaautomation/operation_panel.lua b/advtrains/advtrains_luaautomation/operation_panel.lua
new file mode 100644
index 0000000..1d585f7
--- /dev/null
+++ b/advtrains/advtrains_luaautomation/operation_panel.lua
@@ -0,0 +1,23 @@
+
+local function on_punch(pos, player)
+ atlatc.interrupt.add(0, pos, {type="punch", punch=true})
+end
+
+
+minetest.register_node("advtrains_luaautomation:oppanel", {
+ drawtype = "normal",
+ tiles={"atlatc_oppanel.png"},
+ description = "LuaAutomation operation panel",
+ groups = {
+ choppy = 1,
+ save_in_nodedb=1,
+ },
+ after_place_node = atlatc.active.after_place_node,
+ after_dig_node = atlatc.active.after_dig_node,
+ on_receive_fields = atlatc.active.on_receive_fields,
+ on_punch = on_punch,
+ luaautomation = {
+ fire_event=atlatc.active.run_in_env
+ }
+
+})
diff --git a/advtrains/advtrains_luaautomation/p_display.lua b/advtrains/advtrains_luaautomation/p_display.lua
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/advtrains/advtrains_luaautomation/p_display.lua
diff --git a/advtrains/advtrains_luaautomation/p_mesecon_iface.lua b/advtrains/advtrains_luaautomation/p_mesecon_iface.lua
new file mode 100644
index 0000000..d7e1052
--- /dev/null
+++ b/advtrains/advtrains_luaautomation/p_mesecon_iface.lua
@@ -0,0 +1,60 @@
+-- p_mesecon_iface.lua
+-- Mesecons interface by overriding the switch
+
+if not mesecon then return end
+
+minetest.override_item("mesecons_switch:mesecon_switch_off", {
+ groups = {
+ dig_immediate=2,
+ save_in_nodedb=1,
+ },
+ on_rightclick = function (pos, node)
+ if(mesecon.flipstate(pos, node) == "on") then
+ mesecon.receptor_on(pos)
+ else
+ mesecon.receptor_off(pos)
+ end
+ minetest.sound_play("mesecons_switch", {pos=pos})
+ advtrains.ndb.update(pos, node)
+ end,
+ on_updated_from_nodedb = function(pos, node)
+ mesecon.receptor_off(pos)
+ end,
+ luaautomation = {
+ getstate = "off",
+ setstate = function(pos, node, newstate)
+ if newstate=="on" then
+ advtrains.ndb.swap_node(pos, {name="mesecons_switch:mesecon_switch_on", param2=node.param2})
+ mesecon.receptor_on(pos)
+ end
+ end,
+ },
+})
+
+minetest.override_item("mesecons_switch:mesecon_switch_on", {
+ groups = {
+ dig_immediate=2,
+ save_in_nodedb=1,
+ },
+ on_rightclick = function (pos, node)
+ if(mesecon.flipstate(pos, node) == "on") then
+ mesecon.receptor_on(pos)
+ else
+ mesecon.receptor_off(pos)
+ end
+ minetest.sound_play("mesecons_switch", {pos=pos})
+ advtrains.ndb.update(pos, node)
+ end,
+ on_updated_from_nodedb = function(pos, node)
+ mesecon.receptor_on(pos)
+ end,
+ luaautomation = {
+ getstate = "on",
+ setstate = function(pos, node, newstate)
+ if newstate=="off" then
+ advtrains.ndb.swap_node(pos, {name="mesecons_switch:mesecon_switch_off", param2=node.param2})
+ mesecon.receptor_off(pos)
+ end
+ end,
+ },
+})
diff --git a/advtrains/advtrains_luaautomation/passive.lua b/advtrains/advtrains_luaautomation/passive.lua
new file mode 100644
index 0000000..78b8c2d
--- /dev/null
+++ b/advtrains/advtrains_luaautomation/passive.lua
@@ -0,0 +1,29 @@
+-- passive.lua
+-- API to passive components, as described in passive_api.txt
+
+local function getstate(pos)
+ local node=advtrains.ndb.get_node(pos)
+ local ndef=minetest.registered_nodes[node.name]
+ if ndef and ndef.luaautomation and ndef.luaautomation.getstate then
+ local st=ndef.luaautomation.getstate
+ if type(st)=="function" then
+ return st(pos, node)
+ else
+ return st
+ end
+ end
+ return nil
+end
+
+local function setstate(pos, newstate)
+ local node=advtrains.ndb.get_node(pos)
+ local ndef=minetest.registered_nodes[node.name]
+ if ndef and ndef.luaautomation and ndef.luaautomation.setstate then
+ local st=ndef.luaautomation.setstate
+ st(pos, node, newstate)
+ end
+end
+
+-- gets called from environment.lua
+-- return the values here to keep them local
+return getstate, setstate
diff --git a/advtrains/advtrains_luaautomation/passive_api.txt b/advtrains/advtrains_luaautomation/passive_api.txt
new file mode 100644
index 0000000..a735208
--- /dev/null
+++ b/advtrains/advtrains_luaautomation/passive_api.txt
@@ -0,0 +1,23 @@
+Lua Automation - Passive Component API
+
+Passive components are nodes that do not have code running in them. However, active components can query these and request actions from them. Examples:
+Switches
+Signals
+Displays
+Mesecon Transmitter
+
+All passive components have a table called 'luaautomation' in their node definition and have the group 'save_in_nodedb' set, so they work in unloaded chunks.
+Example for a switch:
+luaautomation = {
+ getstate = function(pos, node)
+ return "st"
+ end,
+ -- OR
+ getstate = "st",
+
+ setstate = function(pos, node, newstate)
+ if newstate=="cr" then
+ advtrains.ndb.swap_node(pos, <corresponding switch alt>)
+ end
+ end
+} \ No newline at end of file
diff --git a/advtrains/advtrains_luaautomation/textures/atlatc_oppanel.png b/advtrains/advtrains_luaautomation/textures/atlatc_oppanel.png
new file mode 100644
index 0000000..96eb30e
--- /dev/null
+++ b/advtrains/advtrains_luaautomation/textures/atlatc_oppanel.png
Binary files differ