make mcl_util.call_safe and use it to make sure that mesecon devices are isolated from each other and can't crash the server, handle powered solid blocks, increase powered on to 16

This commit is contained in:
teknomunk 2024-04-14 20:35:38 +00:00
parent 6107bba52f
commit e970a5f414
4 changed files with 61 additions and 25 deletions

View file

@ -1225,3 +1225,16 @@ function mcl_util.metadata_last_act(meta, name, delay)
return true return true
end end
-- Call a function safely and provide a backtrace on error
function mcl_util.call_safe(label, func, args)
local function caller()
return func(unpack(args))
end
local ok,ret = xpcall(caller, debug.traceback)
if not ok then
minetest.log("error",(label or "")..ret)
end
return ok,ret
end

View file

@ -4,6 +4,9 @@ local modpath = minetest.get_modpath(modname)
vl_scheduler = {} vl_scheduler = {}
local mod = vl_scheduler local mod = vl_scheduler
-- Imports
local call_safe = mcl_util.call_safe
dofile(modpath.."/queue.lua") dofile(modpath.."/queue.lua")
dofile(modpath.."/fifo.lua") dofile(modpath.."/fifo.lua")
dofile(modpath.."/test.lua") dofile(modpath.."/test.lua")
@ -129,13 +132,10 @@ local function run_scheduler(dtime)
local func = functions[task.fid] local func = functions[task.fid]
if func then if func then
--print("Running task "..dump(task)..",func="..dump(func)) --print("Running task "..dump(task)..",func="..dump(func))
local function caller() local ok,ret = call_safe(
return func.func(task, unpack(task.args or {})) "Error while running task "..func.name..": ",
end func.func, {task, unpack(task.args or {})}
local ok,ret = xpcall(caller, debug.traceback) )
if not ok then
minetest.log("error","Error while running task "..func.name..": "..tostring(ret))
end
-- If the task was returned, reschedule it -- If the task was returned, reschedule it
if ret == task then if ret == task then

View file

