14 Commits

Author SHA1 Message Date
HybridDog
e1f07e048d Fix timegate's dependency on utils
The addition of dependencies in commit 52fc535d26
was incomplete, so loading the moremesecons_timegate mod can fail.

This change adds the moremesecons_utils dependency to those mods which
require the moremesecons table as reported by luacheck.
I've also made .luacheckrc more strict so that it sets globals per mod
and allows reading the moremesecons table only in mods which also have
the moremesecons_utils dependency.
2025-12-25 12:42:43 +01:00
tour
3c3fc6749a prevent invalid rotations and add on_rotate callbacks 2025-11-15 09:57:45 +01:00
tour
46d1f500bf entity_detactor: detect stacks of dropped items 2025-11-15 09:57:45 +01:00
tour
3b0d06569d rewrite timegate to make use of nodetimers
- fixes a crash with timer=NaN
- fixes 1-tick circels with timer=0
- fixes timegates getting stuck when unloaded in on-state
2025-11-15 09:57:45 +01:00
tour
52fc535d26 Add missing dependencies (#41)
Some dependencies are technically not necessary but should help to avoid issues in the future.
2025-07-17 19:22:07 +02:00
tour
3e93b939f3 Validate user inputs (#39) 2024-12-19 19:47:36 +01:00
tour
79b72e8b76 Replace deprecated formspec element 'invsize' with 'size' (#37) 2024-11-22 21:21:37 +01:00
fluxionary
e72790f0eb commandblock: don't allow sending colored text if server forbids it (#28) 2024-05-26 17:39:51 +02:00
tour
93384188d5 Make mesechest more reliable (#35)
The current hack to get on the `on_player_receive_fields` function is quite likely to break with instrumented code.
This also changes the workaround to get custom inventory callbacks by using `minetest.override_item`.
2024-04-30 17:38:20 +02:00
tour
b24feb4073 Add missing formspec escape (#34)
* fix missing formspec escape

* formspec_escape at the correct place
2024-01-23 15:33:02 +01:00
HybridDog
31a98e2c9b Add a GitHub action for luacheck (#33) 2023-08-28 12:19:58 +02:00
fluxionary
cbae2c7f88 Add a distance limit to the entity detector (#32)
The distance limit can be configured with the `moremesecons_entity_detector.max_radius` setting.

An additional change prevents a crash if an object returned by `get_objects_inside_radius` is neither a valid player nor a luaentity. We don't know why this happens but it sometimes does.
2023-08-17 20:42:54 +02:00
luk3yx
464699e78b Fix error in command blocks (#30)
Possibly caused by force-loaded blocks.
2023-04-07 17:20:43 +02:00
fluxionary
13645134a6 Fix comma typos (#27) 2022-11-07 20:00:35 +01:00
22 changed files with 164 additions and 127 deletions

10
.github/workflows/luacheck.yml vendored Normal file
View File

@@ -0,0 +1,10 @@
name: luacheck
on: [push, pull_request]
jobs:
luacheck:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@master
- name: Luacheck
uses: lunarmodules/luacheck@master

View File

@@ -1,22 +1,17 @@
read_globals = {
-- Defined by Minetest
"vector", "PseudoRandom", "VoxelArea", "table",
-- Defined by Luanti
"minetest", "vector", "PseudoRandom", "VoxelArea", "table", "ItemStack",
-- Mods
"digiline", "default", "creative",
-- Required for the mesechest registration
minetest = {
fields = {
register_lbm = {read_only = false},
register_node = {read_only = false},
registered_on_player_receive_fields = {
read_only = false,
other_fields = true,
},
},
other_fields = true
}
"digiline", "default", "creative", "mesecon"
}
globals = {"moremesecons", "mesecon"}
files.moremesecons_utils = {globals = {"moremesecons"}}
local utils_depender = {"adjustable_blinkyplant", "adjustable_player_detector",
"commandblock", "entity_detector", "jammer", "luablock", "playerkiller",
"sayer", "teleporter", "timegate", "wireless"}
for _, folder_suffix in ipairs(utils_depender) do
files["moremesecons_" .. folder_suffix] = {read_globals = {"moremesecons"}}
end
files.moremesecons_jammer.globals = {"mesecon"}
ignore = {"212", "631", "422", "432"}

View File

@@ -1,3 +1,3 @@
name = moremesecons_adjustable_blinkyplant
depends = mesecons,moremesecons_utils
depends = mesecons,moremesecons_utils,default
optional_depends = craft_guide

View File

@@ -2,9 +2,11 @@
-- Detects players in a certain radius
-- The radius can be changes by right-click (by default 6)
local MAX_RADIUS = moremesecons.setting("adjustable_player_detector", "max_radius", 16, 0)
local function make_formspec(meta)
meta:set_string("formspec", "size[9,5]" ..
"field[0.3, 0;9,2;scanname;Coma-separated list of the names of players to scan for (empty for any):;${scanname}]"..
"field[0.3, 0;9,2;scanname;Comma-separated list of the names of players to scan for (empty for any):;${scanname}]"..
"field[0.3,1.5;4,2;digiline_channel;Digiline Channel (optional):;${digiline_channel}]"..
"field[0.3,3;4,2;radius;Detection radius:;${radius}]"..
"button_exit[3.5,3.5;2,3;;Save]")
@@ -36,7 +38,7 @@ local object_detector_scan = function (pos)
local scanname = meta:get_string("scanname")
local scan_all = scanname == ""
local scan_names = scanname:split(',')
local radius = meta:get_int("radius")
local radius = math.min(meta:get_int("radius"), MAX_RADIUS)
if radius <= 0 then
radius = 6
end
@@ -76,11 +78,11 @@ local object_detector_digiline = {
make_formspec(meta)
end
end
if msg.scanname then
if type(msg.scanname) == "string" then
meta:set_string("scanname", msg.scanname)
make_formspec(meta)
end
if msg.command and msg.command == "get" then
if msg.command == "get" then
local found, name = object_detector_scan(pos)
if not found then
name = ""

View File

@@ -1,3 +1,3 @@
name = moremesecons_adjustable_player_detector
depends = mesecons
depends = mesecons,moremesecons_utils,default
optional_depends = craft_guide

View File

@@ -1,10 +1,12 @@
local strip_color_codes = minetest.settings:get_bool("strip_color_codes", false)
local function initialize_data(meta)
local NEAREST_MAX_DISTANCE = moremesecons.setting("commandblock", "nearest_max_distance", 8, 1)
local commands = meta:get_string("commands")
meta:set_string("formspec",
"invsize[9,5;]" ..
"textarea[0.5,0.5;8.5,4;commands;Commands;"..commands.."]" ..
"size[9,5]" ..
"textarea[0.5,0.5;8.5,4;commands;Commands;"..minetest.formspec_escape(commands).."]" ..
"label[1,3.8;@nearest is replaced by the nearest player name ("..tostring(NEAREST_MAX_DISTANCE).." nodes max for the nearest distance)".."]" ..
"button_exit[3.3,4.5;2,1;submit;Submit]")
local owner = meta:get_string("owner")
@@ -46,17 +48,21 @@ local function receive_fields(pos, _, fields, player)
and player:get_player_name() ~= owner then
return
end
meta:set_string("commands", fields.commands)
if strip_color_codes then
meta:set_string("commands", minetest.strip_colors(fields.commands))
else
meta:set_string("commands", fields.commands)
end
initialize_data(meta)
end
local function resolve_commands(commands, pos)
local nearest = nil
local nearest = ""
local min_distance = math.huge
local players = minetest.get_connected_players()
for _, player in pairs(players) do
local distance = vector.distance(pos, player:getpos())
local distance = vector.distance(pos, player:get_pos())
if distance < min_distance then
min_distance = distance
nearest = player:get_player_name()

View File

@@ -1,3 +1,3 @@
name = moremesecons_commandblock
depends = mesecons,moremesecons_utils
depends = mesecons,moremesecons_utils,default
optional_depends = craft_guide

View File

@@ -45,10 +45,11 @@ mesecon.register_node("moremesecons_conductor_signalchanger:conductor_signalchan
inventory_image = "moremesecons_conductor_signalchanger_off.png",
groups = {dig_immediate = 2},
paramtype = "light",
paramtype2 = "facedir",
paramtype2 = "4dir",
drawtype = "nodebox",
selection_box = nodebox,
node_box = nodebox,
on_rotate = mesecon.on_rotate,
},{
groups = {dig_immediate = 2},
mesecons = {

View File

@@ -68,7 +68,7 @@ for n,i in pairs({{0,0},{1,0},{1,1}}) do
inventory_image = top_texture,
wield_image = top_texture,
paramtype = "light",
paramtype2 = "facedir",
paramtype2 = "4dir",
drawtype = "nodebox",
node_box = {
type = "fixed",
@@ -79,6 +79,7 @@ for n,i in pairs({{0,0},{1,0},{1,1}}) do
groups = groups,
tiles = {top_texture, "moremesecons_dual_delayer_bottom.png", "moremesecons_dual_delayer_side_left.png", "moremesecons_dual_delayer_side_right.png", "moremesecons_dual_delayer_ends.png", "moremesecons_dual_delayer_ends.png"},
use_texture_alpha = use_texture_alpha,
on_rotate = mesecon.on_rotate,
mesecons = {
receptor = {
state = mesecon.state.off,

View File

@@ -2,9 +2,11 @@
-- Detects entitys in a certain radius
-- The radius can be changes by right-click (by default 6)
local MAX_RADIUS = moremesecons.setting("entity_detector", "max_radius", 16, 0)
local function make_formspec(meta)
meta:set_string("formspec", "size[9,5]" ..
"field[0.3, 0;9,2;scanname;Coma-separated list of the names (itemstring) of entities to scan for (empty for any):;${scanname}]"..
"field[0.3, 0;9,2;scanname;Comma-separated list of the names (itemstring) of entities to scan for (empty for any):;${scanname}]"..
"field[0.3,1.5;4,2;digiline_channel;Digiline Channel (optional):;${digiline_channel}]"..
"field[0.3,3;2,2;radius;Detection radius:;${radius}]"..
"button_exit[3.5,3.5;2,3;;Save]")
@@ -26,7 +28,7 @@ local function object_detector_on_receive_fields(pos, _, fields, player)
meta:set_string("digiline_channel", fields.digiline_channel)
local r = tonumber(fields.radius)
if r then
meta:set_int("radius", r)
meta:set_int("radius", math.min(r, MAX_RADIUS))
end
end
@@ -36,22 +38,22 @@ local object_detector_scan = function (pos)
local scanname = meta:get_string("scanname")
local scan_all = scanname == ""
local scan_names = scanname:split(',')
local radius = meta:get_int("radius")
if radius == 0 then
radius = 6
end
local radius = math.min(tonumber(meta:get("radius")) or 6, MAX_RADIUS)
for _,obj in pairs(minetest.get_objects_inside_radius(pos, radius)) do
if not obj:is_player() then
local luaentity = obj:get_luaentity()
local luaentity = obj:get_luaentity()
if luaentity then
if scan_all then
return true
end
local isname = luaentity.name
if isname then
if scan_all then
return true
-- If the item is present as dropped item entity:
local bitemname = (isname == "__builtin:item" and ItemStack(luaentity.itemstring):get_name())
for _, name in ipairs(scan_names) do
if name == isname then
return true -- object name matches
end
for _, name in ipairs(scan_names) do
if isname == name or (isname == "__builtin:item" and luaentity.itemstring == name) then
return true
end
if name == bitemname then
return true -- item of the item entity matches
end
end
end
@@ -65,7 +67,7 @@ local object_detector_digiline = {
action = function (pos, node, channel, msg)
local meta = minetest.get_meta(pos)
local active_channel = meta:get_string("digiline_channel")
if channel ~= active_channel then
if channel ~= active_channel or type(msg) ~= "string" then
return
end
meta:set_string("scanname", msg)

View File

@@ -1,3 +1,3 @@
name = moremesecons_entity_detector
depends = mesecons
depends = mesecons,moremesecons_utils,default
optional_depends = craft_guide

View File

@@ -58,6 +58,7 @@ mesecon.register_node("moremesecons_induction_transmitter:induction_transmitter"
{-0.25, -0.25, -0.5, 0.25, 0.25, -0.1875},
},
},
on_rotate = mesecon.on_rotate,
}, {
tiles = {"default_mese_block.png"},
groups = {cracky = 3},

View File

@@ -42,12 +42,13 @@ mesecon.register_node("moremesecons_injector_controller:injector_controller", {
drawtype = "nodebox",
inventory_image = "moremesecons_injector_controller_off.png",
paramtype = "light",
paramtype2 = "facedir",
paramtype2 = "4dir",
node_box = {
type = "fixed",
fixed = {{-8/16, -8/16, -8/16, 8/16, -7/16, 8/16 }},
},
on_timer = on_timer,
on_rotate = mesecon.on_rotate,
},{
tiles = {"moremesecons_injector_controller_off.png", "moremesecons_injector_controller_side.png", "moremesecons_injector_controller_side.png"},
groups = {dig_immediate=2},

View File

@@ -1,2 +1,2 @@
name = moremesecons_luacontroller_tool
depends = mesecons,mesecons_luacontroller,moremesecons_utils
depends = mesecons,mesecons_luacontroller

View File

@@ -8,59 +8,8 @@ local function mesechest_get_output_rules(node)
return rules
end
local open_chests = {}
-- Override minetest.register_node so it adds a prefix ":"
local old_minetest_register_node = minetest.register_node
minetest.register_node = function(name, def)
local old_on_metadata_inventory_put = def.on_metadata_inventory_put
local old_on_metadata_inventory_take = def.on_metadata_inventory_take
local old_on_rightclick = def.on_rightclick
def.on_metadata_inventory_put = function(pos, ...)
old_on_metadata_inventory_put(pos, ...)
mesecon.receptor_on(pos, {mesechest_get_output_rules(minetest.get_node(pos))[2]})
minetest.after(1, function(pos)
mesecon.receptor_off(pos, {mesechest_get_output_rules(minetest.get_node(pos))[2]})
end, pos)
end
def.on_metadata_inventory_take = function(pos, ...)
old_on_metadata_inventory_take(pos, ...)
mesecon.receptor_on(pos, {mesechest_get_output_rules(minetest.get_node(pos))[3]})
minetest.after(1, function(pos)
mesecon.receptor_off(pos, {mesechest_get_output_rules(minetest.get_node(pos))[3]})
end, pos)
end
def.on_rightclick = function(pos, node, clicker, ...)
if old_on_rightclick(pos, node, clicker, ...) == nil then
mesecon.receptor_on(pos, {mesechest_get_output_rules(node)[1]})
open_chests[clicker:get_player_name()] = pos
end
end
old_minetest_register_node(":"..name, def)
end
local old_minetest_register_lbm = minetest.register_lbm
minetest.register_lbm = function() end
-- Get the on_player_receive_fields function. That's a huge hack
for i, f in ipairs(minetest.registered_on_player_receive_fields) do
local serialized = minetest.serialize(f)
if string.find(serialized, "default:chest") then
minetest.registered_on_player_receive_fields[i] = function(player, formname, fields)
if f(player, formname, fields) == true then
local pn = player:get_player_name()
if open_chests[pn] then
mesecon.receptor_off(open_chests[pn], {mesechest_get_output_rules(minetest.get_node(open_chests[pn]))[1]})
open_chests[pn] = nil
end
end
end
break
end
end
-- default.chest.register_chest() doesn't allow to register most of the callbacks we need
-- we have to override the chest node we registered again
default.chest.register_chest("moremesecons_mesechest:mesechest", {
description = "Mese Chest",
tiles = {
@@ -104,8 +53,55 @@ default.chest.register_chest("moremesecons_mesechest:mesechest_locked", {
}
})
minetest.register_node = old_minetest_register_node
minetest.register_lbm = old_minetest_register_lbm
local moremesecons_chests = {}
for _, chest in ipairs({"moremesecons_mesechest:mesechest", "moremesecons_mesechest:mesechest_locked",
"moremesecons_mesechest:mesechest_open", "moremesecons_mesechest:mesechest_locked_open"}) do
local old_def = minetest.registered_nodes[chest]
local old_on_metadata_inventory_put = old_def.on_metadata_inventory_put
local old_on_metadata_inventory_take = old_def.on_metadata_inventory_take
local old_on_rightclick = old_def.on_rightclick
local override = {}
override.on_metadata_inventory_put = function(pos, ...)
old_on_metadata_inventory_put(pos, ...)
mesecon.receptor_on(pos, {mesechest_get_output_rules(minetest.get_node(pos))[2]})
minetest.after(1, function(pos)
mesecon.receptor_off(pos, {mesechest_get_output_rules(minetest.get_node(pos))[2]})
end, pos)
end
override.on_metadata_inventory_take = function(pos, ...)
old_on_metadata_inventory_take(pos, ...)
mesecon.receptor_on(pos, {mesechest_get_output_rules(minetest.get_node(pos))[3]})
minetest.after(1, function(pos)
mesecon.receptor_off(pos, {mesechest_get_output_rules(minetest.get_node(pos))[3]})
end, pos)
end
override.on_rightclick = function(pos, node, clicker, ...)
if old_on_rightclick(pos, node, clicker, ...) == nil then
mesecon.receptor_on(pos, {mesechest_get_output_rules(node)[1]})
end
end
minetest.override_item(chest, override)
moremesecons_chests[chest] = true
end
-- if the chest is getting closed, turn the signal off
-- luacheck: ignore 122
local old_lid_close = default.chest.chest_lid_close
function default.chest.chest_lid_close(pn)
local pos = default.chest.open_chests[pn].pos
-- old_lid_close will return true if the chest won't be closed
if old_lid_close(pn) then
return true
end
local node = minetest.get_node(pos)
if moremesecons_chests[node.name] then
mesecon.receptor_off(pos, {mesechest_get_output_rules(node)[1]})
end
end
minetest.register_craft({
output = "moremesecons_mesechest:mesechest",

View File

@@ -1,3 +1,3 @@
name = moremesecons_playerkiller
depends = mesecons,mesecons_materials,moremesecons_utils
depends = mesecons,mesecons_materials,moremesecons_utils,default
optional_depends = craft_guide

View File

@@ -41,10 +41,11 @@ mesecon.register_node("moremesecons_signalchanger:signalchanger", {
inventory_image = "moremesecons_signalchanger_off.png",
groups = {dig_immediate = 2},
paramtype = "light",
paramtype2 = "facedir",
paramtype2 = "4dir",
drawtype = "nodebox",
selection_box = nodebox,
node_box = nodebox,
on_rotate = mesecon.on_rotate,
},{
groups = {dig_immediate = 2},
mesecons = {

View File

@@ -1,3 +1,3 @@
name = moremesecons_teleporter
depends = mesecons,moremesecons_utils
depends = mesecons,moremesecons_utils,default
optional_depends = craft_guide

View File

@@ -1,3 +1,5 @@
local MIN_DELAY = moremesecons.setting("timegate", "min_delay", 0.5)
local timegate_get_output_rules = function(node)
local rules = {{x = 0, y = 0, z = 1}}
for _ = 0, node.param2 do
@@ -14,25 +16,28 @@ local timegate_get_input_rules = function(node)
return rules
end
-- Functions that are called after the delay time
local function turnoff(pos, _, node)
if mesecon.is_receptor_on(node.name) then
node.name = "moremesecons_timegate:timegate_off"
minetest.swap_node(pos, node)
mesecon.receptor_off(pos)
end
end
local function timegate_activate(pos, node)
local function turnon(pos, node)
-- using a meta string allows writing the time in hexadecimals
local time = tonumber(minetest.get_meta(pos):get_string("time"))
if not time then
if not (time and time >= MIN_DELAY) then
return
end
node.name = "moremesecons_timegate:timegate_on"
minetest.swap_node(pos, node)
mesecon.receptor_on(pos)
minetest.after(time, function()
local node = minetest.get_node(pos)
if node.name == "moremesecons_timegate:timegate_on" then
mesecon.receptor_off(pos)
node.name = "moremesecons_timegate:timegate_off"
minetest.swap_node(pos, node)
end
end)
-- restart the timer when the input was turned off and on again
minetest.get_node_timer(pos):start(time)
if mesecon.is_receptor_off(node.name) then
node.name = "moremesecons_timegate:timegate_on"
minetest.swap_node(pos, node)
mesecon.receptor_on(pos)
end
end
local boxes = {{ -6/16, -8/16, -6/16, 6/16, -7/16, 6/16 }, -- the main slab
@@ -66,7 +71,7 @@ mesecon.register_node("moremesecons_timegate:timegate", {
fixed = boxes
},
paramtype = "light",
paramtype2 = "facedir",
paramtype2 = "4dir",
sunlight_propagates = true,
is_ground_content = true,
sounds = default.node_sound_stone_defaults(),
@@ -78,7 +83,9 @@ mesecon.register_node("moremesecons_timegate:timegate", {
and not minetest.is_protected(pos, player:get_player_name()) then
minetest.get_meta(pos):set_string("time", fields.time)
end
end
end,
on_timer = turnoff,
on_rotate = mesecon.on_rotate
},{
tiles = {
"moremesecons_timegate_off.png",
@@ -99,7 +106,7 @@ mesecon.register_node("moremesecons_timegate:timegate", {
effector =
{
rules = timegate_get_input_rules,
action_on = timegate_activate
action_on = turnon
}
},
},{
@@ -120,6 +127,7 @@ mesecon.register_node("moremesecons_timegate:timegate", {
},
effector = {
rules = timegate_get_input_rules,
action_on = turnon
}
},
})

View File

@@ -1,3 +1,3 @@
name = moremesecons_timegate
depends = mesecons
depends = mesecons,moremesecons_utils,default
optional_depends = craft_guide

View File

@@ -1,3 +1,3 @@
name = moremesecons_wireless
depends = mesecons,moremesecons_utils
depends = mesecons,moremesecons_utils,default
optional_depends = digilines,craft_guide

View File

@@ -3,6 +3,10 @@
# Minimal interval authorized. Any lower will be set to it.
moremesecons_adjustable_blinky_plant.min_interval (Minimum Interval) float 0.5
[Adjustable Player Detector]
moremesecons_adjustable_player_detector.max_radius (Maximum adjustable player detector radius) float 16 0
[Craftable Commandblock]
# Space-separated list of authorized commands
@@ -13,6 +17,10 @@ moremesecons_commandblock.authorized_commands (Authorized commands) string tell
# Any value less than or equal to 0 will be changed to 1 and a NaN value will be changed to the default value
moremesecons_commandblock.nearest_max_distance (Nearest player maximum distance) float 8
[Entity Detector]
moremesecons_entity_detector.max_radius (Maximum entity detector radius) float 16 0
[Signal Jammer]
# Jammer action range
@@ -61,6 +69,11 @@ moremesecons_teleporter.max_p2t_distance (Maximum Player To Teleporter distance)
# from an older version which did not use it.
moremesecons_teleporter.enable_lbm (Enable Registration LBM) bool false
[Timegate]
# Minimum authorized length for the timegate signal. Timegates with a shorter time will not respond.
moremesecons_timegate.min_delay (Minimum timegate delay) float 0.5
[Wireless]
# Whether to enable the wireless jammer node