Compare commits

...

8 Commits

Author SHA1 Message Date
SmallJoker
0fed137824 Luacontroller: Add overheat factor for deferred tasks 2025-11-09 14:28:39 +01:00
sfence
e2fc73da98 Support for Hades Revisited (#710) 2025-06-25 21:22:06 +02:00
SX
f24429b9f7 Switch to mineunit-actions (#708) 2025-06-12 09:28:11 +02:00
The4codeblocks
27072542cf Replace on_rotate_horiz with on_rotate and paramtype2 = "4dir"
This raises the minimal Luanti/Minetest version to 5.7.0.
2025-05-17 22:51:06 +02:00
sfan5
b79962402b Try to fix mineunit in CI 2025-05-03 22:36:14 +02:00
sfan5
49879ab13c Update README for Luanti 2025-05-03 22:29:18 +02:00
sfan5
4c142ea871 Add more versions to CI smoke test 2025-05-03 22:29:18 +02:00
Niklp
1513a490dc Replace deprecated image fields in crafitem definitions
This has been a legacy field for at least 13 years now
2025-04-28 21:37:02 +02:00
17 changed files with 174 additions and 56 deletions

View File

@@ -9,9 +9,9 @@ jobs:
- uses: actions/checkout@main
- name: apt
run: sudo apt-get install -y luarocks
- name: luacheck install
- name: install luacheck
run: luarocks install --local luacheck
- name: luacheck run
- name: run luacheck
run: $HOME/.luarocks/bin/luacheck ./
mineunit:
@@ -19,23 +19,27 @@ jobs:
name: "Mineunit tests"
steps:
- uses: actions/checkout@main
- name: apt
run: sudo apt-get install -y luarocks
- name: busted install
run: luarocks install --local busted
- name: luacov install
run: luarocks install --local luacov
- name: mineunit install
run: luarocks install --server=https://luarocks.org/dev --local mineunit
- name: run mesecons tests
working-directory: ./mesecons/
run: $HOME/.luarocks/bin/mineunit -q
uses: mt-mods/mineunit-actions@master
with:
coverage: false
mineunit-args: -q
working-directory: ./mesecons
- name: run mesecons_mvps tests
working-directory: ./mesecons_mvps/
run: $HOME/.luarocks/bin/mineunit -q
uses: mt-mods/mineunit-actions@master
with:
coverage: false
mineunit-args: -q
working-directory: ./mesecons_mvps
- name: run mesecons_fpga tests
working-directory: ./mesecons_fpga/
run: $HOME/.luarocks/bin/mineunit -q
uses: mt-mods/mineunit-actions@master
with:
coverage: false
mineunit-args: -q
working-directory: ./mesecons_fpga
- name: run mesecons_luacontroller tests
working-directory: ./mesecons_luacontroller/
run: $HOME/.luarocks/bin/mineunit -q
uses: mt-mods/mineunit-actions@master
with:
coverage: false
mineunit-args: -q
working-directory: ./mesecons_luacontroller

View File

@@ -10,11 +10,13 @@ jobs:
matrix:
cfg:
- { image: 'registry.gitlab.com/minetest/minetest/server:5.0.1', mtg: false }
- { image: 'ghcr.io/minetest/minetest:5.10.0', mtg: true }
- { image: 'ghcr.io/minetest/minetest:5.9.0', mtg: true }
- { image: 'ghcr.io/luanti-org/luanti:5.11.0', mtg: true }
steps:
- uses: actions/checkout@main
- uses: actions/checkout@main
- name: Download Minetest Game
uses: actions/checkout@main
with:
repository: 'minetest/minetest_game'
path: ./.test/minetest_game

View File

@@ -30,6 +30,7 @@ read_globals = {
"VoxelArea",
"mcl_dyes",
"mcl_sounds",
"hades_sounds",
}
globals = {"mesecon"}

View File

@@ -99,8 +99,9 @@ do
offstate = "mesecons:test_conductor_rot_off",
}}
mesecon.register_node("mesecons:test_conductor_rot", {
paramtype2 = "4dir",
description = "Rotatable Test Conductor",
on_rotate = mesecon.on_rotate_horiz,
on_rotate = mesecon.on_rotate,
}, {mesecons = off_spec}, {mesecons = on_spec})
end

View File