@ -52,7 +52,7 @@ minetest.register_node("mesecons_walllever:wall_lever_off", {
_doc_items_usagehelp = S("Use the lever to flip it on or off."), _doc_items_usagehelp = S("Use the lever to flip it on or off."),
on_rightclick = function(pos, node) on_rightclick = function(pos, node)
minetest.swap_node(pos, {name="mesecons_walllever:wall_lever_on", param2=node.param2}) minetest.swap_node(pos, {name="mesecons_walllever:wall_lever_on", param2=node.param2})
vl_redstone.set_power(pos, 15) vl_redstone.set_power(pos, 16)
minetest.sound_play("mesecons_button_push", {pos=pos, max_hear_distance=16}, true) minetest.sound_play("mesecons_button_push", {pos=pos, max_hear_distance=16}, true)
end, end,
node_placement_prediction = "", node_placement_prediction = "",

View file

@ -36,17 +36,18 @@ local function update_sink(pos)
local last_strength = meta:get_int(REDSTONE_POWER_META_LAST_STATE) local last_strength = meta:get_int(REDSTONE_POWER_META_LAST_STATE)
if last_strength ~= strength then if last_strength ~= strength then
print("Updating "..node.name.." at "..vector.to_string(pos).."("..tostring(last_strength).."->"..tostring(strength)..")")
-- Inform the node of changes -- Inform the node of changes
if strength > 0 then if strength > 0 then
-- Handle activation -- Handle activation
if sink.action_on then if sink.action_on then
sink.action_on(pos, node) mcl_util.call_safe(nil, sink.action_on, {pos, node})
end end
else else
-- Handle deactivation -- Handle deactivation
if sink.action_off then if sink.action_off then
sink.action_off(pos, node) mcl_util.call_safe(nil, sink.action_off, {pos, node})
end end
end end
@ -57,28 +58,52 @@ local function update_sink(pos)
end end
end end
local function get_positions_from_node_rules(pos, rules_type, list) local POWERED_BLOCK_RULES = {
vector.new( 1, 0, 0),
vector.new(-1, 0, 0),
vector.new( 0, 1, 0),
vector.new( 0,-1, 0),
vector.new( 0, 0, 1),
vector.new( 0, 0,-1),
}
local function get_positions_from_node_rules(pos, rules_type, list, powered)
list = list or {} list = list or {}
local node = minetest.get_node(pos) local node = minetest.get_node(pos)
local nodedef = minetest.registered_nodes[node.name] local nodedef = minetest.registered_nodes[node.name]
if not nodedef.mesecons then return list end local rules
if nodedef.mesecons then
-- Get mesecons rules -- Get mesecons rules
if not nodedef.mesecons[rules_type] then if not nodedef.mesecons[rules_type] then
minetest.log("info","Node "..node.name.." has no mesecons."..rules_type.." rules") minetest.log("info","Node "..node.name.." has no mesecons."..rules_type.." rules")
return list return list
end end
local rules = nodedef.mesecons[rules_type].rules rules = nodedef.mesecons[rules_type].rules
if type(rules) == "function" then rules = rules(node) end if type(rules) == "function" then rules = rules(node) end
else
-- The only blocks that don't support mesecon that propagate power are solid blocks that
-- are powered by another device. Mesecons calls this 'spread'
if not powered[vector.to_string(pos)] then return list end
if minetest.get_item_group(node.name,"solid") == 0 then return list end
--print("rules="..dump(rules)) rules = POWERED_BLOCK_RULES
end
print("rules="..dump(rules))
-- Convert to absolute positions -- Convert to absolute positions
for i=1,#rules do for i=1,#rules do
local next_pos = vector.add(pos, rules[i]) local next_pos = vector.add(pos, rules[i])
local next_pos_str = vector.to_string(next_pos) local next_pos_str = vector.to_string(next_pos)
print("\tnext: "..next_pos_str..", prev="..tostring(list[next_pos_str]))
list[next_pos_str] = true list[next_pos_str] = true
-- Power solid blocks
if rules[i].spread then
powered[next_pos_str] = true
print("powering "..next_pos_str)
end
end end
return list return list
@ -87,6 +112,7 @@ end
vl_scheduler.register_function("vl_redstone:flow_power",function(task, source_pos, strength, distance) vl_scheduler.register_function("vl_redstone:flow_power",function(task, source_pos, strength, distance)
print("Flowing lv"..tostring(strength).." power from "..vector.to_string(source_pos).." for "..tostring(distance).." blocks") print("Flowing lv"..tostring(strength).." power from "..vector.to_string(source_pos).." for "..tostring(distance).." blocks")
local processed = {} local processed = {}
local powered = {}
local source_pos_str = vector.to_string(source_pos) local source_pos_str = vector.to_string(source_pos)
-- Update the source node's redstone power -- Update the source node's redstone power
@ -95,8 +121,8 @@ vl_scheduler.register_function("vl_redstone:flow_power",function(task, source_po
-- Get rules -- Get rules
local list = {} local list = {}
get_positions_from_node_rules(source_pos, "receptor", list) get_positions_from_node_rules(source_pos, "receptor", list, powered)
print("initial list="..dump(list)) --print("initial list="..dump(list))
for i=1,distance do for i=1,distance do
local next_list = {} local next_list = {}
@ -117,10 +143,7 @@ vl_scheduler.register_function("vl_redstone:flow_power",function(task, source_po
print("pos="..vector.to_string(pos)..", strength="..tostring(strength)) print("pos="..vector.to_string(pos)..", strength="..tostring(strength))
-- handle spread -- handle spread
local spread_to = get_positions_from_node_rules(pos, "conductor") get_positions_from_node_rules(pos, "conductor", next_list, powered)
for j=1,#spread_to do
next_list[vector.to_string(spread_to[j])] = true
end
-- Update the position -- Update the position
update_sink(pos) update_sink(pos)