Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • Illuna-Minetest/mesecons
1 result
Show changes
Showing
with 94 additions and 41 deletions
Effector, glows blue when powered. Effector, glows blue when powered.
It works in an inactive block.
Effector, glows dark grey when powered. Effector, glows dark grey when powered.
It works in an inactive block.
Effector, glows green when powered. Effector, glows green when powered.
It works in an inactive block.
Effector, glows light grey when powered. Effector, glows light grey when powered.
It works in an inactive block.
Effector, glows red when powered. Effector, glows red when powered.
It works in an inactive block.
Effector, glows yellow when powered. Effector, glows yellow when powered.
It works in an inactive block.
The Luacontroller is an advanced programmable component. The Luacontroller is an advanced programmable component.
You can simply code it in the language Mesecons uses itself: Lua! You can simply code it in the language Mesecons uses itself: Lua!
All the code runs in a sandbox, so it's completely safe (but I won't guarantee that for absolute certainty!). All the code runs in a sandbox, so it's completely safe (but I won't guarantee that for absolute certainty!).
It works fine in an unloaded block, loading the block when the program needs to run.
Events are properly delivered after a server restart.
<a href="http://mesecons.net/luacontroller/">Documentation is available here!</a> <a href="http://mesecons.net/luacontroller/">Documentation is available here!</a>
...@@ -266,32 +266,45 @@ local function remove_functions(x) ...@@ -266,32 +266,45 @@ local function remove_functions(x)
return x return x
end end
-- itbl: Flat table of functions to run after sandbox cleanup, used to prevent various security hazards -- The setting affects API so is not intended to be changeable at runtime
local function get_interrupt(pos, itbl, send_warning) local get_interrupt
-- iid = interrupt id if mesecon.setting("luacontroller_lightweight_interrupts", false) then
local function interrupt(time, iid) -- use node timer
-- NOTE: This runs within string metatable sandbox, so don't *rely* on anything of the form (""):y get_interrupt = function(pos, itbl, send_warning)
-- Hence the values get moved out. Should take less time than original, so totally compatible return (function(time, iid)
if type(time) ~= "number" then return end if type(time) ~= "number" then error("Delay must be a number") end
table.insert(itbl, function () if iid ~= nil then send_warning("Interrupt IDs are disabled on this server") end
-- Outside string metatable sandbox, can safely run this now table.insert(itbl, function() minetest.get_node_timer(pos):start(time) end)
local luac_id = minetest.get_meta(pos):get_int("luac_id")
-- Check if IID is dodgy, so you can't use interrupts to store an infinite amount of data.
-- Note that this is safe from alter-after-free because this code gets run after the sandbox has ended.
-- This runs outside of the timer and *shouldn't* harm perf. unless dodgy data is being sent in the first place
iid = remove_functions(iid)
local msg_ser = minetest.serialize(iid)
if #msg_ser <= mesecon.setting("luacontroller_interruptid_maxlen", 256) then
mesecon.queue:add_action(pos, "lc_interrupt", {luac_id, iid}, time, iid, 1)
else
send_warning("An interrupt ID was too large!")
end
end) end)
end end
return interrupt else
-- use global action queue
-- itbl: Flat table of functions to run after sandbox cleanup, used to prevent various security hazards
get_interrupt = function(pos, itbl, send_warning)
-- iid = interrupt id
local function interrupt(time, iid)
-- NOTE: This runs within string metatable sandbox, so don't *rely* on anything of the form (""):y
-- Hence the values get moved out. Should take less time than original, so totally compatible
if type(time) ~= "number" then error("Delay must be a number") end
table.insert(itbl, function ()
-- Outside string metatable sandbox, can safely run this now
local luac_id = minetest.get_meta(pos):get_int("luac_id")
-- Check if IID is dodgy, so you can't use interrupts to store an infinite amount of data.
-- Note that this is safe from alter-after-free because this code gets run after the sandbox has ended.
-- This runs outside of the timer and *shouldn't* harm perf. unless dodgy data is being sent in the first place
iid = remove_functions(iid)
local msg_ser = minetest.serialize(iid)
if #msg_ser <= mesecon.setting("luacontroller_interruptid_maxlen", 256) then
mesecon.queue:add_action(pos, "lc_interrupt", {luac_id, iid}, time, iid, 1)
else
send_warning("An interrupt ID was too large!")
end
end)
end
return interrupt
end
end end
-- Given a message object passed to digiline_send, clean it up into a form -- Given a message object passed to digiline_send, clean it up into a form
-- which is safe to transmit over the network and compute its "cost" (a very -- which is safe to transmit over the network and compute its "cost" (a very
-- rough estimate of its memory usage). -- rough estimate of its memory usage).
...@@ -414,7 +427,6 @@ local function get_digiline_send(pos, itbl, send_warning) ...@@ -414,7 +427,6 @@ local function get_digiline_send(pos, itbl, send_warning)
end end
end end
local safe_globals = { local safe_globals = {
-- Don't add pcall/xpcall unless willing to deal with the consequences (unless very careful, incredibly likely to allow killing server indirectly) -- Don't add pcall/xpcall unless willing to deal with the consequences (unless very careful, incredibly likely to allow killing server indirectly)
"assert", "error", "ipairs", "next", "pairs", "select", "assert", "error", "ipairs", "next", "pairs", "select",
...@@ -619,12 +631,13 @@ local function reset_formspec(meta, code, errmsg) ...@@ -619,12 +631,13 @@ local function reset_formspec(meta, code, errmsg)
meta:mark_as_private("code") meta:mark_as_private("code")
code = minetest.formspec_escape(code or "") code = minetest.formspec_escape(code or "")
errmsg = minetest.formspec_escape(tostring(errmsg or "")) errmsg = minetest.formspec_escape(tostring(errmsg or ""))
meta:set_string("formspec", "size[12,10]".. meta:set_string("formspec", "size[12,10]"
"background[-0.2,-0.25;12.4,10.75;jeija_luac_background.png]".. .."background[-0.2,-0.25;12.4,10.75;jeija_luac_background.png]"
"textarea[0.2,0.2;12.2,9.5;code;;"..code.."]".. .."label[0.1,8.3;"..errmsg.."]"
"image_button[4.75,8.75;2.5,1;jeija_luac_runbutton.png;program;]".. .."textarea[0.2,0.2;12.2,9.5;code;;"..code.."]"
"image_button_exit[11.72,-0.25;0.425,0.4;jeija_close_window.png;exit;]".. .."image_button[4.75,8.75;2.5,1;jeija_luac_runbutton.png;program;]"
"label[0.1,9;"..errmsg.."]") .."image_button_exit[11.72,-0.25;0.425,0.4;jeija_close_window.png;exit;]"
)
end end
local function reset_meta(pos, code, errmsg) local function reset_meta(pos, code, errmsg)
...@@ -650,6 +663,14 @@ local function reset(pos) ...@@ -650,6 +663,14 @@ local function reset(pos)
set_port_states(pos, {a=false, b=false, c=false, d=false}) set_port_states(pos, {a=false, b=false, c=false, d=false})
end end
local function node_timer(pos)
if minetest.registered_nodes[minetest.get_node(pos).name].is_burnt then
return false
end
run(pos, {type="interrupt"})
return false
end
----------------------- -----------------------
-- A.Queue callbacks -- -- A.Queue callbacks --
----------------------- -----------------------
...@@ -822,6 +843,7 @@ for d = 0, 1 do ...@@ -822,6 +843,7 @@ for d = 0, 1 do
mesecon.receptor_off(pos, output_rules) mesecon.receptor_off(pos, output_rules)
end, end,
is_luacontroller = true, is_luacontroller = true,
on_timer = node_timer,
on_blast = mesecon.on_blastnode, on_blast = mesecon.on_blastnode,
}) })
end end
......
...@@ -571,6 +571,7 @@ yc.command_parsecondition = function(cond, L, eeprom) ...@@ -571,6 +571,7 @@ yc.command_parsecondition = function(cond, L, eeprom)
if cond:sub(i+1, i+1) == nil then break end if cond:sub(i+1, i+1) == nil then break end
if s == "&" then if s == "&" then
if a==nil then return nil end if a==nil then return nil end
if b==nil then return nil end
local buf = ((a==1) and (b==1)) local buf = ((a==1) and (b==1))
if buf == true then buf = "1" end if buf == true then buf = "1" end
if buf == false then buf = "0" end if buf == false then buf = "0" end
...@@ -580,6 +581,7 @@ yc.command_parsecondition = function(cond, L, eeprom) ...@@ -580,6 +581,7 @@ yc.command_parsecondition = function(cond, L, eeprom)
end end
if s == "|" then if s == "|" then
if a==nil then return nil end if a==nil then return nil end
if b==nil then return nil end
local buf = ((a == 1) or (b == 1)) local buf = ((a == 1) or (b == 1))
if buf == true then buf = "1" end if buf == true then buf = "1" end
if buf == false then buf = "0" end if buf == false then buf = "0" end
...@@ -589,6 +591,7 @@ yc.command_parsecondition = function(cond, L, eeprom) ...@@ -589,6 +591,7 @@ yc.command_parsecondition = function(cond, L, eeprom)
end end
if s == "~" then if s == "~" then
if a==nil then return nil end if a==nil then return nil end
if b==nil then return nil end
local buf = (((a == 1) or (b == 1)) and not((a==1) and (b==1))) local buf = (((a == 1) or (b == 1)) and not((a==1) and (b==1)))
if buf == true then buf = "1" end if buf == true then buf = "1" end
if buf == false then buf = "0" end if buf == false then buf = "0" end
......
Movestones are effectors that push the blocks in front of them. They move along on the right side of a mesecon wire track. Movestones are effectors that push the blocks in front of them. They move along on the right side of a mesecon wire track.
A movestone trying to move into, or push other nodes into, an unloaded block doesn't move.
Movestones are effectors that push the blocks in front of them. They move along on the right side of a mesecon wire track. Sticky ones also pull blocks. Movestones are effectors that push the blocks in front of them. They move along on the right side of a mesecon wire track. Sticky ones also pull blocks.
A sticky movestone trying to move into, or push other nodes into, an unloaded block doesn't move.
A sticky movestone trying to pull nodes from an unloaded block moves but leaves them behind.
...@@ -55,8 +55,6 @@ end ...@@ -55,8 +55,6 @@ end
-- tests if the node can be pushed into, e.g. air, water, grass -- tests if the node can be pushed into, e.g. air, water, grass
local function node_replaceable(name) local function node_replaceable(name)
if name == "ignore" then return true end
if minetest.registered_nodes[name] then if minetest.registered_nodes[name] then
return minetest.registered_nodes[name].buildable_to or false return minetest.registered_nodes[name].buildable_to or false
end end
...@@ -260,6 +258,10 @@ function mesecon.mvps_move_objects(pos, dir, nodestack, movefactor) ...@@ -260,6 +258,10 @@ function mesecon.mvps_move_objects(pos, dir, nodestack, movefactor)
end end
end end
-- Never push into unloaded blocks. Don’t try to pull from them, either.
-- TODO: load blocks instead, as with wires.
mesecon.register_mvps_stopper("ignore")
mesecon.register_mvps_stopper("doors:door_steel_b_1") mesecon.register_mvps_stopper("doors:door_steel_b_1")
mesecon.register_mvps_stopper("doors:door_steel_t_1") mesecon.register_mvps_stopper("doors:door_steel_t_1")
mesecon.register_mvps_stopper("doors:door_steel_b_2") mesecon.register_mvps_stopper("doors:door_steel_b_2")
......
Pistons are effectors, they push up to 20 blocks in front of them. The push direction can be set by placing them from different angles. Pistons are effectors, they push up to 20 blocks in front of them. The push direction can be set by placing them from different angles.
A piston pointing into an unloaded block won't extend.
A piston retracting from an unloaded block works, but gravity-sensitive nodes above the empty space may not fall.
Sticky pistons are effectors, they push up to 20 blocks in front of them. The push direction can be set by placing them from different angles. Sticky ones also pull 1 block. Sticky pistons are effectors, they push up to 20 blocks in front of them. The push direction can be set by placing them from different angles. Sticky ones also pull 1 block.
A sticky piston pointing into an unloaded block won't extend.
A sticky piston retracting from within an unloaded block works and pulls a node, but if it doesn't pull anything, then gravity-sensitive nodes above the empty space may not fall.
An extended sticky piston that touches the surface of an unloaded block and loses signal retracts but doesn't pull anything.
...@@ -55,7 +55,7 @@ local function piston_get_rules(node) ...@@ -55,7 +55,7 @@ local function piston_get_rules(node)
return rules return rules
end end
local function piston_remove_pusher(pos, node) local function piston_remove_pusher(pos, node, check_falling)
local pistonspec = get_pistonspec(node.name, "onname") local pistonspec = get_pistonspec(node.name, "onname")
local dir = vector.multiply(minetest.facedir_to_dir(node.param2), -1) local dir = vector.multiply(minetest.facedir_to_dir(node.param2), -1)
local pusherpos = vector.add(pos, dir) local pusherpos = vector.add(pos, dir)
...@@ -72,7 +72,14 @@ local function piston_remove_pusher(pos, node) ...@@ -72,7 +72,14 @@ local function piston_remove_pusher(pos, node)
max_hear_distance = 20, max_hear_distance = 20,
gain = 0.3, gain = 0.3,
}) })
minetest.check_for_falling(pusherpos)
if check_falling then
minetest.check_for_falling(pusherpos)
end
end
local function piston_after_dig(pos, node)
piston_remove_pusher(pos, node, true)
end end
local piston_on = function(pos, node) local piston_on = function(pos, node)
...@@ -97,7 +104,7 @@ end ...@@ -97,7 +104,7 @@ end
local function piston_off(pos, node) local function piston_off(pos, node)
local pistonspec = get_pistonspec(node.name, "onname") local pistonspec = get_pistonspec(node.name, "onname")
minetest.set_node(pos, {param2 = node.param2, name = pistonspec.offname}) minetest.set_node(pos, {param2 = node.param2, name = pistonspec.offname})
piston_remove_pusher(pos, node) piston_remove_pusher(pos, node, not pistonspec.sticky)
if not pistonspec.sticky then if not pistonspec.sticky then
return return
...@@ -293,7 +300,7 @@ minetest.register_node("mesecons_pistons:piston_normal_on", { ...@@ -293,7 +300,7 @@ minetest.register_node("mesecons_pistons:piston_normal_on", {
paramtype2 = "facedir", paramtype2 = "facedir",
is_ground_content = false, is_ground_content = false,
drop = "mesecons_pistons:piston_normal_off", drop = "mesecons_pistons:piston_normal_off",
after_dig_node = piston_remove_pusher, after_dig_node = piston_after_dig,
node_box = piston_on_box, node_box = piston_on_box,
selection_box = piston_on_box, selection_box = piston_on_box,
sounds = default.node_sound_wood_defaults(), sounds = default.node_sound_wood_defaults(),
...@@ -371,7 +378,7 @@ minetest.register_node("mesecons_pistons:piston_sticky_on", { ...@@ -371,7 +378,7 @@ minetest.register_node("mesecons_pistons:piston_sticky_on", {
paramtype2 = "facedir", paramtype2 = "facedir",
is_ground_content = false, is_ground_content = false,
drop = "mesecons_pistons:piston_sticky_off", drop = "mesecons_pistons:piston_sticky_off",
after_dig_node = piston_remove_pusher, after_dig_node = piston_after_dig,
node_box = piston_on_box, node_box = piston_on_box,
selection_box = piston_on_box, selection_box = piston_on_box,
sounds = default.node_sound_wood_defaults(), sounds = default.node_sound_wood_defaults(),
......
A power plant is a receptor that is always turned on: It provides energy. A power plant is a receptor that is always turned on: it provides energy.
It continues to work in an unloaded block.
This receptor turns on if there's an object above it. And object can be a player, an item, a mob... This receptor turns on if there's an object above it. An object can be a player, an item, a mob...
This receptor turns on if there's an object above it. And object can be a player, an item, a mob... This receptor turns on if there's an object above it. An object can be a player, an item, a mob...
Ghoststones disappear when powered, just like Removestones. But in contrast to Removestones, they Reappear again when not powered anymore and they are also conductive. Ghoststones disappear when powered, just like Removestones. But in contrast to Removestones, they reappear again when not powered anymore and they are also conductive.
They work in inactive blocks.
Removestones are propably the simplest effectors possible. They simply disappear when powered. Removestones are probably the simplest effectors possible. They simply disappear when powered.
They work in inactive blocks.