LuaAutomation - Basic component implementation
Implements the base code for LuaAutomation, an ATC rail and a punch-operated 'operation panel' as well as interface for passive components. Changes in advtrains code where neccessary. Supported passive components are light signals, switches and mesecon switches
This commit is contained in:
parent
a8f9e3d43e
commit
b19033b224
|
@ -11,7 +11,7 @@ advtrains = {trains={}, wagon_save={}}
|
|||
|
||||
advtrains.modpath = minetest.get_modpath("advtrains")
|
||||
|
||||
local function print_concat_table(a)
|
||||
function advtrains.print_concat_table(a)
|
||||
local str=""
|
||||
local stra=""
|
||||
for i=1,50 do
|
||||
|
@ -43,7 +43,11 @@ local function print_concat_table(a)
|
|||
end
|
||||
atprint=function() end
|
||||
if minetest.setting_getbool("advtrains_debug") then
|
||||
atprint=function(t, ...) minetest.log("action", "[advtrains]"..print_concat_table({t, ...})) minetest.chat_send_all("[advtrains]"..print_concat_table({t, ...})) end
|
||||
atprint=function(t, ...)
|
||||
local text=advtrains.print_concat_table({t, ...})
|
||||
minetest.log("action", "[advtrains]"..text)
|
||||
minetest.chat_send_all("[advtrains]"..text)
|
||||
end
|
||||
end
|
||||
sid=function(id) return string.sub(id, -4) end
|
||||
|
||||
|
|
|
@ -250,6 +250,7 @@ function advtrains.register_tracks(tracktype, def, preset)
|
|||
if newstate~=is_state then
|
||||
advtrains.ndb.swap_node(pos, {name=def.nodename_prefix.."_"..suffix_target, param2=node.param2})
|
||||
end
|
||||
advtrains.invalidate_all_paths()
|
||||
end
|
||||
local mesec
|
||||
if mesecon_state then -- if mesecons is not wanted, do not.
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
|||
advtrains
|
||||
mesecons?
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
||||
}
|
||||
|
||||
})
|
|
@ -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,
|
||||
},
|
||||
})
|
|
@ -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
|
|
@ -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
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 631 B |
Loading…
Reference in New Issue