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 switchesmaster
parent
a8f9e3d43e
commit
b19033b224
|
@ -11,7 +11,7 @@ advtrains = {trains={}, wagon_save={}}
|
||||||
|
|
||||||
advtrains.modpath = minetest.get_modpath("advtrains")
|
advtrains.modpath = minetest.get_modpath("advtrains")
|
||||||
|
|
||||||
local function print_concat_table(a)
|
function advtrains.print_concat_table(a)
|
||||||
local str=""
|
local str=""
|
||||||
local stra=""
|
local stra=""
|
||||||
for i=1,50 do
|
for i=1,50 do
|
||||||
|
@ -43,7 +43,11 @@ local function print_concat_table(a)
|
||||||
end
|
end
|
||||||
atprint=function() end
|
atprint=function() end
|
||||||
if minetest.setting_getbool("advtrains_debug") then
|
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
|
end
|
||||||
sid=function(id) return string.sub(id, -4) 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
|
if newstate~=is_state then
|
||||||
advtrains.ndb.swap_node(pos, {name=def.nodename_prefix.."_"..suffix_target, param2=node.param2})
|
advtrains.ndb.swap_node(pos, {name=def.nodename_prefix.."_"..suffix_target, param2=node.param2})
|
||||||
end
|
end
|
||||||
|
advtrains.invalidate_all_paths()
|
||||||
end
|
end
|
||||||
local mesec
|
local mesec
|
||||||
if mesecon_state then -- if mesecons is not wanted, do not.
|
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