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
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 = {}
local mod = vl_scheduler
-- Imports
local call_safe = mcl_util.call_safe
dofile(modpath.."/queue.lua")
dofile(modpath.."/fifo.lua")
dofile(modpath.."/test.lua")
@ -129,13 +132,10 @@ local function run_scheduler(dtime)
local func = functions[task.fid]
if func then
--print("Running task "..dump(task)..",func="..dump(func))
local function caller()
return func.func(task, unpack(task.args or {}))
end
local ok,ret = xpcall(caller, debug.traceback)
if not ok then
minetest.log("error","Error while running task "..func.name..": "..tostring(ret))
end
local ok,ret = call_safe(
"Error while running task "..func.name..": ",
func.func, {task, unpack(task.args or {})}
)
-- If the task was returned, reschedule it
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."),
on_rightclick = function(pos, node)
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)
end,
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)
if last_strength ~= strength then
print("Updating "..node.name.." at "..vector.to_string(pos).."("..tostring(last_strength).."->"..tostring(strength)..")")
-- Inform the node of changes
if strength > 0 then
-- Handle activation
if sink.action_on then
sink.action_on(pos, node)
mcl_util.call_safe(nil, sink.action_on, {pos, node})
end
else
-- Handle deactivation
if sink.action_off then
sink.action_off(pos, node)
mcl_util.call_safe(nil, sink.action_off, {pos, node})
end
end
@ -57,28 +58,52 @@ local function update_sink(pos)
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 {}
local node = minetest.get_node(pos)
local nodedef = minetest.registered_nodes[node.name]
if not nodedef.mesecons then return list end
local rules
if nodedef.mesecons then
-- Get mesecons rules
if not nodedef.mesecons[rules_type] then
minetest.log("info","Node "..node.name.." has no mesecons."..rules_type.." rules")
return list
end
rules = nodedef.mesecons[rules_type].rules
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
-- Get mesecons rules
if not nodedef.mesecons[rules_type] then
minetest.log("info","Node "..node.name.." has no mesecons."..rules_type.." rules")
return list
rules = POWERED_BLOCK_RULES
end
local rules = nodedef.mesecons[rules_type].rules
if type(rules) == "function" then rules = rules(node) end
--print("rules="..dump(rules))
print("rules="..dump(rules))
-- Convert to absolute positions
for i=1,#rules do
local next_pos = vector.add(pos, rules[i])
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
-- Power solid blocks
if rules[i].spread then
powered[next_pos_str] = true
print("powering "..next_pos_str)
end
end
return list
@ -87,6 +112,7 @@ end
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")
local processed = {}
local powered = {}
local source_pos_str = vector.to_string(source_pos)
-- 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
local list = {}
get_positions_from_node_rules(source_pos, "receptor", list)
print("initial list="..dump(list))
get_positions_from_node_rules(source_pos, "receptor", list, powered)
--print("initial list="..dump(list))
for i=1,distance do
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))
-- handle spread
local spread_to = get_positions_from_node_rules(pos, "conductor")
for j=1,#spread_to do
next_list[vector.to_string(spread_to[j])] = true
end
get_positions_from_node_rules(pos, "conductor", next_list, powered)
-- Update the position
update_sink(pos)