@@ -13,9 +13,10 @@ MESECONS by Jeija and contributors
Mezzee-what?
------------
[Mesecons](https://mesecons.net/)! They're yellow, they're conductive, and they'll add a whole new dimension to Minetest's gameplay.
[Mesecons](https://mesecons.net/)! They're yellow, they're conductive, and they'll add a whole new dimension to Luanti's gameplay.
Mesecons is a mod for [Minetest](https://www.minetest.net/) that implements a ton of items related to digital circuitry, such as wires, buttons, lights, and even programmable controllers. Among other things, there are also pistons, solar panels, pressure plates, and note blocks.
Mesecons is a mod for [Luanti](https://www.luanti.org/) that implements a ton of items related to digital circuitry, such as wires, buttons, lights, and even programmable controllers.
Among other things, there are also pistons, solar panels, pressure plates, and note blocks.
Mesecons has a similar goal to Redstone in Minecraft, but works in its own way, with different rules and mechanics.
@@ -24,15 +25,15 @@ OK, I want in.
Go get it!
[![ContentDB](https://content.minetest.net/packages/Jeija/mesecons/shields/downloads/)](https://content.minetest.net/packages/Jeija/mesecons/)
[![ContentDB](https://content.luanti.org/packages/Jeija/mesecons/shields/downloads/)](https://content.luanti.org/packages/Jeija/mesecons/)
Install it directly from your client by searching it in the Online Content tab.
**Or** if you've downloaded a ZIP file check out [this page](https://wiki.minetest.net/Mods) over at the official Minetest Wiki. For your convenience, here's a quick summary:
**Or** if you've downloaded a ZIP file check out [this page](https://docs.luanti.org/for-players/mods/) over at the official Luanti Wiki. For your convenience, here's a quick summary:
1. If Mesecons is still in a ZIP file, extract the folder inside to somewhere on the computer.
2. Make sure that when you open the folder, you can directly find `README.md` in the listing. If you just see another folder, move that folder up one level and delete the old one.
3. Open up the Minetest mods folder - called `mods`.
3. Open up the Luanti mods folder - called `mods`.
4. Copy the Mesecons folder into the mods folder.
Don't like some parts of Mesecons? Open up the Mesecons folder and delete the subfolder containing the mod you don't want. If you didn't want movestones, for example, all you have to do is delete the `mesecons_movestones` folder and they will no longer be available.

View File

@@ -17,6 +17,7 @@ function mesecon.on_rotate(pos, node, _, _, new_param2)
end
-- An on_rotate callback for components which stay horizontal.
-- Deprecated. Use paramtype2 = "4dir" instead.
function mesecon.on_rotate_horiz(pos, node, user, mode, new_param2)
if not minetest.global_exists("screwdriver") or mode ~= screwdriver.ROTATE_FACE then
return false

View File

@@ -18,7 +18,7 @@ minetest.register_node("mesecons_extrawires:corner_on", {
{ name = "jeija_insulated_wire_ends_on.png", backface_culling = true },
},
paramtype = "light",
paramtype2 = "facedir",
paramtype2 = "4dir",
is_ground_content = false,
walkable = false,
sunlight_propagates = true,
@@ -33,7 +33,7 @@ minetest.register_node("mesecons_extrawires:corner_on", {
offstate = "mesecons_extrawires:corner_off"
}},
on_blast = mesecon.on_blastnode,
on_rotate = mesecon.on_rotate_horiz,
on_rotate = mesecon.on_rotate,
})
minetest.register_node("mesecons_extrawires:corner_off", {
@@ -45,7 +45,7 @@ minetest.register_node("mesecons_extrawires:corner_off", {
{ name = "jeija_insulated_wire_ends_off.png", backface_culling = true },
},
paramtype = "light",
paramtype2 = "facedir",
paramtype2 = "4dir",
is_ground_content = false,
walkable = false,
sunlight_propagates = true,
@@ -59,7 +59,7 @@ minetest.register_node("mesecons_extrawires:corner_off", {
onstate = "mesecons_extrawires:corner_on"
}},
on_blast = mesecon.on_blastnode,
on_rotate = mesecon.on_rotate_horiz,
on_rotate = mesecon.on_rotate,
})
minetest.register_craft({

View File

@@ -41,7 +41,7 @@ for k, state in ipairs(doublecorner_states) do
{ name = "jeija_insulated_wire_ends_" .. w2 .. ".png", backface_culling = true },
},
paramtype = "light",
paramtype2 = "facedir",
paramtype2 = "4dir",
is_ground_content = false,
walkable = false,
sunlight_propagates = true,
@@ -56,7 +56,7 @@ for k, state in ipairs(doublecorner_states) do
},
},
on_blast = mesecon.on_blastnode,
on_rotate = mesecon.on_rotate_horiz,
on_rotate = mesecon.on_rotate,
})
end

View File

@@ -29,7 +29,7 @@ minetest.register_node("mesecons_extrawires:tjunction_on", {
"jeija_insulated_wire_ends_on.png"
},
paramtype = "light",
paramtype2 = "facedir",
paramtype2 = "4dir",
is_ground_content = false,
walkable = false,
sunlight_propagates = true,
@@ -45,7 +45,7 @@ minetest.register_node("mesecons_extrawires:tjunction_on", {
offstate = "mesecons_extrawires:tjunction_off"
}},
on_blast = mesecon.on_blastnode,
on_rotate = mesecon.on_rotate_horiz,
on_rotate = mesecon.on_rotate,
})
minetest.register_node("mesecons_extrawires:tjunction_off", {
@@ -60,7 +60,7 @@ minetest.register_node("mesecons_extrawires:tjunction_off", {
"jeija_insulated_wire_ends_off.png"
},
paramtype = "light",
paramtype2 = "facedir",
paramtype2 = "4dir",
is_ground_content = false,
walkable = false,
sunlight_propagates = true,
@@ -75,7 +75,7 @@ minetest.register_node("mesecons_extrawires:tjunction_off", {
onstate = "mesecons_extrawires:tjunction_on"
}},
on_blast = mesecon.on_blastnode,
on_rotate = mesecon.on_rotate_horiz,
on_rotate = mesecon.on_rotate,
})
minetest.register_craft({

View File

@@ -0,0 +1,72 @@
--Aliases
core.register_alias("mesecons_gamecompat:chest", "hades_chests:chest")
core.register_alias("mesecons_gamecompat:chest_locked", "hades_chests:chest_locked")
core.register_alias("mesecons_gamecompat:coalblock", "hades_core:coalblock")
core.register_alias("mesecons_gamecompat:cobble", "hades_core:cobble")
core.register_alias("mesecons_gamecompat:glass", "hades_core:glass")
core.register_alias("mesecons_gamecompat:lava_source", "hades_core:lava_source")
core.register_alias("mesecons_gamecompat:mese", "hades_core:mese")
core.register_alias("mesecons_gamecompat:mese_crystal", "hades_core:mese_crystal")
core.register_alias("mesecons_gamecompat:mese_crystal_fragment", "hades_core:mese_crystal_fragment")
core.register_alias("mesecons_gamecompat:obsidian_glass", "hades_core:obsidian_glass")
core.register_alias("mesecons_gamecompat:stone", "hades_core:stone")
core.register_alias("mesecons_gamecompat:steel_ingot", "hades_core:steel_ingot")
core.register_alias("mesecons_gamecompat:steelblock", "hades_core:steelblock")
core.register_alias("mesecons_gamecompat:torch", "hades_torches:torch")
if core.get_modpath("hades_dye") then
for _, color in ipairs(mesecon.dye_colors) do
core.register_alias("mesecons_gamecompat:dye_" .. color, "hades_dye:" .. color)
end
end
-- Sounds
mesecon.node_sound.default = hades_sounds.node_sound_defaults()
mesecon.node_sound.glass = hades_sounds.node_sound_glass_defaults()
mesecon.node_sound.leaves = hades_sounds.node_sound_leaves_defaults()
mesecon.node_sound.stone = hades_sounds.node_sound_stone_defaults()
mesecon.node_sound.wood = hades_sounds.node_sound_wood_defaults()
if core.get_modpath("hades_fire") then
mesecon.sound_name.fire = "fire_fire"
end
if core.get_modpath("hades_tnt") then
mesecon.sound_name.explode = "tnt_explode"
end
-- Textures
mesecon.texture.steel_block = "default_steel_block.png"
-- MVPS stoppers
if core.get_modpath("mesecons_mvps") then
-- All of the locked and internal nodes in Hades Revisited
for _, name in ipairs({
"hades_chests:chest_locked",
"hades_chests:chest_locked_open",
"hades_doors:hidden",
"hades_doors:hidden_center",
}) do
mesecon.register_mvps_stopper(name)
end
core.register_on_mods_loaded(function()
if core.get_modpath("hades_doors") then
for _,v in pairs(core.registered_nodes) do
if v.groups and (v.groups.door or v.groups.trapdoor) then
mesecon.register_mvps_stopper(v.name)
end
end
end
if core.get_modpath("hades_beds") then
for _,v in pairs(core.registered_nodes) do
if v.groups and v.groups.bed then
mesecon.register_mvps_stopper(v.name)
end
end
end
end)
end

View File

@@ -19,6 +19,11 @@ if minetest.get_modpath("mcl_redstone") then
dofile(minetest.get_modpath("mesecons_gamecompat").."/compat_mcla.lua")
end
if minetest.get_modpath("hades_core") then
minetest.log("info", "Mesecons: detected Hades Revisited Game for game compatibility")
dofile(minetest.get_modpath("mesecons_gamecompat").."/compat_hades.lua")
end
if minetest.get_modpath("doors") then
dofile(minetest.get_modpath("mesecons_gamecompat").."/doors.lua")
end

View File

@@ -1,3 +1,3 @@
name = mesecons_gamecompat
depends = mesecons
optional_depends = fire, default, dye, mesecons_mvps, tnt, doors, beds, mcl_fire, mcl_core, mcl_dye, mcl_tnt
optional_depends = fire, default, dye, mesecons_mvps, tnt, doors, beds, mcl_fire, mcl_core, mcl_dye, mcl_tnt, hades_sounds

View File

@@ -89,7 +89,7 @@ local function register_gate(name, inputnumber, assess, recipe, description)
description = description,
inventory_image = "jeija_gate_off.png^jeija_gate_"..name..".png",
paramtype = "light",
paramtype2 = "facedir",
paramtype2 = "4dir",
is_ground_content = false,
drawtype = "nodebox",
drop = basename.."_off",
@@ -102,7 +102,7 @@ local function register_gate(name, inputnumber, assess, recipe, description)
offstate = basename.."_off",
inputnumber = inputnumber,
after_dig_node = mesecon.do_cooldown,
on_rotate = mesecon.on_rotate_horiz,
on_rotate = mesecon.on_rotate,
},{
tiles = {
"jeija_microcontroller_bottom.png^".."jeija_gate_off.png^"..

View File

@@ -17,7 +17,7 @@ minetest.register_node("mesecons_insulated:insulated_on", {
"jeija_insulated_wire_sides_on.png"
},
paramtype = "light",
paramtype2 = "facedir",
paramtype2 = "4dir",
is_ground_content = false,
walkable = false,
sunlight_propagates = true,
@@ -39,7 +39,7 @@ minetest.register_node("mesecons_insulated:insulated_on", {
rules = insulated_wire_get_rules
}},
on_blast = mesecon.on_blastnode,
on_rotate = mesecon.on_rotate_horiz,
on_rotate = mesecon.on_rotate,
})
minetest.register_node("mesecons_insulated:insulated_off", {
@@ -54,7 +54,7 @@ minetest.register_node("mesecons_insulated:insulated_off", {
"jeija_insulated_wire_sides_off.png"
},
paramtype = "light",
paramtype2 = "facedir",
paramtype2 = "4dir",
is_ground_content = false,
walkable = false,
sunlight_propagates = true,
@@ -75,7 +75,7 @@ minetest.register_node("mesecons_insulated:insulated_off", {
rules = insulated_wire_get_rules
}},
on_blast = mesecon.on_blastnode,
on_rotate = mesecon.on_rotate_horiz,
on_rotate = mesecon.on_rotate,
})
minetest.register_craft({

View File

@@ -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,
@@ -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)
@@ -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"
@@ -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
@@ -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
@@ -677,6 +700,13 @@ 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()
@@ -684,13 +714,15 @@ local function run_inner(pos, code, event)
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]"
@@ -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"})
@@ -740,7 +772,7 @@ 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)
@@ -748,7 +780,7 @@ mesecon.queue:add_function("lc_digiline_relay", function (pos, channel, luac_id,
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)
@@ -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},

View File

@@ -2,13 +2,13 @@ local S = minetest.get_translator(minetest.get_current_modname())
-- Glue and fiber
minetest.register_craftitem("mesecons_materials:glue", {
image = "mesecons_glue.png",
inventory_image = "mesecons_glue.png",
on_place_on_ground = minetest.craftitem_place_item,
description = S("Glue"),
})
minetest.register_craftitem("mesecons_materials:fiber", {
image = "mesecons_fiber.png",
inventory_image = "mesecons_fiber.png",
on_place_on_ground = minetest.craftitem_place_item,
description = S("Fiber"),
})
@@ -29,7 +29,7 @@ minetest.register_craft({
-- Silicon
minetest.register_craftitem("mesecons_materials:silicon", {
image = "mesecons_silicon.png",
inventory_image = "mesecons_silicon.png",
on_place_on_ground = minetest.craftitem_place_item,
description = S("Silicon"),
})

View File

@@ -1,5 +1,5 @@
name = mesecons
description = Mod that implements a ton of items related to digital circuitry.
min_minetest_version = 5.0
min_minetest_version = 5.7.0
author = Jeija
title = Mesecons