Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 40 additions & 9 deletions mesecons_luacontroller/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
-- ports = get_real_port_states(pos): gets if inputs are powered from outside
-- newport = merge_port_states(state1, state2): just does result = state1 or state2 for every port
-- set_port(pos, rule, state): activates/deactivates the mesecons according to the port states
-- set_port_states(pos, ports): Applies new port states to a Luacontroller at pos
-- set_port_states(pos, ports, ignore_overheat): Applies new port states to a Luacontroller at pos
-- run_inner(pos, code, event): runs code on the controller at pos and event
-- reset_formspec(pos, code, errmsg): installs new code and prints error messages, without resetting LCID
-- reset_meta(pos, code, errmsg): performs a software-reset, installs new code and prints error message
-- run(pos, event): a wrapper for run_inner which gets code & handles errors via reset_meta
-- resetn(pos): performs a hardware reset, turns off all ports
-- reset(pos): performs a hardware reset, turns off all ports
--
-- The Sandbox
-- The whole code of the controller runs in a sandbox,
Expand Down Expand Up @@ -129,10 +129,15 @@ local function clean_port_states(ports)
ports.d = ports.d and true or false
end

local is_controller_burnt

local function set_port_states(pos, ports)
local function set_port_states(pos, ports, ignore_overheat)
local node = minetest.get_node(pos)
local name = node.name
if not ignore_overheat and is_controller_burnt(name) then
return -- Avoid swapping back to a non-burnt node
end

clean_port_states(ports)
local vports = minetest.registered_nodes[name].virtual_portstates
local new_name = generate_name(ports)
Expand Down Expand Up @@ -168,6 +173,16 @@ end
-----------------
-- Overheating --
-----------------

-- Overheating factor of deferred tasks (e.g. digilines).
-- Higher values result in faster overheating.
-- See also: settings 'overheat_max' and 'cooldown_time'
local TASK_HEAT_FACTOR = 0.8

is_controller_burnt = function(node_name)
return node_name == (BASENAME .. "_burnt")
end

local function burn_controller(pos)
local node = minetest.get_node(pos)
node.name = BASENAME.."_burnt"
Expand All @@ -178,6 +193,10 @@ local function burn_controller(pos)
end

local function overheat(pos)
if is_controller_burnt(core.get_node(pos).name) then
-- Avoid spamming "Node overheats" log messages.
return true
end
if mesecon.do_overheat(pos) then -- If too hot
burn_controller(pos)
return true
Expand Down Expand Up @@ -450,6 +469,10 @@ local function get_digiline_send(pos, itbl, send_warning)
local chan_maxlen = mesecon.setting("luacontroller_digiline_channel_maxlen", 256)
local maxlen = mesecon.setting("luacontroller_digiline_maxlen", 50000)
return function(channel, msg)
if is_controller_burnt(pos) then
return false
end

-- NOTE: This runs within string metatable sandbox, so don't *rely* on anything of the form (""):y
-- or via anything that could.
-- Make sure channel is string, number or boolean
Expand Down Expand Up @@ -677,20 +700,29 @@ local function run_inner(pos, code, event)
-- Save memory. This may burn the luacontroller if a memory overflow occurs.
save_memory(pos, meta, env.mem)

-- Action queues can escape the sandbox, thus give a harsh penalty.
for i = 1, math.floor(#itbl * TASK_HEAT_FACTOR + 0.5) do
if overheat(pos) then
break
end
end

-- Execute deferred tasks
for _, v in ipairs(itbl) do
local failure = v()
if failure then
return false, failure
end
end

return true, warning
end

local function reset_formspec(meta, code, errmsg)
code = code or ""
meta:set_string("code", code)
meta:mark_as_private("code")
code = minetest.formspec_escape(code or "")
code = minetest.formspec_escape(code)
errmsg = minetest.formspec_escape(tostring(errmsg or ""))
meta:set_string("formspec", "size[12,10]"
.."style_type[label,textarea;font=mono]"
Expand Down Expand Up @@ -722,11 +754,11 @@ local function run(pos, event)
end

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}, true)
end

local function node_timer(pos)
if minetest.registered_nodes[minetest.get_node(pos).name].is_burnt then
if is_controller_burnt(core.get_node(pos).name) then
return false
end
run(pos, {type="interrupt"})
Expand All @@ -740,15 +772,15 @@ end
mesecon.queue:add_function("lc_interrupt", function (pos, luac_id, iid)
-- There is no luacontroller anymore / it has been reprogrammed / replaced / burnt
if (minetest.get_meta(pos):get_int("luac_id") ~= luac_id) then return end
if (minetest.registered_nodes[minetest.get_node(pos).name].is_burnt) then return end
if is_controller_burnt(core.get_node(pos).name) then return end
run(pos, {type="interrupt", iid = iid})
end)

mesecon.queue:add_function("lc_digiline_relay", function (pos, channel, luac_id, msg)
if not digiline then return end
-- This check is only really necessary because in case of server crash, old actions can be thrown into the future
if (minetest.get_meta(pos):get_int("luac_id") ~= luac_id) then return end
if (minetest.registered_nodes[minetest.get_node(pos).name].is_burnt) then return end
if is_controller_burnt(core.get_node(pos).name) then return end
-- The actual work
digiline:receptor_send(pos, digiline.rules.default, channel, msg)
end)
Expand Down Expand Up @@ -929,7 +961,6 @@ minetest.register_node(BASENAME .. "_burnt", {
"jeija_microcontroller_sides.png"
},
inventory_image = "jeija_luacontroller_burnt_top.png",
is_burnt = true,
paramtype = "light",
is_ground_content = false,
groups = {dig_immediate=2, not_in_creative_inventory=1},
Expand Down