81 Commits

Author SHA1 Message Date
302a28934d Document inactive block behaviour (#447) 2018-12-21 22:10:08 +03:00
6e767a6c76 Make sticky piston stick falling things as well (#436) 2018-12-21 22:02:57 +03:00
9d239cbfff Fix typos (#442) 2018-12-09 16:38:23 +03:00
d3cabedbb0 Prevent long error message from covering the button 2018-12-09 13:59:49 +01:00
df4e880d8b Fix crash in microcontroller (#439)
Add check nil var with bug from crash server.
fixes #438
2018-11-06 09:48:44 +01:00
45bbd9f7e3 Don’t damage unloaded blocks (#435) 2018-10-29 23:58:07 +03:00
028c290cd7 Mark 'code' as private as well
Saves on bandwidth, however the code is still accessible via the formspec.
2018-09-18 13:01:18 +02:00
8808bb8911 Mark LuaController memory as private
If LuaControllers handle sensitive information, hacked clients could get this information from the LuaController. Marking the memory as private fixes this and saves a small amount of bandwidth.
2018-09-18 13:01:18 +02:00
fa040eb085 Fix vertical movestone textures (#430) 2018-09-09 00:35:15 +03:00
a4f5ae5b89 Remove tiny (+0.001) selection box oversize 2018-09-07 17:31:49 +02:00
9e6eac4285 Make insulated wires’ selection box fit in the node 2018-09-07 17:31:49 +02:00
c73b451f9b Update wiki links (#432)
wiki.minetest.net is the official Minetest wiki
2018-08-29 00:45:46 +03:00
DS
9ff2329253 Noteblock: use new fire sounds (#385) 2018-08-26 23:13:47 +03:00
444cd0f2f1 Replace usage of default.LIGHT_MAX with minetest.LIGHT_MAX
It was moved a long time ago and the former is not guaranteed to be available.
fixes #424
2018-07-24 21:30:04 +02:00
d4e05f33af Fix Luacontroller’s print() (#422)
Because of working inside the sandbox, it was unable to print tables.
2018-07-23 15:53:32 +03:00
334400a541 Add 'settingtypes.txt' (#350) 2018-07-23 15:48:34 +03:00
5a0492b176 Check protection on microcontroller programming (#420)
Also, fix globals assignment.
2018-07-23 14:00:59 +03:00
f61b1affea Let things fall behind movestones (#421)
* Let things fall behind movestones
2018-07-23 13:14:05 +03:00
a234006592 Fix mesecon receiver duplication (#419) 2018-07-21 10:42:24 +03:00
12fb09f452 Fix crash when error()ing an invalid value on programming (#418) 2018-07-18 22:49:34 +03:00
ff73d0e60f Merge pull request #401 from Wuzzy2/sfx
Add missing sound effects and fix wrong pressure plate sounds
2018-07-18 01:37:45 +03:00
7013f2e5d4 Add screenshot.png 2018-05-15 20:21:33 +01:00
f70a308bb0 Replace deprecated methods:
- 'setting_get' with 'settings:get'
- 'setting_getbool' with 'settings:get_bool'
2018-03-25 22:05:10 +02:00
d8f82e6771 Fix sticky pistons (#403)
Fixed method name to get the position.
2018-02-15 11:47:24 +01:00
DS
7537b9f381 Use rotate_simple for insulated wires (#382) 2018-01-30 21:47:03 +03:00
5ae3354227 Change hydroturbine sound to metal 2018-01-28 21:22:09 +01:00
1b513d2111 Fix sounds of pressure plates, change groups 2018-01-28 21:14:54 +01:00
6f1ff24474 Add sounds for wires and piston heads 2018-01-28 20:56:42 +01:00
065e870349 Improve LuaController security (#393)
Fixes:
1. Lack of 'safe' on minetest.deserialize usage
2. String sandbox bypass via (""):evil()
3. Loss of upcoming digilines messages on server shutdown
4. LCs failing to show information on some errors
5. Interrupt IDs as infinite data storage
2018-01-22 14:26:21 +03:00
fec82ab246 Sticky block: Add texture, sounds and groups (#397) 2018-01-18 22:01:53 +03:00
02f8d29170 Microcontroller craft fallbacks for detectors (#390)
Register both luacontroller and microcontroller variations of detector craft recipes.
2018-01-14 23:21:43 +03:00
2b096f050d Limit and optimize digiline_send (#379)
* Close vulnerability and optimize digiline_send

`digiline_send` as it previously existed was vulnerable to a
time-of-check-to-time-of-use vulnerability in which a table could be
sent, size-checked, and then modified after the send but before
delivery. This would allow larger tables to be sent. It was also slow
because it called `minetest.serialize`. Fix both of these by
implementing custom message cleanup logic which simultaneously computes
the message’s cost.

* Clean up interaction with Digilines

Use `minetest.global_exists` to avoid an undefined global variable
warning when operating a Luacontroller with Digilines not available. Use
the new `digilines` table in preference to the old `digiline` table.

* Copy received messages

When a Digiline message is received at a Luacontroller, copy it so that
local modifications made by the Luacontroller code will not modify
copies of the table that are being passed to other nodes on the Digiline
network.
2018-01-13 22:27:00 +03:00
DS
993fdedd8c Check for falling after piston pusher removal (#392) 2018-01-07 01:32:15 +03:00
DS
ff0bd76efe Set is_ground_content to false (#386) 2017-11-01 00:50:39 +03:00
db39eef2f6 Make the fpga overheat 2017-10-29 21:19:08 +01:00
f78937b351 Fix typo 2017-10-21 17:05:09 +03:00
cc860acf8a Publish some LuaC API (#348) 2017-10-18 22:54:28 +03:00
DS
748446b3cc MVPS: pull objects (#374) 2017-10-18 22:53:22 +03:00
DS
1b10610f92 Check for falling nodes on MVPS move (#371) 2017-10-18 22:40:25 +03:00
4f400f3fae Add bower compatibility (#347) 2017-10-15 17:40:44 +03:00
DS
7094f0b606 Improve movestones (#366)
Improve the code and add vertical movestones
2017-10-08 19:41:22 +03:00
b08e93fc8b Remove obsolete nodeupdate call
It wasn't necessary, actually.
2017-10-08 19:20:24 +03:00
DS
928f04fa9c Rewrite pistons (#362) 2017-10-08 19:21:19 +03:00
DS
c4a1aa0b98 MVPS: Improve object move (#367) 2017-10-08 18:18:47 +03:00
DS
37eb7f00e8 Improve rules and rule handling (#370) 2017-10-08 02:39:02 +03:00
DS
905260c8db Handle blasts (#356) 2017-10-07 01:44:49 +03:00
DS
8999597df2 Support an on-move callback (#363) 2017-10-06 00:09:48 +03:00
DS
0b3039e9a9 Make removestone check nodes above for falling (#357)
Fixes a part of #290.
2017-10-04 01:19:09 +03:00
DS
1f5601661e Do not reset conductor param2 on place (#364) 2017-10-04 00:55:33 +03:00
fa58607546 fpga: Make rotatable with screwdrivers 2017-10-03 17:46:58 +02:00
DS
1963d8b3d5 Prevent wires from being rotated (#365) 2017-09-19 18:12:00 +03:00
954262d016 Add 6 new lightstone colors (#329)
Cyan, magenta, orange, pink, violet and white.
2017-09-18 01:38:55 +03:00
DS
3ef8ae01e7 Add conversation recipes for Lua- and Microcontroller (#354) 2017-09-11 21:26:37 +02:00
DS
42cd973921 Do not stop node timers on mvps move (#359) 2017-09-11 21:26:04 +02:00
DS
36a6419481 Fix active button staying on forever (#355) 2017-09-03 21:27:31 +03:00
564bdc2d9b Fix crash when error()ing an invalid value
See #337 for details.
2017-05-08 17:34:30 +03:00
DS
f9ebbdc0fb Make LuaC formspec bigger (#338) 2017-05-08 02:59:57 +04:00
4dcc7fb32f Update github links in the README 2017-04-21 21:33:21 +02:00
54b9eaffa2 Improve overheating (#334)
New overheating system that doesn’t use the meta.
2017-04-14 23:14:17 +04:00
2554164674 Improve object and node detectors
* Support detection of multiple players in object detector
* Add distance parameter to node detector
* Fix area protection
2017-03-31 12:11:40 +02:00
967bde284a Spell-check and clarify item names
* Fix inconsistent insulated mesecon names
* Clarify lightstone names
* Rename meselamp to "Mesecon Lamp"
* Use capitalization "Luacontroller" consistently
* Cleanup / improvements for logic gate naming
2017-03-07 19:04:05 +01:00
DS
d80c788fab Change sticky movestone craft recipe
It used to be so that you could gain two sticky movestones out of one normal one,
this didn't make much sense.
2017-03-04 09:10:44 +01:00
03ab151c87 Fix active ghoststones being droppable with sand
Fixes #289
2017-02-15 23:00:02 +01:00
dd2b36c41c fix wrong UV mapping on "on" turbine blades 2017-02-13 14:24:29 -05:00
0cb286b425 Merge pull request #321 from Jeija/animated_turbine
Make "on" water turbine animated!
2017-02-12 19:56:29 -05:00
46cbc76988 make "on" water turbine animated!
This uses a fairly simple trick: the textures are animated, while the
model is static and has a number of rotated copies of the parts that are
seen to move (four copies of the blades, eight copies of the top/bottom
and their edges).  The opaque parts of the textures then decide which of
those faces' copies are visible in each frame.
2017-02-12 13:57:30 -05:00
fa3bd19270 fix Z-fighting on water turbine 2017-02-12 10:56:54 -05:00
2bc3c5d97c fpga: Make formspec usage less prone to breakage 2017-01-28 11:54:27 +01:00
07d074075c FPGAs (#315) 2017-01-28 11:33:13 +01:00
5188853014 Make descriptions of insulated wires consistent
This allows finding all of them by searching
"insulated" in the creative menu.
2017-01-22 13:39:10 +01:00
7667e7d8c5 Fix Lua error with microcontrollers
caused by an oversight in ececf525b6
2017-01-19 16:31:36 +01:00
ececf525b6 Fix remaining namespace pollutions (closes #311) 2017-01-15 23:12:13 +01:00
54daee236e Luacontroller: Revert function stripping from digiline messages 2017-01-15 20:11:12 +01:00
c2e3d7c4e5 Fix most of the namespace pollutions reported in #311 2017-01-15 11:53:49 +01:00
7415036f5b Make insulated cossover to mesh
This removes the weird "holes" in the isolation of the crossing
2017-01-02 14:59:52 +01:00
1bd936ad8c Properly handle rotation of buttons / levers
Buttons and levers can now also be pointed upwards / downwards which will make them connect to corresponding up / down receivers. You will need to use the screwdriver for this.

Receivers cannot be rotated using the screwdriver anymore.
2017-01-02 14:51:28 +01:00
79edbed8d7 Allow water turbines to work with river water (fixes #294)
This also makes turbines compatible with any liquid that is in the `water` group and has `liquidtype == "flowing"`.
2016-12-31 10:57:50 +01:00
8743699298 Luacontroller: Fix bugs in 703e6fda, no more functions as keys
Thanks to @ShadowNinja for reporting this
Make sure functions that are keys in tables and functions inside nested tables also get removed when using digiline_send.
2016-12-31 10:43:45 +01:00
703e6fdadb Luacontroller: Restrict digiline messages
Restrict maximum length of messages to 50.000 characters and disable sending functions or table references over the wire. Restrict types of channel variable to string, number or boolean.

The missing length restriction made DoS-like attacks possible by overflowing memory using string concatenation. Thanks to gamemanj for disclosing this issue.
2016-12-28 10:07:59 +01:00
67cd17aa79 Fix crash with movestones 2016-12-04 19:37:31 +08:00
203aaf3c90 Remove an unused function. 2016-11-17 14:55:02 +01:00
133 changed files with 4584 additions and 1282 deletions

View File

@ -22,9 +22,9 @@ OK, I want in.
--------------
Go get it!
[DOWNLOAD IT NOW](https://github.com/Jeija/minetest-mod-mesecons/archive/master.zip)
[DOWNLOAD IT NOW](https://github.com/minetest-mods/mesecons/archive/master.zip)
Now go ahead and install it like any other Minetest mod. Don't know how? Check out [the wonderful page about it](http://wiki.minetest.com/wiki/Mods) over at the Minetest Wiki. For your convenience, here's a quick summary:
Now go ahead and install it like any other Minetest mod. Don't know how? Check out [the wonderful page about it](https://wiki.minetest.net/Mods) over at the official Minetest 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.
@ -43,9 +43,11 @@ Or maybe a [comprehensive reference](http://mesecons.net/items.html) is your sty
An overview for the very newest of new beginners? How does [this one](http://uberi.mesecons.net/projects/MeseconsBasics/index.html) look?
There is also a [wiki page](https://wiki.minetest.net/Mods/Mesecons) dedicated to this mod.
Want to get more into building? Why not check out the [Mesecons Laboratory](http://uberi.mesecons.net/), a website dedicated to advanced Mesecons builders?
Want to contribute to Mesecons itself? Check out the [source code](https://github.com/Jeija/minetest-mod-mesecons)!
Want to contribute to Mesecons itself? Check out the [source code](https://github.com/minetest-mods/mesecons)!
Who wrote it anyways?
---------------------

12
bower.json Normal file
View File

@ -0,0 +1,12 @@
{
"name": "mesecons",
"description": "Mesecons is a mod for Minetest that implements items related to digital circuitry: wires, buttons, lights, and programmable controllers.",
"homepage": "http://mesecons.net",
"authors": "Jeija",
"license": "LGPL-3.0+",
"keywords": [
"mesecons",
"minetest",
"mod"
]
}

View File

@ -42,6 +42,8 @@
},
"Logic" : {
"Luacontroller" : "mesecons_luacontroller/doc/luacontroller",
"FPGA" : "mesecons_fpga/doc/fpga",
"FPGA Programmer" : "mesecons_fpga/doc/programmer",
"Torch" : "mesecons_torch/doc/torch",
"Delayer" : "mesecons_delayer/doc/delayer",
"Gates" : {

View File

@ -548,52 +548,3 @@ function mesecon.is_powered(pos, rule)
if (#sourcepos == 0) then return false
else return sourcepos end
end
--Rules rotation Functions:
function mesecon.rotate_rules_right(rules)
local nr = {}
for i, rule in ipairs(rules) do
table.insert(nr, {
x = -rule.z,
y = rule.y,
z = rule.x,
name = rule.name})
end
return nr
end
function mesecon.rotate_rules_left(rules)
local nr = {}
for i, rule in ipairs(rules) do
table.insert(nr, {
x = rule.z,
y = rule.y,
z = -rule.x,
name = rule.name})
end
return nr
end
function mesecon.rotate_rules_down(rules)
local nr = {}
for i, rule in ipairs(rules) do
table.insert(nr, {
x = -rule.y,
y = rule.x,
z = rule.z,
name = rule.name})
end
return nr
end
function mesecon.rotate_rules_up(rules)
local nr = {}
for i, rule in ipairs(rules) do
table.insert(nr, {
x = rule.y,
y = -rule.x,
z = rule.z,
name = rule.name})
end
return nr
end

View File

@ -4,7 +4,7 @@ minetest.register_node("mesecons:mesecon_off", {
inventory_image = "jeija_mesecon_off.png",
wield_image = "jeija_mesecon_off.png",
paramtype = "light",
is_ground_content = true,
is_ground_content = false,
walkable = false,
selection_box = {
type = "fixed",
@ -22,7 +22,7 @@ minetest.register_node("mesecons:mesecon_on", {
drawtype = "raillike",
tiles = {"jeija_mesecon_on.png", "jeija_mesecon_curved_on.png", "jeija_mesecon_t_junction_on.png", "jeija_mesecon_crossing_on.png"},
paramtype = "light",
is_ground_content = true,
is_ground_content = false,
walkable = false,
selection_box = {
type = "fixed",
@ -30,7 +30,7 @@ minetest.register_node("mesecons:mesecon_on", {
},
groups = {dig_immediate=3, not_in_creaive_inventory=1, mesecon=1},
drop = "mesecons:mesecon_off 1",
light_source = default.LIGHT_MAX-11,
light_source = minetest.LIGHT_MAX-11,
mesecons = {conductor={
state = mesecon.state.on,
offstate = "mesecons:mesecon_off"

View File

@ -1,54 +1,85 @@
mesecon.rules = {}
mesecon.state = {}
mesecon.rules.default =
{{x=0, y=0, z=-1},
{x=1, y=0, z=0},
{x=-1, y=0, z=0},
{x=0, y=0, z=1},
{x=1, y=1, z=0},
{x=1, y=-1, z=0},
{x=-1, y=1, z=0},
{x=-1, y=-1, z=0},
{x=0, y=1, z=1},
{x=0, y=-1, z=1},
{x=0, y=1, z=-1},
{x=0, y=-1, z=-1}}
mesecon.rules.default = {
{x = 0, y = 0, z = -1},
{x = 1, y = 0, z = 0},
{x = -1, y = 0, z = 0},
{x = 0, y = 0, z = 1},
{x = 1, y = 1, z = 0},
{x = 1, y = -1, z = 0},
{x = -1, y = 1, z = 0},
{x = -1, y = -1, z = 0},
{x = 0, y = 1, z = 1},
{x = 0, y = -1, z = 1},
{x = 0, y = 1, z = -1},
{x = 0, y = -1, z = -1},
}
mesecon.rules.pplate = mesecon.mergetable(mesecon.rules.default, {{x=0, y=-2, z=0}})
mesecon.rules.floor = mesecon.mergetable(mesecon.rules.default, {{x = 0, y = -1, z = 0}})
mesecon.rules.buttonlike =
{{x = 1, y = 0, z = 0},
{x = 1, y = 1, z = 0},
{x = 1, y =-1, z = 0},
{x = 1, y =-1, z = 1},
{x = 1, y =-1, z =-1},
{x = 2, y = 0, z = 0}}
mesecon.rules.pplate = mesecon.mergetable(mesecon.rules.floor, {{x = 0, y = -2, z = 0}})
mesecon.rules.flat =
{{x = 1, y = 0, z = 0},
{x =-1, y = 0, z = 0},
{x = 0, y = 0, z = 1},
{x = 0, y = 0, z =-1}}
mesecon.rules.buttonlike = {
{x = 1, y = 0, z = 0},
{x = 1, y = 1, z = 0},
{x = 1, y = -1, z = 0},
{x = 1, y = -1, z = 1},
{x = 1, y = -1, z = -1},
{x = 2, y = 0, z = 0},
}
mesecon.rules.alldirs =
{{x= 1, y= 0, z= 0},
{x=-1, y= 0, z= 0},
{x= 0, y= 1, z= 0},
{x= 0, y=-1, z= 0},
{x= 0, y= 0, z= 1},
{x= 0, y= 0, z=-1}}
mesecon.rules.flat = {
{x = 1, y = 0, z = 0},
{x = -1, y = 0, z = 0},
{x = 0, y = 0, z = 1},
{x = 0, y = 0, z = -1},
}
mesecon.rules.alldirs = {
{x = 1, y = 0, z = 0},
{x = -1, y = 0, z = 0},
{x = 0, y = 1, z = 0},
{x = 0, y = -1, z = 0},
{x = 0, y = 0, z = 1},
{x = 0, y = 0, z = -1},
}
local rules_wallmounted = {
xp = mesecon.rotate_rules_down(mesecon.rules.floor),
xn = mesecon.rotate_rules_up(mesecon.rules.floor),
yp = mesecon.rotate_rules_up(mesecon.rotate_rules_up(mesecon.rules.floor)),
yn = mesecon.rules.floor,
zp = mesecon.rotate_rules_left(mesecon.rotate_rules_up(mesecon.rules.floor)),
zn = mesecon.rotate_rules_right(mesecon.rotate_rules_up(mesecon.rules.floor)),
}
local rules_buttonlike = {
xp = mesecon.rules.buttonlike,
xn = mesecon.rotate_rules_right(mesecon.rotate_rules_right(mesecon.rules.buttonlike)),
yp = mesecon.rotate_rules_down(mesecon.rules.buttonlike),
yn = mesecon.rotate_rules_up(mesecon.rules.buttonlike),
zp = mesecon.rotate_rules_right(mesecon.rules.buttonlike),
zn = mesecon.rotate_rules_left(mesecon.rules.buttonlike),
}
local function rules_from_dir(ruleset, dir)
if dir.x == 1 then return ruleset.xp end
if dir.y == 1 then return ruleset.yp end
if dir.z == 1 then return ruleset.zp end
if dir.x == -1 then return ruleset.xn end
if dir.y == -1 then return ruleset.yn end
if dir.z == -1 then return ruleset.zn end
end
mesecon.rules.wallmounted_get = function(node)
local dir = minetest.wallmounted_to_dir(node.param2)
return rules_from_dir(rules_wallmounted, dir)
end
mesecon.rules.buttonlike_get = function(node)
local rules = mesecon.rules.buttonlike
if node.param2 == 2 then
rules=mesecon.rotate_rules_left(rules)
elseif node.param2 == 3 then
rules=mesecon.rotate_rules_right(mesecon.rotate_rules_right(rules))
elseif node.param2 == 0 then
rules=mesecon.rotate_rules_right(rules)
end
return rules
local dir = minetest.facedir_to_dir(node.param2)
return rules_from_dir(rules_buttonlike, dir)
end
mesecon.state.on = "on"

View File

@ -21,7 +21,8 @@ mesecon.on_placenode = function(pos, node)
end
--mesecon.receptor_on (pos, mesecon.conductor_get_rules(node))
elseif mesecon.is_conductor_on(node) then
minetest.swap_node(pos, {name = mesecon.get_conductor_off(node)})
node.name = mesecon.get_conductor_off(node)
minetest.swap_node(pos, node)
end
end
@ -62,34 +63,74 @@ mesecon.on_dignode = function(pos, node)
mesecon.execute_autoconnect_hooks_queue(pos, node)
end
function mesecon.on_blastnode(pos, intensity)
local node = minetest.get_node(pos)
minetest.remove_node(pos)
mesecon.on_dignode(pos, node)
return minetest.get_node_drops(node.name, "")
end
minetest.register_on_placenode(mesecon.on_placenode)
minetest.register_on_dignode(mesecon.on_dignode)
-- Overheating service for fast circuits
local OVERHEAT_MAX = mesecon.setting("overheat_max", 20)
local COOLDOWN_TIME = mesecon.setting("cooldown_time", 2.0)
local COOLDOWN_STEP = mesecon.setting("cooldown_granularity", 0.5)
local COOLDOWN_MULTIPLIER = OVERHEAT_MAX / COOLDOWN_TIME
local cooldown_timer = 0.0
local object_heat = {}
-- returns true if heat is too high
mesecon.do_overheat = function(pos)
local meta = minetest.get_meta(pos)
local heat = meta:get_int("heat") or 0
heat = heat + 1
meta:set_int("heat", heat)
if heat < mesecon.setting("overheat_max", 20) then
mesecon.queue:add_action(pos, "cooldown", {}, 1, nil, 0)
else
function mesecon.do_overheat(pos)
local id = minetest.hash_node_position(pos)
local heat = (object_heat[id] or 0) + 1
object_heat[id] = heat
if heat >= OVERHEAT_MAX then
minetest.log("action", "Node overheats at " .. minetest.pos_to_string(pos))
object_heat[id] = nil
return true
end
return false
end
function mesecon.do_cooldown(pos)
local id = minetest.hash_node_position(pos)
object_heat[id] = nil
end
mesecon.queue:add_function("cooldown", function (pos)
local meta = minetest.get_meta(pos)
local heat = meta:get_int("heat")
function mesecon.get_heat(pos)
local id = minetest.hash_node_position(pos)
return object_heat[id] or 0
end
if (heat > 0) then
meta:set_int("heat", heat - 1)
function mesecon.move_hot_nodes(moved_nodes)
local new_heat = {}
for _, n in ipairs(moved_nodes) do
local old_id = minetest.hash_node_position(n.oldpos)
local new_id = minetest.hash_node_position(n.pos)
new_heat[new_id] = object_heat[old_id]
object_heat[old_id] = nil
end
end)
for id, heat in pairs(new_heat) do
object_heat[id] = heat
end
end
local function global_cooldown(dtime)
cooldown_timer = cooldown_timer + dtime
if cooldown_timer < COOLDOWN_STEP then
return -- don't overload the CPU
end
local cooldown = COOLDOWN_MULTIPLIER * cooldown_timer
cooldown_timer = 0
for id, heat in pairs(object_heat) do
heat = heat - cooldown
if heat <= 0 then
object_heat[id] = nil -- free some RAM
else
object_heat[id] = heat
end
end
end
minetest.register_globalstep(global_cooldown)

View File

@ -1,15 +1,15 @@
-- SETTINGS
function mesecon.setting(setting, default)
if type(default) == "boolean" then
local read = minetest.setting_getbool("mesecon."..setting)
local read = minetest.settings:get_bool("mesecon."..setting)
if read == nil then
return default
else
return read
end
elseif type(default) == "string" then
return minetest.setting_get("mesecon."..setting) or default
return minetest.settings:get("mesecon."..setting) or default
elseif type(default) == "number" then
return tonumber(minetest.setting_get("mesecon."..setting) or default)
return tonumber(minetest.settings:get("mesecon."..setting) or default)
end
end

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -6,6 +6,56 @@ function mesecon.move_node(pos, newpos)
minetest.get_meta(pos):from_table(meta)
end
-- Rules rotation Functions:
function mesecon.rotate_rules_right(rules)
local nr = {}
for i, rule in ipairs(rules) do
table.insert(nr, {
x = -rule.z,
y = rule.y,
z = rule.x,
name = rule.name})
end
return nr
end
function mesecon.rotate_rules_left(rules)
local nr = {}
for i, rule in ipairs(rules) do
table.insert(nr, {
x = rule.z,
y = rule.y,
z = -rule.x,
name = rule.name})
end
return nr
end
function mesecon.rotate_rules_down(rules)
local nr = {}
for i, rule in ipairs(rules) do
table.insert(nr, {
x = -rule.y,
y = rule.x,
z = rule.z,
name = rule.name})
end
return nr
end
function mesecon.rotate_rules_up(rules)
local nr = {}
for i, rule in ipairs(rules) do
table.insert(nr, {
x = rule.y,
y = -rule.x,
z = rule.z,
name = rule.name})
end
return nr
end
--
function mesecon.flattenrules(allrules)
--[[
{
@ -178,6 +228,7 @@ end
function mesecon.register_node(name, spec_common, spec_off, spec_on)
spec_common.drop = spec_common.drop or name .. "_off"
spec_common.on_blast = spec_common.on_blast or mesecon.on_blastnode
spec_common.__mesecon_basename = name
spec_on.__mesecon_state = "on"
spec_off.__mesecon_state = "off"
@ -231,11 +282,6 @@ local function hash_blockpos(pos)
})
end
-- convert block hash --> node position
local function unhash_blockpos(hash)
return vector.multiply(minetest.get_position_from_hash(hash), BLOCKSIZE)
end
-- Maps from a hashed mapblock position (as returned by hash_blockpos) to a
-- table.
--

View File

@ -1 +1,2 @@
The blinky plants toggles between on and off state every three seconds. Can be used to make clocks. Also works after having restarted the game.
It stops blinking in an inactive block, then starts again when the block becomes active.

View File

@ -24,6 +24,7 @@ mesecon.register_node("mesecons_blinkyplant:blinky_plant", {
drawtype = "plantlike",
inventory_image = "jeija_blinky_plant_off.png",
paramtype = "light",
is_ground_content = false,
walkable = false,
sounds = default.node_sound_leaves_defaults(),
selection_box = {

View File

@ -4,12 +4,13 @@
mesecon.button_turnoff = function (pos)
local node = minetest.get_node(pos)
if node.name=="mesecons_button:button_on" then --has not been dug
minetest.swap_node(pos, {name = "mesecons_button:button_off", param2=node.param2})
minetest.sound_play("mesecons_button_pop", {pos=pos})
local rules = mesecon.rules.buttonlike_get(node)
mesecon.receptor_off(pos, rules)
if node.name ~= "mesecons_button:button_on" then -- has been dug
return
end
minetest.swap_node(pos, {name = "mesecons_button:button_off", param2 = node.param2})
minetest.sound_play("mesecons_button_pop", {pos = pos})
local rules = mesecon.rules.buttonlike_get(node)
mesecon.receptor_off(pos, rules)
end
minetest.register_node("mesecons_button:button_off", {
@ -24,8 +25,10 @@ minetest.register_node("mesecons_button:button_off", {
},
paramtype = "light",
paramtype2 = "facedir",
is_ground_content = false,
legacy_wallmounted = true,
walkable = false,
on_rotate = mesecon.buttonlike_onrotate,
sunlight_propagates = true,
selection_box = {
type = "fixed",
@ -44,13 +47,14 @@ minetest.register_node("mesecons_button:button_off", {
minetest.swap_node(pos, {name = "mesecons_button:button_on", param2=node.param2})
mesecon.receptor_on(pos, mesecon.rules.buttonlike_get(node))
minetest.sound_play("mesecons_button_push", {pos=pos})
minetest.after(1, mesecon.button_turnoff, pos)
minetest.get_node_timer(pos):start(1)
end,
sounds = default.node_sound_stone_defaults(),
mesecons = {receptor = {
state = mesecon.state.off,
rules = mesecon.rules.buttonlike_get
}}
}},
on_blast = mesecon.on_blastnode,
})
minetest.register_node("mesecons_button:button_on", {
@ -65,9 +69,11 @@ minetest.register_node("mesecons_button:button_on", {
},
paramtype = "light",
paramtype2 = "facedir",
is_ground_content = false,
legacy_wallmounted = true,
walkable = false,
light_source = default.LIGHT_MAX-7,
on_rotate = false,
light_source = minetest.LIGHT_MAX-7,
sunlight_propagates = true,
selection_box = {
type = "fixed",
@ -87,7 +93,9 @@ minetest.register_node("mesecons_button:button_on", {
mesecons = {receptor = {
state = mesecon.state.on,
rules = mesecon.rules.buttonlike_get
}}
}},
on_timer = mesecon.button_turnoff,
on_blast = mesecon.on_blastnode,
})
minetest.register_craft({

View File

@ -1 +1,2 @@
There is no crafting recipe as this should only be available for server admins. Quite similar to the Minecraft counterpart. Executes server commands.
It works in inactive blocks.

View File

@ -181,6 +181,7 @@ minetest.register_node("mesecons_commandblock:commandblock_off", {
description = "Command Block",
tiles = {"jeija_commandblock_off.png"},
inventory_image = minetest.inventorycube("jeija_commandblock_off.png"),
is_ground_content = false,
groups = {cracky=2, mesecon_effector_off=1},
on_construct = construct,
after_place_node = after_place,
@ -189,11 +190,13 @@ minetest.register_node("mesecons_commandblock:commandblock_off", {
sounds = default.node_sound_stone_defaults(),
mesecons = {effector = {
action_on = commandblock_action_on
}}
}},
on_blast = mesecon.on_blastnode,
})
minetest.register_node("mesecons_commandblock:commandblock_on", {
tiles = {"jeija_commandblock_on.png"},
is_ground_content = false,
groups = {cracky=2, mesecon_effector_on=1, not_in_creative_inventory=1},
light_source = 10,
drop = "mesecons_commandblock:commandblock_off",
@ -204,5 +207,6 @@ minetest.register_node("mesecons_commandblock:commandblock_on", {
sounds = default.node_sound_stone_defaults(),
mesecons = {effector = {
action_off = commandblock_action_off
}}
}},
on_blast = mesecon.on_blastnode,
})

View File

@ -1 +1 @@
The delayer delays the signal from the input for a determined time. The time can be set by punching the delayer. Possible delays are: 0.1 seconds, 0.3 seconds, 0.5 seconds and 1 second. You may try to use it for creating songs with the noteblock.
The delayer delays the signal from the input for a determined time. The time can be set by punching the delayer. Possible delays are: 0.1 seconds, 0.3 seconds, 0.5 seconds and 1 second. You may try to use it for creating songs with the noteblock. It works in unloaded blocks.

View File

@ -47,7 +47,8 @@ elseif i == 2 then delaytime = 0.3
elseif i == 3 then delaytime = 0.5
elseif i == 4 then delaytime = 1.0 end
boxes = {{ -6/16, -8/16, -6/16, 6/16, -7/16, 6/16 }, -- the main slab
local boxes = {
{ -6/16, -8/16, -6/16, 6/16, -7/16, 6/16 }, -- the main slab
{ -2/16, -7/16, -4/16, 2/16, -26/64, -3/16 }, -- the jeweled "on" indicator
{ -3/16, -7/16, -3/16, 3/16, -26/64, -2/16 },
@ -57,7 +58,8 @@ boxes = {{ -6/16, -8/16, -6/16, 6/16, -7/16, 6/16 }, -- the main slab
{ -6/16, -7/16, -6/16, -4/16, -27/64, -4/16 }, -- the timer indicator
{ -8/16, -8/16, -1/16, -6/16, -7/16, 1/16 }, -- the two wire stubs
{ 6/16, -8/16, -1/16, 8/16, -7/16, 1/16 }}
{ 6/16, -8/16, -1/16, 8/16, -7/16, 1/16 }
}
minetest.register_node("mesecons_delayer:delayer_off_"..tostring(i), {
description = "Delayer",
@ -85,7 +87,7 @@ minetest.register_node("mesecons_delayer:delayer_off_"..tostring(i), {
paramtype = "light",
paramtype2 = "facedir",
sunlight_propagates = true,
is_ground_content = true,
is_ground_content = false,
drop = 'mesecons_delayer:delayer_off_1',
on_punch = function (pos, node)
if node.name=="mesecons_delayer:delayer_off_1" then
@ -112,7 +114,8 @@ minetest.register_node("mesecons_delayer:delayer_off_"..tostring(i), {
rules = delayer_get_input_rules,
action_on = delayer_activate
}
}
},
on_blast = mesecon.on_blastnode,
})
@ -140,7 +143,7 @@ minetest.register_node("mesecons_delayer:delayer_on_"..tostring(i), {
paramtype = "light",
paramtype2 = "facedir",
sunlight_propagates = true,
is_ground_content = true,
is_ground_content = false,
drop = 'mesecons_delayer:delayer_off_1',
on_punch = function (pos, node)
if node.name=="mesecons_delayer:delayer_on_1" then
@ -155,6 +158,7 @@ minetest.register_node("mesecons_delayer:delayer_on_"..tostring(i), {
end,
delayer_time = delaytime,
delayer_offstate = "mesecons_delayer:delayer_off_"..tostring(i),
sounds = default.node_sound_stone_defaults(),
mesecons = {
receptor =
{
@ -166,7 +170,8 @@ minetest.register_node("mesecons_delayer:delayer_on_"..tostring(i), {
rules = delayer_get_input_rules,
action_off = delayer_deactivate
}
}
},
on_blast = mesecon.on_blastnode,
})
end

View File

@ -4,3 +4,5 @@ It can also receive digiline signals. You can either send "GET" and it will
respond with the detected nodename or you can send any other string and it will
set this string as the node to scan for.
Nodenames must include the mod they reside in, so for instance default:dirt, not just dirt.
The distance parameter specifies how many blocks are between the node detector and the node to detect.
Automatic scanning with Mesecons output only works when the detector is in an active block, but Digilines queries always work.

View File

@ -1,3 +1,5 @@
The object detector is a receptor. It changes its state when a player approaches.
Right-click it to set a name to scan for.
You can also search for comma-separated lists of players where the detector gets activated if any of the names in the list are found.
It can also receive digiline signals which are the name to scan for on the specified channel in the right-click menu.
Automatic scanning with Mesecons output only works when the detector is in an active block, but Digilines queries always work.

View File

@ -5,15 +5,18 @@ local GET_COMMAND = "GET"
-- The radius can be specified in mesecons/settings.lua
local function object_detector_make_formspec(pos)
minetest.get_meta(pos):set_string("formspec", "size[9,2.5]" ..
local meta = minetest.get_meta(pos)
meta:set_string("formspec", "size[9,2.5]" ..
"field[0.3, 0;9,2;scanname;Name of player to scan for (empty for any):;${scanname}]"..
"field[0.3,1.5;4,2;digiline_channel;Digiline Channel (optional):;${digiline_channel}]"..
"button_exit[7,0.75;2,3;;Save]")
end
local function object_detector_on_receive_fields(pos, _, fields)
local function object_detector_on_receive_fields(pos, formname, fields, sender)
if not fields.scanname or not fields.digiline_channel then return end
if minetest.is_protected(pos, sender:get_player_name()) then return end
local meta = minetest.get_meta(pos)
meta:set_string("scanname", fields.scanname)
meta:set_string("digiline_channel", fields.digiline_channel)
@ -28,14 +31,17 @@ local function object_detector_scan(pos)
if next(objs) == nil then return false end
local scanname = minetest.get_meta(pos):get_string("scanname")
local scan_for = {}
for _, str in pairs(string.split(scanname:gsub(" ", ""), ",")) do
scan_for[str] = true
end
local every_player = scanname == ""
for _, obj in pairs(objs) do
-- "" is returned if it is not a player; "" ~= nil; so only handle objects with foundname ~= ""
local foundname = obj:get_player_name()
if foundname ~= "" then
-- return true if scanning for any player or if specific playername was detected
if scanname == "" or foundname == scanname then
if every_player or scan_for[foundname] then
return true
end
end
@ -60,6 +66,7 @@ local object_detector_digiline = {
minetest.register_node("mesecons_detector:object_detector_off", {
tiles = {"default_steel_block.png", "default_steel_block.png", "jeija_object_detector_off.png", "jeija_object_detector_off.png", "jeija_object_detector_off.png", "jeija_object_detector_off.png"},
paramtype = "light",
is_ground_content = false,
walkable = true,
groups = {cracky=3},
description="Player Detector",
@ -70,12 +77,14 @@ minetest.register_node("mesecons_detector:object_detector_off", {
on_construct = object_detector_make_formspec,
on_receive_fields = object_detector_on_receive_fields,
sounds = default.node_sound_stone_defaults(),
digiline = object_detector_digiline
digiline = object_detector_digiline,
on_blast = mesecon.on_blastnode,
})
minetest.register_node("mesecons_detector:object_detector_on", {
tiles = {"default_steel_block.png", "default_steel_block.png", "jeija_object_detector_on.png", "jeija_object_detector_on.png", "jeija_object_detector_on.png", "jeija_object_detector_on.png"},
paramtype = "light",
is_ground_content = false,
walkable = true,
groups = {cracky=3,not_in_creative_inventory=1},
drop = 'mesecons_detector:object_detector_off',
@ -86,7 +95,8 @@ minetest.register_node("mesecons_detector:object_detector_on", {
on_construct = object_detector_make_formspec,
on_receive_fields = object_detector_on_receive_fields,
sounds = default.node_sound_stone_defaults(),
digiline = object_detector_digiline
digiline = object_detector_digiline,
on_blast = mesecon.on_blastnode,
})
minetest.register_craft({
@ -98,6 +108,15 @@ minetest.register_craft({
}
})
minetest.register_craft({
output = 'mesecons_detector:object_detector_off',
recipe = {
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
{"default:steel_ingot", "mesecons_microcontroller:microcontroller0000", "default:steel_ingot"},
{"default:steel_ingot", "group:mesecon_conductor_craftable", "default:steel_ingot"},
}
})
minetest.register_abm({
nodenames = {"mesecons_detector:object_detector_off"},
interval = 1,
@ -128,17 +147,23 @@ minetest.register_abm({
-- Detects the node in front of it
local function node_detector_make_formspec(pos)
minetest.get_meta(pos):set_string("formspec", "size[9,2.5]" ..
local meta = minetest.get_meta(pos)
if meta:get_string("distance") == "" then meta:set_string("distance", "0") end
meta:set_string("formspec", "size[9,2.5]" ..
"field[0.3, 0;9,2;scanname;Name of node to scan for (empty for any):;${scanname}]"..
"field[0.3,1.5;4,2;digiline_channel;Digiline Channel (optional):;${digiline_channel}]"..
"field[0.3,1.5;2.5,2;distance;Distance (0-"..mesecon.setting("node_detector_distance_max", 10).."):;${distance}]"..
"field[3,1.5;4,2;digiline_channel;Digiline Channel (optional):;${digiline_channel}]"..
"button_exit[7,0.75;2,3;;Save]")
end
local function node_detector_on_receive_fields(pos, _, fields)
local function node_detector_on_receive_fields(pos, fieldname, fields, sender)
if not fields.scanname or not fields.digiline_channel then return end
if minetest.is_protected(pos, sender:get_player_name()) then return end
local meta = minetest.get_meta(pos)
meta:set_string("scanname", fields.scanname)
meta:set_string("distance", fields.distance or "0")
meta:set_string("digiline_channel", fields.digiline_channel)
node_detector_make_formspec(pos)
end
@ -148,10 +173,17 @@ local function node_detector_scan(pos)
local node = minetest.get_node_or_nil(pos)
if not node then return end
local meta = minetest.get_meta(pos)
local distance = meta:get_int("distance")
local distance_max = mesecon.setting("node_detector_distance_max", 10)
if distance < 0 then distance = 0 end
if distance > distance_max then distance = distance_max end
local frontname = minetest.get_node(
vector.subtract(pos, minetest.facedir_to_dir(node.param2))
vector.subtract(pos, vector.multiply(minetest.facedir_to_dir(node.param2), distance + 1))
).name
local scanname = minetest.get_meta(pos):get_string("scanname")
local scanname = meta:get_string("scanname")
return (frontname == scanname) or
(frontname ~= "air" and frontname ~= "ignore" and scanname == "")
@ -162,11 +194,17 @@ local node_detector_digiline = {
effector = {
action = function(pos, node, channel, msg)
local meta = minetest.get_meta(pos)
local distance = meta:get_int("distance")
local distance_max = mesecon.setting("node_detector_distance_max", 10)
if distance < 0 then distance = 0 end
if distance > distance_max then distance = distance_max end
if channel ~= meta:get_string("digiline_channel") then return end
if msg == GET_COMMAND then
local nodename = minetest.get_node(
vector.subtract(pos, minetest.facedir_to_dir(node.param2))
vector.subtract(pos, vector.multiply(minetest.facedir_to_dir(node.param2), distance + 1))
).name
digiline:receptor_send(pos, digiline.rules.default, channel, nodename)
@ -200,6 +238,7 @@ minetest.register_node("mesecons_detector:node_detector_off", {
tiles = {"default_steel_block.png", "default_steel_block.png", "default_steel_block.png", "default_steel_block.png", "default_steel_block.png", "jeija_node_detector_off.png"},
paramtype = "light",
paramtype2 = "facedir",
is_ground_content = false,
walkable = true,
groups = {cracky=3},
description="Node Detector",
@ -208,15 +247,16 @@ minetest.register_node("mesecons_detector:node_detector_off", {
}},
on_construct = node_detector_make_formspec,
on_receive_fields = node_detector_on_receive_fields,
after_place_node = after_place_node_detector,
sounds = default.node_sound_stone_defaults(),
digiline = node_detector_digiline
digiline = node_detector_digiline,
on_blast = mesecon.on_blastnode,
})
minetest.register_node("mesecons_detector:node_detector_on", {
tiles = {"default_steel_block.png", "default_steel_block.png", "default_steel_block.png", "default_steel_block.png", "default_steel_block.png", "jeija_node_detector_on.png"},
paramtype = "light",
paramtype2 = "facedir",
is_ground_content = false,
walkable = true,
groups = {cracky=3,not_in_creative_inventory=1},
drop = 'mesecons_detector:node_detector_off',
@ -225,9 +265,9 @@ minetest.register_node("mesecons_detector:node_detector_on", {
}},
on_construct = node_detector_make_formspec,
on_receive_fields = node_detector_on_receive_fields,
after_place_node = after_place_node_detector,
sounds = default.node_sound_stone_defaults(),
digiline = node_detector_digiline
digiline = node_detector_digiline,
on_blast = mesecon.on_blastnode,
})
minetest.register_craft({
@ -239,6 +279,15 @@ minetest.register_craft({
}
})
minetest.register_craft({
output = 'mesecons_detector:node_detector_off',
recipe = {
{"default:steel_ingot", "group:mesecon_conductor_craftable", "default:steel_ingot"},
{"default:steel_ingot", "mesecons_microcontroller:microcontroller0000", "default:steel_ingot"},
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
}
})
minetest.register_abm({
nodenames = {"mesecons_detector:node_detector_off"},
interval = 1,

View File

@ -1,12 +1,15 @@
local screwdriver_exists = minetest.global_exists("screwdriver")
local corner_nodebox = {
type = "fixed",
-- ±0.001 is to prevent z-fighting
fixed = {{ -16/32-0.001, -17/32, -3/32, 0, -13/32, 3/32 },
{ -3/32, -17/32, -16/32+0.001, 3/32, -13/32, 3/32}}
}
local corner_selectionbox = {
type = "fixed",
fixed = { -16/32-0.001, -18/32, -16/32, 5/32, -12/32, 5/32 },
fixed = { -16/32, -16/32, -16/32, 5/32, -12/32, 5/32 },
}
local corner_get_rules = function (node)
@ -33,23 +36,27 @@ minetest.register_node("mesecons_extrawires:corner_on", {
},
paramtype = "light",
paramtype2 = "facedir",
is_ground_content = false,
walkable = false,
sunlight_propagates = true,
selection_box = corner_selectionbox,
node_box = corner_nodebox,
groups = {dig_immediate = 3, not_in_creative_inventory = 1},
drop = "mesecons_extrawires:corner_off",
sounds = default.node_sound_defaults(),
mesecons = {conductor =
{
state = mesecon.state.on,
rules = corner_get_rules,
offstate = "mesecons_extrawires:corner_off"
}}
}},
on_blast = mesecon.on_blastnode,
on_rotate = screwdriver_exists and screwdriver.rotate_simple,
})
minetest.register_node("mesecons_extrawires:corner_off", {
drawtype = "nodebox",
description = "Mesecon Corner",
description = "Insulated Mesecon Corner",
tiles = {
"jeija_insulated_wire_curved_tb_off.png",
"jeija_insulated_wire_curved_tb_off.png^[transformR270",
@ -60,17 +67,21 @@ minetest.register_node("mesecons_extrawires:corner_off", {
},
paramtype = "light",
paramtype2 = "facedir",
is_ground_content = false,
walkable = false,
sunlight_propagates = true,
selection_box = corner_selectionbox,
node_box = corner_nodebox,
groups = {dig_immediate = 3},
sounds = default.node_sound_defaults(),
mesecons = {conductor =
{
state = mesecon.state.off,
rules = corner_get_rules,
onstate = "mesecons_extrawires:corner_on"
}}
}},
on_blast = mesecon.on_blastnode,
on_rotate = screwdriver_exists and screwdriver.rotate_simple,
})
minetest.register_craft({

View File

@ -1,4 +1,4 @@
function crossover_get_rules(node)
local function crossover_get_rules(node)
return {
{--first wire
{x=-1,y=0,z=0},
@ -19,143 +19,110 @@ local crossover_states = {
}
minetest.register_node("mesecons_extrawires:crossover_off", {
description = "Insulated Crossover",
drawtype = "nodebox",
description = "Insulated Mesecon Crossover",
drawtype = "mesh",
mesh = "mesecons_extrawires_crossover.b3d",
tiles = {
"jeija_insulated_wire_crossing_tb_off.png",
"jeija_insulated_wire_crossing_tb_off.png",
"jeija_insulated_wire_ends_off.png",
"jeija_insulated_wire_sides_off.png",
"jeija_insulated_wire_sides_off.png",
"jeija_insulated_wire_ends_off.png"
},
paramtype = "light",
is_ground_content = false,
walkable = false,
stack_max = 99,
selection_box = {type="fixed", fixed={-16/32-0.0001, -18/32, -16/32-0.001, 16/32+0.001, -5/32, 16/32+0.001}},
node_box = {
type = "fixed",
fixed = {
{ -16/32-0.001, -17/32, -3/32, 16/32+0.001, -13/32, 3/32 },
{ -3/32, -17/32, -16/32-0.001, 3/32, -13/32, -6/32 },
{ -3/32, -13/32, -9/32, 3/32, -6/32, -6/32 },
{ -3/32, -9/32, -9/32, 3/32, -6/32, 9/32 },
{ -3/32, -13/32, 6/32, 3/32, -6/32, 9/32 },
{ -3/32, -17/32, 6/32, 3/32, -13/32, 16/32+0.001 },
},
},
selection_box = {type="fixed", fixed={-16/32, -16/32, -16/32, 16/32, -5/32, 16/32}},
groups = {dig_immediate=3, mesecon=3},
sounds = default.node_sound_defaults(),
mesecons = {
conductor = {
states = crossover_states,
rules = crossover_get_rules(),
}
},
on_blast = mesecon.on_blastnode,
})
minetest.register_node("mesecons_extrawires:crossover_01", {
description = "You hacker you!",
drop = "mesecons_extrawires:crossover_off",
drawtype = "nodebox",
drawtype = "mesh",
mesh = "mesecons_extrawires_crossover.b3d",
tiles = {
"jeija_insulated_wire_crossing_tb_01.png",
"jeija_insulated_wire_crossing_tb_01.png",
"jeija_insulated_wire_ends_01x.png",
"jeija_insulated_wire_ends_01x.png",
"jeija_insulated_wire_ends_01z.png",
"jeija_insulated_wire_ends_01z.png"
"jeija_insulated_wire_ends_on.png",
"jeija_insulated_wire_sides_on.png",
"jeija_insulated_wire_sides_off.png",
"jeija_insulated_wire_ends_off.png"
},
paramtype = "light",
is_ground_content = false,
walkable = false,
stack_max = 99,
selection_box = {type="fixed", fixed={-16/32-0.0001, -18/32, -16/32-0.001, 16/32+0.001, -5/32, 16/32+0.001}},
node_box = {
type = "fixed",
fixed = {
{ -16/32-0.001, -17/32, -3/32, 16/32+0.001, -13/32, 3/32 },
{ -3/32, -17/32, -16/32-0.001, 3/32, -13/32, -6/32 },
{ -3/32, -13/32, -9/32, 3/32, -6/32, -6/32 },
{ -3/32, -9/32, -9/32, 3/32, -6/32, 9/32 },
{ -3/32, -13/32, 6/32, 3/32, -6/32, 9/32 },
{ -3/32, -17/32, 6/32, 3/32, -13/32, 16/32+0.001 },
},
},
selection_box = {type="fixed", fixed={-16/32, -16/32, -16/32, 16/32, -5/32, 16/32}},
groups = {dig_immediate=3, mesecon=3, not_in_creative_inventory=1},
sounds = default.node_sound_defaults(),
mesecons = {
conductor = {
states = crossover_states,
rules = crossover_get_rules(),
}
},
on_blast = mesecon.on_blastnode,
})
minetest.register_node("mesecons_extrawires:crossover_10", {
description = "You hacker you!",
drop = "mesecons_extrawires:crossover_off",
drawtype = "nodebox",
drawtype = "mesh",
mesh = "mesecons_extrawires_crossover.b3d",
tiles = {
"jeija_insulated_wire_crossing_tb_10.png",
"jeija_insulated_wire_crossing_tb_10.png",
"jeija_insulated_wire_ends_10x.png",
"jeija_insulated_wire_ends_10x.png",
"jeija_insulated_wire_ends_10z.png",
"jeija_insulated_wire_ends_10z.png"
"jeija_insulated_wire_ends_off.png",
"jeija_insulated_wire_sides_off.png",
"jeija_insulated_wire_sides_on.png",
"jeija_insulated_wire_ends_on.png"
},
paramtype = "light",
is_ground_content = false,
walkable = false,
stack_max = 99,
selection_box = {type="fixed", fixed={-16/32-0.0001, -18/32, -16/32-0.001, 16/32+0.001, -5/32, 16/32+0.001}},
node_box = {
type = "fixed",
fixed = {
{ -16/32-0.001, -17/32, -3/32, 16/32+0.001, -13/32, 3/32 },
{ -3/32, -17/32, -16/32-0.001, 3/32, -13/32, -6/32 },
{ -3/32, -13/32, -9/32, 3/32, -6/32, -6/32 },
{ -3/32, -9/32, -9/32, 3/32, -6/32, 9/32 },
{ -3/32, -13/32, 6/32, 3/32, -6/32, 9/32 },
{ -3/32, -17/32, 6/32, 3/32, -13/32, 16/32+0.001 },
},
},
selection_box = {type="fixed", fixed={-16/32, -16/32, -16/32, 16/32, -5/32, 16/32}},
groups = {dig_immediate=3, mesecon=3, not_in_creative_inventory=1},
sounds = default.node_sound_defaults(),
mesecons = {
conductor = {
states = crossover_states,
rules = crossover_get_rules(),
}
},
on_blast = mesecon.on_blastnode,
})
minetest.register_node("mesecons_extrawires:crossover_on", {
description = "You hacker you!",
drop = "mesecons_extrawires:crossover_off",
drawtype = "nodebox",
drawtype = "mesh",
mesh = "mesecons_extrawires_crossover.b3d",
tiles = {
"jeija_insulated_wire_crossing_tb_on.png",
"jeija_insulated_wire_crossing_tb_on.png",
"jeija_insulated_wire_ends_on.png",
"jeija_insulated_wire_ends_on.png",
"jeija_insulated_wire_ends_on.png",
"jeija_insulated_wire_sides_on.png",
"jeija_insulated_wire_sides_on.png",
"jeija_insulated_wire_ends_on.png"
},
paramtype = "light",
is_ground_content = false,
walkable = false,
stack_max = 99,
selection_box = {type="fixed", fixed={-16/32-0.0001, -18/32, -16/32-0.001, 16/32+0.001, -5/32, 16/32+0.001}},
node_box = {
type = "fixed",
fixed = {
{ -16/32-0.001, -17/32, -3/32, 16/32+0.001, -13/32, 3/32 },
{ -3/32, -17/32, -16/32-0.001, 3/32, -13/32, -6/32 },
{ -3/32, -13/32, -9/32, 3/32, -6/32, -6/32 },
{ -3/32, -9/32, -9/32, 3/32, -6/32, 9/32 },
{ -3/32, -13/32, 6/32, 3/32, -6/32, 9/32 },
{ -3/32, -17/32, 6/32, 3/32, -13/32, 16/32+0.001 },
},
},
selection_box = {type="fixed", fixed={-16/32, -16/32, -16/32, 16/32, -5/32, 16/32}},
groups = {dig_immediate=3, mesecon=3, not_in_creative_inventory=1},
sounds = default.node_sound_defaults(),
mesecons = {
conductor = {
states = crossover_states,
rules = crossover_get_rules(),
}
},
on_blast = mesecon.on_blastnode,
})
minetest.register_craft({

View File

@ -1,2 +1,3 @@
default
mesecons
screwdriver?

View File

@ -1 +1 @@
Insulated corners are conductors that only conduct between the inputs (also not up or down). When placing they always point to the left in direction of your vision.
Insulated corners are conductors that only conduct between the inputs (also not up or down). When placing they always point to the left in direction of your vision. Like uninsulated wires, they work through unloaded blocks.

View File

@ -1 +1 @@
Insulated crossing are conductors that conduct two signals between the opposing sides, the signals are insulated to each other.
Insulated crossing are conductors that conduct two signals between the opposing sides, the signals are insulated to each other. Like uninsulated wires, they work through unloaded blocks.

View File

@ -1 +1 @@
The basic prerequesite for mesecons, can be crafted into wires and other stuff. Have a look at the <a href="http://wiki.minetest.net/Mese">Minetest Wiki</a> for more information. Mese is a conductor. It conducts in all six directions: Up/Down/Left/Right/Forward/Backward
The basic prerequesite for mesecons, can be crafted into wires and other stuff. Have a look at the <a href="http://wiki.minetest.net/Mese">Minetest Wiki</a> for more information. Mese is a conductor. It conducts in all six directions: Up/Down/Left/Right/Forward/Backward. Like horizontal wires, Mese conduction works through unloaded blocks.

View File

@ -1 +1 @@
Insulated T-Junctions are conductors that only conduct between the inputs (also not up or down).
Insulated T-Junctions are conductors that only conduct between the inputs (also not up or down). Like uninsulated wires, they work through unloaded blocks.

View File

@ -1 +1 @@
Vertical Mesecons only conduct up and down. Plates appear at the ends, at that place they also conduct to the side.
Vertical Mesecons only conduct up and down. Plates appear at the ends, at that place they also conduct to the side. Like horizontal wires, they work through unloaded blocks.

View File

@ -26,7 +26,8 @@ local powered_def = mesecon.mergetable(minetest.registered_nodes["default:mese"]
offstate = "default:mese",
rules = mesewire_rules
}},
groups = {cracky = 1, not_in_creative_inventory = 1}
groups = {cracky = 1, not_in_creative_inventory = 1},
on_blast = mesecon.on_blastnode,
})
for i, v in pairs(powered_def.tiles) do

View File

@ -1,12 +1,15 @@
local screwdriver_exists = minetest.global_exists("screwdriver")
local tjunction_nodebox = {
type = "fixed",
-- ±0.001 is to prevent z-fighting
fixed = {{ -16/32-0.001, -17/32, -3/32, 16/32+0.001, -13/32, 3/32 },
{ -3/32, -17/32, -16/32+0.001, 3/32, -13/32, -3/32},}
}
local tjunction_selectionbox = {
type = "fixed",
fixed = { -16/32-0.001, -18/32, -16/32, 16/32+0.001, -12/32, 7/32 },
fixed = { -16/32, -16/32, -16/32, 16/32, -12/32, 7/32 },
}
local tjunction_get_rules = function (node)
@ -34,23 +37,27 @@ minetest.register_node("mesecons_extrawires:tjunction_on", {
},
paramtype = "light",
paramtype2 = "facedir",
is_ground_content = false,
walkable = false,
sunlight_propagates = true,
selection_box = tjunction_selectionbox,
node_box = tjunction_nodebox,
groups = {dig_immediate = 3, not_in_creative_inventory = 1},
drop = "mesecons_extrawires:tjunction_off",
sounds = default.node_sound_defaults(),
mesecons = {conductor =
{
state = mesecon.state.on,
rules = tjunction_get_rules,
offstate = "mesecons_extrawires:tjunction_off"
}}
}},
on_blast = mesecon.on_blastnode,
on_rotate = screwdriver_exists and screwdriver.rotate_simple,
})
minetest.register_node("mesecons_extrawires:tjunction_off", {
drawtype = "nodebox",
description = "T-junction",
description = "Insulated Mesecon T-junction",
tiles = {
"jeija_insulated_wire_tjunction_tb_off.png",
"jeija_insulated_wire_tjunction_tb_off.png^[transformR180",
@ -61,17 +68,21 @@ minetest.register_node("mesecons_extrawires:tjunction_off", {
},
paramtype = "light",
paramtype2 = "facedir",
is_ground_content = false,
walkable = false,
sunlight_propagates = true,
selection_box = tjunction_selectionbox,
node_box = tjunction_nodebox,
groups = {dig_immediate = 3},
sounds = default.node_sound_defaults(),
mesecons = {conductor =
{
state = mesecon.state.off,
rules = tjunction_get_rules,
onstate = "mesecons_extrawires:tjunction_on"
}}
}},
on_blast = mesecon.on_blastnode,
on_rotate = screwdriver_exists and screwdriver.rotate_simple,
})
minetest.register_craft({

View File

@ -77,17 +77,19 @@ end
-- Vertical wire
mesecon.register_node("mesecons_extrawires:vertical", {
description = "Vertical mesecon",
description = "Vertical Mesecon",
drawtype = "nodebox",
walkable = false,
paramtype = "light",
is_ground_content = false,
sunlight_propagates = true,
selection_box = vertical_box,
node_box = vertical_box,
is_vertical_conductor = true,
drop = "mesecons_extrawires:vertical_off",
after_place_node = vertical_update,
after_dig_node = vertical_update
after_dig_node = vertical_update,
sounds = default.node_sound_defaults(),
},{
tiles = {"mesecons_wire_off.png"},
groups = {dig_immediate=3},
@ -112,6 +114,7 @@ mesecon.register_node("mesecons_extrawires:vertical_top", {
drawtype = "nodebox",
walkable = false,
paramtype = "light",
is_ground_content = false,
sunlight_propagates = true,
groups = {dig_immediate=3, not_in_creative_inventory=1},
selection_box = top_box,
@ -119,7 +122,8 @@ mesecon.register_node("mesecons_extrawires:vertical_top", {
is_vertical_conductor = true,
drop = "mesecons_extrawires:vertical_off",
after_place_node = vertical_update,
after_dig_node = vertical_update
after_dig_node = vertical_update,
sounds = default.node_sound_defaults(),
},{
tiles = {"mesecons_wire_off.png"},
mesecons = {conductor = {
@ -142,6 +146,7 @@ mesecon.register_node("mesecons_extrawires:vertical_bottom", {
drawtype = "nodebox",
walkable = false,
paramtype = "light",
is_ground_content = false,
sunlight_propagates = true,
groups = {dig_immediate = 3, not_in_creative_inventory = 1},
selection_box = bottom_box,
@ -149,7 +154,8 @@ mesecon.register_node("mesecons_extrawires:vertical_bottom", {
is_vertical_conductor = true,
drop = "mesecons_extrawires:vertical_off",
after_place_node = vertical_update,
after_dig_node = vertical_update
after_dig_node = vertical_update,
sounds = default.node_sound_defaults(),
},{
tiles = {"mesecons_wire_off.png"},
mesecons = {conductor = {

View File

@ -0,0 +1,2 @@
mesecons
screwdriver?

View File

@ -0,0 +1,6 @@
FPGAs can be used to chain multiple logic gates together in a compact manner.
They come with 4 I/O ports and 10 internal registers,
which can then be connected with each other to form logic circuits.
They work fine in unloaded blocks.<br />
Supported gate types: <b>AND</b>, <b>OR</b>, <b>NOT</b>, <b>XOR</b>, <b>NAND</b>, <b>XNOR</b>, <b>Buffer</b> (=)<br />
I/O ports: <b>A B C D</b>; Registers: numbered <b>0</b> to <b>9</b>

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -0,0 +1,3 @@
The FPGA programmer can be used to copy gate configurations from one FPGA to another.<br />
Shift+Right-Click an FPGA to read its configuration and "remember" it.
Left-click (punch) FPGAs to write the saved configuration to them.

Binary file not shown.

After

Width:  |  Height:  |  Size: 598 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

420
mesecons_fpga/init.lua Normal file
View File

@ -0,0 +1,420 @@
local plg = {}
plg.rules = {}
local lcore = dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/logic.lua")
dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/tool.lua")(plg)
plg.register_nodes = function(template)
-- each loop is for one of the 4 IO ports
for a = 0, 1 do
for b = 0, 1 do
for c = 0, 1 do
for d = 0, 1 do
local ndef = table.copy(template)
local nodename = "mesecons_fpga:fpga"
.. tostring(d) .. tostring(c) .. tostring(b) .. tostring(a)
-- build top texture string
local texture = "jeija_fpga_top.png"
if a == 1 then texture = texture .. "^jeija_microcontroller_LED_A.png" end
if b == 1 then texture = texture .. "^jeija_microcontroller_LED_B.png" end
if c == 1 then texture = texture .. "^jeija_microcontroller_LED_C.png" end
if d == 1 then texture = texture .. "^jeija_microcontroller_LED_D.png" end
ndef.tiles[1] = texture
ndef.inventory_image = texture
if (a + b + c + d) > 0 then
ndef.groups["not_in_creative_inventory"] = 1
end
-- interaction with mesecons (input / output)
local rules_out = {}
if a == 1 then table.insert(rules_out, {x = -1, y = 0, z = 0}) end
if b == 1 then table.insert(rules_out, {x = 0, y = 0, z = 1}) end
if c == 1 then table.insert(rules_out, {x = 1, y = 0, z = 0}) end
if d == 1 then table.insert(rules_out, {x = 0, y = 0, z = -1}) end
plg.rules[nodename] = rules_out
local rules_in = {}
if a == 0 then table.insert(rules_in, {x = -1, y = 0, z = 0}) end
if b == 0 then table.insert(rules_in, {x = 0, y = 0, z = 1}) end
if c == 0 then table.insert(rules_in, {x = 1, y = 0, z = 0}) end
if d == 0 then table.insert(rules_in, {x = 0, y = 0, z = -1}) end
ndef.mesecons.effector.rules = rules_in
if (a + b + c + d) > 0 then
ndef.mesecons.receptor = {
state = mesecon.state.on,
rules = rules_out,
}
end
minetest.register_node(nodename, ndef)
end
end
end
end
end
plg.register_nodes({
description = "FPGA",
drawtype = "nodebox",
tiles = {
"", -- replaced later
"jeija_microcontroller_bottom.png",
"jeija_fpga_sides.png",
"jeija_fpga_sides.png",
"jeija_fpga_sides.png",
"jeija_fpga_sides.png"
},
inventory_image = "", -- replaced later
is_ground_content = false,
sunlight_propagates = true,
paramtype = "light",
walkable = true,
groups = {dig_immediate = 2, mesecon = 3, overheat = 1},
drop = "mesecons_fpga:fpga0000",
selection_box = {
type = "fixed",
fixed = { -8/16, -8/16, -8/16, 8/16, -5/16, 8/16 },
},
node_box = {
type = "fixed",
fixed = {
{ -8/16, -8/16, -8/16, 8/16, -7/16, 8/16 }, -- bottom slab
{ -5/16, -7/16, -5/16, 5/16, -6/16, 5/16 }, -- circuit board
{ -3/16, -6/16, -3/16, 3/16, -5/16, 3/16 }, -- IC
}
},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
local is = { {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {} }
meta:set_string("instr", lcore.serialize(is))
meta:set_int("valid", 0)
meta:set_string("formspec", plg.to_formspec_string(is))
meta:set_string("infotext", "FPGA")
end,
on_receive_fields = function(pos, formname, fields, sender)
if fields.program == nil then return end -- we only care when the user clicks "Program"
local meta = minetest.get_meta(pos)
local is = plg.from_formspec_fields(fields)
meta:set_string("instr", lcore.serialize(is))
plg.update_formspec(pos, is)
end,
sounds = default.node_sound_stone_defaults(),
mesecons = {
effector = {
rules = {}, -- replaced later
action_change = function(pos, node, rule, newstate)
plg.ports_changed(pos, rule, newstate)
plg.update(pos)
end
}
},
after_dig_node = function(pos, node)
mesecon.receptor_off(pos, plg.rules[node.name])
end,
on_blast = mesecon.on_blastnode,
on_rotate = function(pos, node, user, mode)
local abcd1 = {"A", "B", "C", "D"}
local abcd2 = {A = 1, B = 2, C = 3, D = 4}
local ops = {"op1", "op2", "dst"}
local dir = 0
if mode == screwdriver.ROTATE_FACE then -- clock-wise
dir = 1
if user and user:is_player() then
minetest.chat_send_player(user:get_player_name(),
"FPGA ports have been rotated clockwise.")
end
elseif mode == screwdriver.ROTATE_AXIS then -- counter-clockwise
dir = -1
if user and user:is_player() then
minetest.chat_send_player(user:get_player_name(),
"FPGA ports have been rotated counter-clockwise.")
end
end
local meta = minetest.get_meta(pos)
local instr = lcore.deserialize(meta:get_string("instr"))
for i = 1, #instr do
for _, op in ipairs(ops) do
local o = instr[i][op]
if o and o.type == "io" then
local num = abcd2[o.port]
num = num + dir
if num > 4 then num = 1
elseif num < 1 then num = 4 end
instr[i][op].port = abcd1[num]
end
end
end
meta:set_string("instr", lcore.serialize(instr))
plg.update_formspec(pos, instr)
return true
end,
})
plg.to_formspec_string = function(is)
local function dropdown_op(x, y, name, val)
local s = "dropdown[" .. tostring(x) .. "," .. tostring(y) .. ";"
.. "0.75,0.5;" .. name .. ";" -- the height seems to be ignored?
s = s .. " ,A,B,C,D,0,1,2,3,4,5,6,7,8,9;"
if val == nil then
s = s .. "0" -- actually selects no field at all
elseif val.type == "io" then
local mapping = {
["A"] = 1,
["B"] = 2,
["C"] = 3,
["D"] = 4,
}
s = s .. tostring(1 + mapping[val.port])
else -- "reg"
s = s .. tostring(6 + val.n)
end
return s .. "]"
end
local function dropdown_action(x, y, name, val)
local s = "dropdown[" .. tostring(x) .. "," .. tostring(y) .. ";"
.. "1.125,0.5;" .. name .. ";" -- the height seems to be ignored?
s = s .. " , AND, OR, NOT, XOR,NAND, =,XNOR;"
if val == nil then
return s .. "0]" -- actually selects no field at all
end
local mapping = {
["and"] = 1,
["or"] = 2,
["not"] = 3,
["xor"] = 4,
["nand"] = 5,
["buf"] = 6,
["xnor"] = 7,
}
return s .. tostring(1 + mapping[val]) .. "]"
end
local s = "size[9,9]"..
"label[3.4,-0.15;FPGA gate configuration]"..
"button_exit[7,7.5;2,2.5;program;Program]"..
"box[4.2,0.5;0.03,7;#ffffff]"..
"label[0.25,0.25;op. 1]"..
"label[1.0,0.25;gate type]"..
"label[2.125,0.25;op. 2]"..
"label[3.15,0.25;dest]"..
"label[4.5,0.25;op. 1]"..
"label[5.25,0.25;gate type]"..
"label[6.375,0.25;op. 2]"..
"label[7.4,0.25;dest]"
local x = 1 - 0.75
local y = 1 - 0.25
for i = 1, 14 do
local cur = is[i]
s = s .. dropdown_op (x , y, tostring(i).."op1", cur.op1)
s = s .. dropdown_action(x+0.75 , y, tostring(i).."act", cur.action)
s = s .. dropdown_op (x+1.875, y, tostring(i).."op2", cur.op2)
s = s .. "label[" .. tostring(x+2.625) .. "," .. tostring(y+0.1) .. "; ->]"
s = s .. dropdown_op (x+2.9 , y, tostring(i).."dst", cur.dst)
y = y + 1
if i == 7 then
x = 4.5
y = 1 - 0.25
end
end
return s
end
plg.from_formspec_fields = function(fields)
local function read_op(s)
if s == nil or s == " " then
return nil
elseif s == "A" or s == "B" or s == "C" or s == "D" then
return {type = "io", port = s}
else
return {type = "reg", n = tonumber(s)}
end
end
local function read_action(s)
if s == nil or s == " " then
return nil
end
local mapping = {
["AND"] = "and",
["OR"] = "or",
["NOT"] = "not",
["XOR"] = "xor",
["NAND"] = "nand",
["="] = "buf",
["XNOR"] = "xnor",
}
s = s:gsub("^%s*", "") -- remove leading spaces
return mapping[s]
end
local is = {}
for i = 1, 14 do
local cur = {}
cur.op1 = read_op(fields[tonumber(i) .. "op1"])
cur.action = read_action(fields[tonumber(i) .. "act"])
cur.op2 = read_op(fields[tonumber(i) .. "op2"])
cur.dst = read_op(fields[tonumber(i) .. "dst"])
is[#is + 1] = cur
end
return is
end
plg.update_formspec = function(pos, is)
if type(is) == "string" then -- serialized string
is = lcore.deserialize(is)
end
local meta = minetest.get_meta(pos)
local form = plg.to_formspec_string(is)
local err = lcore.validate(is)
if err == nil then
meta:set_int("valid", 1)
meta:set_string("infotext", "FPGA (functional)")
else
meta:set_int("valid", 0)
meta:set_string("infotext", "FPGA")
local fmsg = minetest.colorize("#ff0000", minetest.formspec_escape(err.msg))
form = form .. plg.red_box_around(err.i) ..
"label[0.25,8.25;The gate configuration is erroneous in the marked area:]"..
"label[0.25,8.5;" .. fmsg .. "]"
end
meta:set_string("formspec", form)
-- reset ports and run programmed logic
plg.setports(pos, false, false, false, false)
plg.update(pos)
end
plg.red_box_around = function(i)
local x, y
if i > 7 then
x = 4.5
y = 0.75 + (i - 8)
else
x = 0.25
y = 0.75 + (i - 1)
end
return string.format("box[%f,%f;3.8,0.8;#ff0000]", x-0.1, y-0.05)
end
plg.update = function(pos)
local meta = minetest.get_meta(pos)
if meta:get_int("valid") ~= 1 then
return
elseif mesecon.do_overheat(pos) then
plg.setports(pos, false, false, false, false)
meta:set_int("valid", 0)
meta:set_string("infotext", "FPGA (overheated)")
return
end
local is = lcore.deserialize(meta:get_string("instr"))
local A, B, C, D = plg.getports(pos)
A, B, C, D = lcore.interpret(is, A, B, C, D)
plg.setports(pos, A, B, C, D)
end
plg.ports_changed = function(pos, rule, newstate)
if rule == nil then return end
local meta = minetest.get_meta(pos)
local states
local s = meta:get_string("portstates")
if s == nil then
states = {false, false, false, false}
else
states = {
s:sub(1, 1) == "1",
s:sub(2, 2) == "1",
s:sub(3, 3) == "1",
s:sub(4, 4) == "1",
}
end
-- trick to transform rules (see register_node) into port number
local portno = ({4, 1, nil, 3, 2})[3 + rule.x + 2*rule.z]
states[portno] = (newstate == "on")
meta:set_string("portstates",
(states[1] and "1" or "0") .. (states[2] and "1" or "0") ..
(states[3] and "1" or "0") .. (states[4] and "1" or "0")
)
end
plg.getports = function(pos) -- gets merged states of INPUT & OUTPUT
local sin, sout
local s = minetest.get_meta(pos):get_string("portstates")
if s == nil then
sin = {false, false, false, false}
else
sin = {
s:sub(1, 1) == "1",
s:sub(2, 2) == "1",
s:sub(3, 3) == "1",
s:sub(4, 4) == "1",
}
end
local name = minetest.get_node(pos).name
assert(name:find("mesecons_fpga:fpga") == 1)
local off = #"mesecons_fpga:fpga"
sout = {
name:sub(off+4, off+4) == "1",
name:sub(off+3, off+3) == "1",
name:sub(off+2, off+2) == "1",
name:sub(off+1, off+1) == "1",
}
return unpack({
sin[1] or sout[1],
sin[2] or sout[2],
sin[3] or sout[3],
sin[4] or sout[4],
})
end
plg.setports = function(pos, A, B, C, D) -- sets states of OUTPUT
local base = "mesecons_fpga:fpga"
local name = base
.. (D and "1" or "0") .. (C and "1" or "0")
.. (B and "1" or "0") .. (A and "1" or "0")
minetest.swap_node(pos, {name = name, param2 = minetest.get_node(pos).param2})
if A ~= nil then
local ru = plg.rules[base .. "0001"]
if A then mesecon.receptor_on(pos, ru) else mesecon.receptor_off(pos, ru) end
end
if B ~= nil then
local ru = plg.rules[base .. "0010"]
if B then mesecon.receptor_on(pos, ru) else mesecon.receptor_off(pos, ru) end
end
if C ~= nil then
local ru = plg.rules[base .. "0100"]
if C then mesecon.receptor_on(pos, ru) else mesecon.receptor_off(pos, ru) end
end
if D ~= nil then
local ru = plg.rules[base .. "1000"]
if D then mesecon.receptor_on(pos, ru) else mesecon.receptor_off(pos, ru) end
end
end
minetest.register_craft({
output = "mesecons_fpga:fpga0000 2",
recipe = {
{'group:mesecon_conductor_craftable', 'group:mesecon_conductor_craftable'},
{'mesecons_materials:silicon', 'mesecons_materials:silicon'},
{'group:mesecon_conductor_craftable', 'group:mesecon_conductor_craftable'},
}
})

210
mesecons_fpga/logic.lua Normal file
View File

@ -0,0 +1,210 @@
local lg = {}
-- (de)serialize
lg.serialize = function(t)
local function _op(t)
if t == nil then
return " "
elseif t.type == "io" then
return t.port
else -- t.type == "reg"
return tostring(t.n)
end
end
local function _action(s)
if s == nil then
return " "
end
local mapping = {
["and"] = "&",
["or"] = "|",
["not"] = "~",
["xor"] = "^",
["nand"] = "?", --dunno
["buf"] = "_",
["xnor"] = "=",
}
return mapping[s]
end
local s = ""
for i = 1, 14 do
local cur = t[i]
if next(cur) ~= nil then
s = s .. _op(cur.op1) .. _action(cur.action) .. _op(cur.op2) .. _op(cur.dst)
end
s = s .. "/"
end
return s
end
lg.deserialize = function(s)
local function _op(c)
if c == "A" or c == "B" or c == "C" or c == "D" then
return {type = "io", port = c}
elseif c == " " then
return nil
else
return {type = "reg", n = tonumber(c)}
end
end
local function _action(c)
local mapping = {
["&"] = "and",
["|"] = "or",
["~"] = "not",
["^"] = "xor",
["?"] = "nand",
["_"] = "buf",
["="] = "xnor",
[" "] = nil,
}
return mapping[c]
end
local ret = {}
for part in s:gmatch("(.-)/") do
local parsed
if part == "" then
parsed = {}
else
parsed = {
action = _action( part:sub(2,2) ),
op1 = _op( part:sub(1,1) ),
op2 = _op( part:sub(3,3) ),
dst = _op( part:sub(4,4) ),
}
end
ret[#ret + 1] = parsed
end
-- More than 14 instructions (write to all 10 regs + 4 outputs)
-- will not pass the write-once requirement of the validator
assert(#ret == 14)
return ret
end
-- validation
lg.validate_single = function(t, i)
local function is_reg_written_to(t, n, max)
for i = 1, max-1 do
if next(t[i]) ~= nil
and t[i].dst and t[i].dst.type == "reg"
and t[i].dst.n == n then
return true
end
end
return false
end
local function compare_op(t1, t2, allow_same_io)
if t1 == nil or t2 == nil then
return false
elseif t1.type ~= t2.type then
return false
end
if t1.type == "reg" and t1.n == t2.n then
return true
elseif t1.type == "io" and t1.port == t2.port then
return not allow_same_io
end
return false
end
local elem = t[i]
-- check for completeness
if elem.action == nil then
return {i = i, msg = "Gate type required"}
elseif elem.action == "not" or elem.action == "buf" then
if elem.op1 ~= nil or elem.op2 == nil or elem.dst == nil then
return {i = i, msg = "Second operand (only) and destination required"}
end
else
if elem.op1 == nil or elem.op2 == nil or elem.dst == nil then
return {i = i, msg = "Operands and destination required"}
end
end
-- check whether operands/destination are identical
if compare_op(elem.op1, elem.op2) then
return {i = i, msg = "Operands cannot be identical"}
end
if compare_op(elem.op1, elem.dst, true) or compare_op(elem.op2, elem.dst, true) then
return {i = i, msg = "Destination and operands must be different"}
end
-- check whether operands point to defined registers
if elem.op1 ~= nil and elem.op1.type == "reg"
and not is_reg_written_to(t, elem.op1.n, i) then
return {i = i, msg = "First operand is undefined register"}
end
if elem.op2.type == "reg" and not is_reg_written_to(t, elem.op2.n, i) then
return {i = i, msg = "Second operand is undefined register"}
end
-- check whether destination points to undefined register
if elem.dst.type == "reg" and is_reg_written_to(t, elem.dst.n, i) then
return {i = i, msg = "Destination is already used register"}
end
return nil
end
lg.validate = function(t)
for i = 1, 14 do
if next(t[i]) ~= nil then
local r = lg.validate_single(t, i)
if r ~= nil then
return r
end
end
end
return nil
end
-- interpreter
lg.interpret = function(t, a, b, c, d)
local function _action(s, v1, v2)
if s == "and" then
return v1 and v2
elseif s == "or" then
return v1 or v2
elseif s == "not" then
return not v2
elseif s == "xor" then
return v1 ~= v2
elseif s == "nand" then
return not (v1 and v2)
elseif s == "buf" then
return v2
else -- s == "xnor"
return v1 == v2
end
end
local function _op(t, regs, io_in)
if t.type == "reg" then
return regs[t.n]
else -- t.type == "io"
return io_in[t.port]
end
end
local io_in = {A=a, B=b, C=c, D=d}
local regs = {}
local io_out = {}
for i = 1, 14 do
local cur = t[i]
if next(cur) ~= nil then
local v1, v2
if cur.op1 ~= nil then
v1 = _op(cur.op1, regs, io_in)
end
v2 = _op(cur.op2, regs, io_in)
local result = _action(cur.action, v1, v2)
if cur.dst.type == "reg" then
regs[cur.dst.n] = result
else -- cur.dst.type == "io"
io_out[cur.dst.port] = result
end
end
end
return io_out.A, io_out.B, io_out.C, io_out.D
end
return lg

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 816 B

62
mesecons_fpga/tool.lua Normal file
View File

@ -0,0 +1,62 @@
return function(plg)
minetest.register_tool("mesecons_fpga:programmer", {
description = "FPGA Programmer",
inventory_image = "jeija_fpga_programmer.png",
stack_max = 1,
on_place = function(itemstack, placer, pointed_thing)
if pointed_thing.type ~= "node" then
return itemstack
end
local pos = pointed_thing.under
if minetest.get_node(pos).name:find("mesecons_fpga:fpga") ~= 1 then
return itemstack
end
local meta = minetest.get_meta(pos)
if meta:get_string("instr") == "//////////////" then
minetest.chat_send_player(placer:get_player_name(), "This FPGA is unprogrammed.")
return itemstack
end
itemstack:set_metadata(meta:get_string("instr"))
minetest.chat_send_player(placer:get_player_name(), "FPGA gate configuration was successfully copied!")
return itemstack
end,
on_use = function(itemstack, user, pointed_thing)
if pointed_thing.type ~= "node" then
return itemstack
end
local pos = pointed_thing.under
if minetest.get_node(pos).name:find("mesecons_fpga:fpga") ~= 1 then
return itemstack
end
local imeta = itemstack:get_metadata()
if imeta == "" then
minetest.chat_send_player(user:get_player_name(), "Use shift+right-click to copy a gate configuration first.")
return itemstack
end
local meta = minetest.get_meta(pos)
meta:set_string("instr", imeta)
plg.update_formspec(pos, imeta)
minetest.chat_send_player(user:get_player_name(), "Gate configuration was successfully written to FPGA!")
return itemstack
end
})
minetest.register_craft({
output = "mesecons_fpga:programmer",
recipe = {
{'group:mesecon_conductor_craftable'},
{'mesecons_materials:silicon'},
}
})
end

View File

@ -1 +1,2 @@
And gates power their output if both inputs (from left and right) are powered.
AND gates power their output if both inputs (from left and right) are powered.
They work in unloaded blocks.

View File

@ -1 +1,2 @@
Mesecon diodes, just like real ones, only transfer power (signals) in one direction only.
Diodes conduct signals in one direction only.
They work in unloaded blocks.

View File

@ -1 +1,2 @@
Nand gates do not power their output if both inputs (from left and right) are powered, but power it in every other case.
NAND gates do not power their output if both inputs (from left and right) are powered, but power it in every other case.
They work in unloaded blocks.

View File

@ -1 +1,2 @@
Nor gates only power their output if none of their two inputs is powered. They are basically or gates with a not gate at their output.
NOR gates only power their output if none of their two inputs is powered. They are basically OR gates with a NOT gate at their output.
They work in unloaded blocks.

View File

@ -1 +1,2 @@
Not gates invert signals, just like a mesecon torch does, but faster. The input is at the opposite side of the output.
NOT gates invert signals, just like a mesecon torch does, but faster. The input is at the opposite side of the output.
They work in unloaded blocks.

View File

@ -1 +1,2 @@
Or gates power their output if either of their inputs (or both) are powered. You could basically get the same behaviour with two diodes, but or gates save some space.
OR gates power their output if either of their inputs (or both) are powered. You could basically get the same behaviour with two diodes, but OR gates save some space.
They work in unloaded blocks.

View File

@ -1 +1,2 @@
XOR gates power their output if only one input is powered, they're off if either both or none of the inputs is powered.
They work in unloaded blocks.

View File

@ -54,10 +54,10 @@ local function update_gate(pos, node, link, newstate)
end
end
function register_gate(name, inputnumber, assess, recipe)
local function register_gate(name, inputnumber, assess, recipe, description)
local get_inputrules = inputnumber == 2 and gate_get_input_rules_twoinputs or
gate_get_input_rules_oneinput
local description = "Mesecons Logic Gate: "..name
description = "Logic Gate: "..name
local basename = "mesecons_gates:"..name
mesecon.register_node(basename, {
@ -65,6 +65,7 @@ function register_gate(name, inputnumber, assess, recipe)
inventory_image = "jeija_gate_off.png^jeija_gate_"..name..".png",
paramtype = "light",
paramtype2 = "facedir",
is_ground_content = false,
drawtype = "nodebox",
drop = basename.."_off",
selection_box = nodebox,
@ -74,7 +75,8 @@ function register_gate(name, inputnumber, assess, recipe)
assess = assess,
onstate = basename.."_on",
offstate = basename.."_off",
inputnumber = inputnumber
inputnumber = inputnumber,
after_dig_node = mesecon.do_cooldown,
},{
tiles = {"jeija_microcontroller_bottom.png^".."jeija_gate_off.png^"..
"jeija_gate_"..name..".png"},
@ -103,32 +105,39 @@ function register_gate(name, inputnumber, assess, recipe)
end
register_gate("diode", 1, function (input) return input end,
{{"mesecons:mesecon", "mesecons_torch:mesecon_torch_on", "mesecons_torch:mesecon_torch_on"}})
{{"mesecons:mesecon", "mesecons_torch:mesecon_torch_on", "mesecons_torch:mesecon_torch_on"}},
"Diode")
register_gate("not", 1, function (input) return not input end,
{{"mesecons:mesecon", "mesecons_torch:mesecon_torch_on", "mesecons:mesecon"}})
{{"mesecons:mesecon", "mesecons_torch:mesecon_torch_on", "mesecons:mesecon"}},
"NOT Gate")
register_gate("and", 2, function (val1, val2) return val1 and val2 end,
{{"mesecons:mesecon", "", ""},
{"", "mesecons_materials:silicon", "mesecons:mesecon"},
{"mesecons:mesecon", "", ""}})
{"mesecons:mesecon", "", ""}},
"AND Gate")
register_gate("nand", 2, function (val1, val2) return not (val1 and val2) end,
{{"mesecons:mesecon", "", ""},
{"", "mesecons_materials:silicon", "mesecons_torch:mesecon_torch_on"},
{"mesecons:mesecon", "", ""}})
{"mesecons:mesecon", "", ""}},
"NAND Gate")
register_gate("xor", 2, function (val1, val2) return (val1 or val2) and not (val1 and val2) end,
{{"mesecons:mesecon", "", ""},
{"", "mesecons_materials:silicon", "mesecons_materials:silicon"},
{"mesecons:mesecon", "", ""}})
{"mesecons:mesecon", "", ""}},
"XOR Gate")
register_gate("nor", 2, function (val1, val2) return not (val1 or val2) end,
{{"mesecons:mesecon", "", ""},
{"", "mesecons:mesecon", "mesecons_torch:mesecon_torch_on"},
{"mesecons:mesecon", "", ""}})
{"mesecons:mesecon", "", ""}},
"NOR Gate")
register_gate("or", 2, function (val1, val2) return (val1 or val2) end,
{{"mesecons:mesecon", "", ""},
{"", "mesecons:mesecon", "mesecons:mesecon"},
{"mesecons:mesecon", "", ""}})
{"mesecons:mesecon", "", ""}},
"OR Gate")

View File

@ -1 +1,2 @@
Water turbines are receptors that turn on if flowing water is above them.
They only work in active blocks; in inactive blocks they keep their old state.

View File

@ -5,14 +5,15 @@
minetest.register_node("mesecons_hydroturbine:hydro_turbine_off", {
drawtype = "mesh",
mesh = "jeija_hydro_turbine.obj",
mesh = "jeija_hydro_turbine_off.obj",
tiles = {
"jeija_hydro_turbine_sides_off.png",
"jeija_hydro_turbine_top_bottom.png",
"jeija_hydro_turbine_turbine_top_bottom.png",
"jeija_hydro_turbine_turbine_misc.png"
"jeija_hydro_turbine_turbine_top_bottom_off.png",
"jeija_hydro_turbine_turbine_misc_off.png"
},
inventory_image = "jeija_hydro_turbine_inv.png",
is_ground_content = false,
wield_scale = {x=0.75, y=0.75, z=0.75},
groups = {dig_immediate=2},
description="Water Turbine",
@ -21,21 +22,25 @@ minetest.register_node("mesecons_hydroturbine:hydro_turbine_off", {
type = "fixed",
fixed = { -0.5, -0.5, -0.5, 0.5, 1.5, 0.5 },
},
sounds = default.node_sound_stone_defaults(),
sounds = default.node_sound_metal_defaults(),
mesecons = {receptor = {
state = mesecon.state.off
}}
}},
on_blast = mesecon.on_blastnode,
})
minetest.register_node("mesecons_hydroturbine:hydro_turbine_on", {
drawtype = "mesh",
mesh = "jeija_hydro_turbine.obj",
is_ground_content = false,
mesh = "jeija_hydro_turbine_on.obj",
wield_scale = {x=0.75, y=0.75, z=0.75},
tiles = {
"jeija_hydro_turbine_sides_on.png",
"jeija_hydro_turbine_top_bottom.png",
"jeija_hydro_turbine_turbine_top_bottom.png",
"jeija_hydro_turbine_turbine_misc.png"
{ name = "jeija_hydro_turbine_turbine_top_bottom_on.png",
animation = {type = "vertical_frames", aspect_w = 128, aspect_h = 16, length = 1.6} },
{ name = "jeija_hydro_turbine_turbine_misc_on.png",
animation = {type = "vertical_frames", aspect_w = 256, aspect_h = 32, length = 0.4} }
},
inventory_image = "jeija_hydro_turbine_inv.png",
drop = "mesecons_hydroturbine:hydro_turbine_off 1",
@ -46,22 +51,29 @@ minetest.register_node("mesecons_hydroturbine:hydro_turbine_on", {
type = "fixed",
fixed = { -0.5, -0.5, -0.5, 0.5, 1.5, 0.5 },
},
sounds = default.node_sound_stone_defaults(),
sounds = default.node_sound_metal_defaults(),
mesecons = {receptor = {
state = mesecon.state.on
}}
}},
on_blast = mesecon.on_blastnode,
})
local function is_flowing_water(pos)
local name = minetest.get_node(pos).name
local is_water = minetest.get_item_group(name, "water") > 0
local is_flowing = minetest.registered_items[name].liquidtype == "flowing"
return (is_water and is_flowing)
end
minetest.register_abm({
nodenames = {"mesecons_hydroturbine:hydro_turbine_off"},
interval = 1,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
local waterpos={x=pos.x, y=pos.y+1, z=pos.z}
if minetest.get_node(waterpos).name=="default:water_flowing" then
if is_flowing_water(waterpos) then
minetest.set_node(pos, {name="mesecons_hydroturbine:hydro_turbine_on"})
nodeupdate(pos)
mesecon.receptor_on(pos)
end
end,
@ -73,9 +85,8 @@ nodenames = {"mesecons_hydroturbine:hydro_turbine_on"},
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
local waterpos={x=pos.x, y=pos.y+1, z=pos.z}
if minetest.get_node(waterpos).name~="default:water_flowing" then
if not is_flowing_water(waterpos) then
minetest.set_node(pos, {name="mesecons_hydroturbine:hydro_turbine_off"})
nodeupdate(pos)
mesecon.receptor_off(pos)
end
end,

View File

@ -1,21 +1,21 @@
# Blender v2.73 (sub 0) OBJ File: 'mesecons-water-turbine.blend'
# Blender v2.72 (sub 0) OBJ File: 'mesecons-water-turbine.blend'
# www.blender.org
o Cylinder.002_Cylinder.003
v 0.000000 0.500000 -0.150000
v 0.000000 0.496094 -0.150000
v 0.000000 0.562500 -0.150000
v 0.106066 0.500000 -0.106066
v 0.106066 0.496094 -0.106066
v 0.106066 0.562500 -0.106066
v 0.150000 0.500000 0.000000
v 0.150000 0.496094 0.000000
v 0.150000 0.562500 0.000000
v 0.106066 0.500000 0.106066
v 0.106066 0.496094 0.106066
v 0.106066 0.562500 0.106066
v -0.000000 0.500000 0.150000
v -0.000000 0.496094 0.150000
v -0.000000 0.562500 0.150000
v -0.106066 0.500000 0.106066
v -0.106066 0.496094 0.106066
v -0.106066 0.562500 0.106066
v -0.150000 0.500000 -0.000000
v -0.150000 0.496094 -0.000000
v -0.150000 0.562500 -0.000000
v -0.106066 0.500000 -0.106066
v -0.106066 0.496094 -0.106066
v -0.106066 0.562500 -0.106066
v 0.097545 0.625000 -0.490393
v -0.097545 0.625000 -0.490393
@ -113,38 +113,38 @@ v -0.430779 0.634645 -0.108975
v -0.180802 0.634645 -0.292266
v -0.180802 1.482640 -0.292266
v -0.430779 1.482640 -0.108975
v 0.097545 1.500000 -0.490393
v -0.097545 1.500000 -0.490393
v -0.277785 1.500000 -0.415735
v -0.415735 1.500000 -0.277785
v -0.490393 1.500000 -0.097545
v -0.490393 1.500000 0.097545
v -0.415735 1.500000 0.277785
v -0.277785 1.500000 0.415735
v -0.097545 1.500000 0.490393
v 0.097545 1.500000 0.490393
v 0.277785 1.500000 0.415735
v 0.415735 1.500000 0.277785
v 0.490393 1.500000 0.097545
v 0.490393 1.500000 -0.097545
v 0.415735 1.500000 -0.277785
v 0.277785 1.500000 -0.415735
v 0.097545 1.468750 -0.490393
v -0.097545 1.468750 -0.490393
v -0.277785 1.468750 -0.415735
v -0.415735 1.468750 -0.277785
v -0.490393 1.468750 -0.097545
v -0.490393 1.468750 0.097545
v -0.415735 1.468750 0.277785
v -0.277785 1.468750 0.415735
v -0.097545 1.468750 0.490393
v 0.097545 1.468750 0.490393
v 0.277785 1.468750 0.415735
v 0.415735 1.468750 0.277785
v 0.490393 1.468750 0.097545
v 0.490393 1.468750 -0.097545
v 0.415735 1.468750 -0.277785
v 0.277785 1.468750 -0.415735
v 0.097545 1.496094 -0.490393
v -0.097545 1.496094 -0.490393
v -0.277785 1.496094 -0.415735
v -0.415735 1.496094 -0.277785
v -0.490393 1.496094 -0.097545
v -0.490393 1.496094 0.097545
v -0.415735 1.496094 0.277785
v -0.277785 1.496094 0.415735
v -0.097545 1.496094 0.490393
v 0.097545 1.496094 0.490393
v 0.277785 1.496094 0.415735
v 0.415735 1.496094 0.277785
v 0.490393 1.496094 0.097545
v 0.490393 1.496094 -0.097545
v 0.415735 1.496094 -0.277785
v 0.277785 1.496094 -0.415735
v 0.097545 1.464844 -0.490393
v -0.097545 1.464844 -0.490393
v -0.277785 1.464844 -0.415735
v -0.415735 1.464844 -0.277785
v -0.490393 1.464844 -0.097545
v -0.490393 1.464844 0.097545
v -0.415735 1.464844 0.277785
v -0.277785 1.464844 0.415735
v -0.097545 1.464844 0.490393
v 0.097545 1.464844 0.490393
v 0.277785 1.464844 0.415735
v 0.415735 1.464844 0.277785
v 0.490393 1.464844 0.097545
v 0.490393 1.464844 -0.097545
v 0.415735 1.464844 -0.277785
v 0.277785 1.464844 -0.415735
v 0.025624 0.559630 -0.061863
v 0.025624 1.481372 -0.061863
v 0.061863 0.559630 -0.025624
@ -161,14 +161,14 @@ v -0.061863 0.559630 -0.025624
v -0.061863 1.481372 -0.025624
v -0.025624 0.559630 -0.061863
v -0.025624 1.481372 -0.061863
v 0.499775 -0.499550 -0.499775
v 0.499775 -0.499550 0.499775
v -0.499775 -0.499550 0.499775
v -0.499775 -0.499550 -0.499775
v 0.499775 0.500000 -0.499775
v 0.499775 0.500000 0.499775
v -0.499775 0.500000 0.499775
v -0.499775 0.500000 -0.499775
v 0.496094 -0.496094 -0.496094
v 0.496094 -0.496094 0.496094
v -0.496094 -0.496094 0.496094
v -0.496094 -0.496094 -0.496094
v 0.496094 0.496094 -0.496094
v 0.496094 0.496094 0.496094
v -0.496094 0.496094 0.496094
v -0.496094 0.496094 -0.496094
vt 1.000000 0.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
@ -314,10 +314,10 @@ vt 0.031250 0.875000
vt 0.031250 0.906250
vt 0.062500 0.906250
vn 1.000000 0.000000 0.000000
vn -0.000000 -0.000000 1.000000
vn -1.000000 -0.000000 -0.000000
vn 0.000000 0.000000 1.000000
vn -1.000000 0.000000 0.000000
vn 0.000000 0.000000 -1.000000
vn 0.000000 -1.000000 -0.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 1.000000 0.000000
vn 0.382700 0.000000 -0.923900
vn 0.923900 0.000000 -0.382700

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -1 +1,2 @@
mesecons
screwdriver?

View File

@ -1 +1 @@
Insulated mesecons are conductors that only conduct in one direction (and also not up or down).
Insulated mesecons are conductors that only conduct in one direction (and also not up or down). Like uninsulated wires, they work through unloaded blocks.

View File

@ -1,4 +1,6 @@
function insulated_wire_get_rules(node)
local screwdriver_exists = minetest.global_exists("screwdriver")
local function insulated_wire_get_rules(node)
local rules = {{x = 1, y = 0, z = 0},
{x =-1, y = 0, z = 0}}
if node.param2 == 1 or node.param2 == 3 then
@ -9,7 +11,7 @@ end
minetest.register_node("mesecons_insulated:insulated_on", {
drawtype = "nodebox",
description = "Insulated Mesecon",
description = "Straight Insulated Mesecon",
tiles = {
"jeija_insulated_wire_sides_on.png",
"jeija_insulated_wire_sides_on.png",
@ -20,28 +22,33 @@ minetest.register_node("mesecons_insulated:insulated_on", {
},
paramtype = "light",
paramtype2 = "facedir",
is_ground_content = false,
walkable = false,
sunlight_propagates = true,
selection_box = {
type = "fixed",
fixed = { -16/32-0.001, -18/32, -7/32, 16/32+0.001, -12/32, 7/32 }
fixed = { -16/32, -16/32, -7/32, 16/32, -12/32, 7/32 }
},
node_box = {
type = "fixed",
-- ±0.001 is to prevent z-fighting
fixed = { -16/32-0.001, -17/32, -3/32, 16/32+0.001, -13/32, 3/32 }
},
groups = {dig_immediate = 3, not_in_creative_inventory = 1},
drop = "mesecons_insulated:insulated_off",
sounds = default.node_sound_defaults(),
mesecons = {conductor = {
state = mesecon.state.on,
offstate = "mesecons_insulated:insulated_off",
rules = insulated_wire_get_rules
}}
}},
on_blast = mesecon.on_blastnode,
on_rotate = screwdriver_exists and screwdriver.rotate_simple,
})
minetest.register_node("mesecons_insulated:insulated_off", {
drawtype = "nodebox",
description = "Insulated Mesecon",
description = "Straight Insulated Mesecon",
tiles = {
"jeija_insulated_wire_sides_off.png",
"jeija_insulated_wire_sides_off.png",
@ -52,22 +59,27 @@ minetest.register_node("mesecons_insulated:insulated_off", {
},
paramtype = "light",
paramtype2 = "facedir",
is_ground_content = false,
walkable = false,
sunlight_propagates = true,
selection_box = {
type = "fixed",
fixed = { -16/32-0.001, -18/32, -7/32, 16/32+0.001, -12/32, 7/32 }
fixed = { -16/32, -16/32, -7/32, 16/32, -12/32, 7/32 }
},
node_box = {
type = "fixed",
-- ±0.001 is to prevent z-fighting
fixed = { -16/32-0.001, -17/32, -3/32, 16/32+0.001, -13/32, 3/32 }
},
groups = {dig_immediate = 3},
sounds = default.node_sound_defaults(),
mesecons = {conductor = {
state = mesecon.state.off,
onstate = "mesecons_insulated:insulated_on",
rules = insulated_wire_get_rules
}}
}},
on_blast = mesecon.on_blastnode,
on_rotate = screwdriver_exists and screwdriver.rotate_simple,
})
minetest.register_craft({

Binary file not shown.

Before

Width:  |  Height:  |  Size: 305 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 293 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 246 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 B

View File

@ -1 +1 @@
Lamps are effectors that if powered emit light.
Mesecon lamps are effectors that if powered emit light.

View File

@ -2,7 +2,7 @@
-- A lamp is "is an electrical device used to create artificial light" (wikipedia)
-- guess what?
mesecon_lamp_box = {
local mesecon_lamp_box = {
type = "wallmounted",
wall_top = {-0.3125,0.375,-0.3125,0.3125,0.5,0.3125},
wall_bottom = {-0.3125,-0.5,-0.3125,0.3125,-0.375,0.3125},
@ -14,20 +14,23 @@ minetest.register_node("mesecons_lamp:lamp_on", {
tiles = {"jeija_meselamp_on.png"},
paramtype = "light",
paramtype2 = "wallmounted",
is_ground_content = false,
legacy_wallmounted = true,
sunlight_propagates = true,
walkable = true,
light_source = default.LIGHT_MAX,
light_source = minetest.LIGHT_MAX,
node_box = mesecon_lamp_box,
selection_box = mesecon_lamp_box,
groups = {dig_immediate=3,not_in_creative_inventory=1, mesecon_effector_on = 1},
drop="mesecons_lamp:lamp_off 1",
groups = {dig_immediate = 3,not_in_creative_inventory = 1, mesecon_effector_on = 1},
drop = "mesecons_lamp:lamp_off 1",
sounds = default.node_sound_glass_defaults(),
mesecons = {effector = {
action_off = function (pos, node)
minetest.swap_node(pos, {name = "mesecons_lamp:lamp_off", param2 = node.param2})
end
}}
end,
rules = mesecon.rules.wallmounted_get,
}},
on_blast = mesecon.on_blastnode,
})
minetest.register_node("mesecons_lamp:lamp_off", {
@ -37,18 +40,21 @@ minetest.register_node("mesecons_lamp:lamp_off", {
wield_image = "jeija_meselamp.png",
paramtype = "light",
paramtype2 = "wallmounted",
is_ground_content = false,
sunlight_propagates = true,
walkable = true,
node_box = mesecon_lamp_box,
selection_box = mesecon_lamp_box,
groups = {dig_immediate=3, mesecon_receptor_off = 1, mesecon_effector_off = 1},
description="Meselamp",
description = "Mesecon Lamp",
sounds = default.node_sound_glass_defaults(),
mesecons = {effector = {
action_on = function (pos, node)
minetest.swap_node(pos, {name = "mesecons_lamp:lamp_on", param2 = node.param2})
end
}}
end,
rules = mesecon.rules.wallmounted_get,
}},
on_blast = mesecon.on_blastnode,
})
minetest.register_craft({

View File

@ -1 +1,2 @@
Effector, glows blue when powered.
It works in an inactive block.

View File

@ -1 +1,2 @@
Effector, glows dark grey when powered.
It works in an inactive block.

View File

@ -1 +1,2 @@
Effector, glows green when powered.
It works in an inactive block.

View File

@ -1 +1,2 @@
Effector, glows light grey when powered.
It works in an inactive block.

View File

@ -1 +1,2 @@
Effector, glows red when powered.
It works in an inactive block.

View File

@ -1 +1,2 @@
Effector, glows yellow when powered.
It works in an inactive block.

View File

@ -14,31 +14,38 @@ local lightstone_rules = {
{x=0, y=-1, z=0},
}
function mesecon.lightstone_add(name, base_item, texture_off, texture_on)
function mesecon.lightstone_add(name, base_item, texture_off, texture_on, desc)
if not desc then
desc = name .. " Lightstone"
end
minetest.register_node("mesecons_lightstone:lightstone_" .. name .. "_off", {
tiles = {texture_off},
groups = {cracky=2, mesecon_effector_off = 1, mesecon = 2},
description=name.." Lightstone",
is_ground_content = false,
groups = {cracky = 2, mesecon_effector_off = 1, mesecon = 2},
description = desc,
sounds = default.node_sound_stone_defaults(),
mesecons = {effector = {
rules = lightstone_rules,
action_on = function (pos, node)
minetest.swap_node(pos, {name = "mesecons_lightstone:lightstone_" .. name .. "_on", param2 = node.param2})
end,
}}
}},
on_blast = mesecon.on_blastnode,
})
minetest.register_node("mesecons_lightstone:lightstone_" .. name .. "_on", {
tiles = {texture_on},
groups = {cracky=2,not_in_creative_inventory=1, mesecon = 2},
drop = "mesecons_lightstone:lightstone_" .. name .. "_off",
light_source = default.LIGHT_MAX-2,
sounds = default.node_sound_stone_defaults(),
mesecons = {effector = {
rules = lightstone_rules,
action_off = function (pos, node)
minetest.swap_node(pos, {name = "mesecons_lightstone:lightstone_" .. name .. "_off", param2 = node.param2})
end,
}}
tiles = {texture_on},
is_ground_content = false,
groups = {cracky = 2, not_in_creative_inventory = 1, mesecon = 2},
drop = "mesecons_lightstone:lightstone_" .. name .. "_off",
light_source = minetest.LIGHT_MAX - 2,
sounds = default.node_sound_stone_defaults(),
mesecons = {effector = {
rules = lightstone_rules,
action_off = function (pos, node)
minetest.swap_node(pos, {name = "mesecons_lightstone:lightstone_" .. name .. "_off", param2 = node.param2})
end,
}},
on_blast = mesecon.on_blastnode,
})
minetest.register_craft({
@ -52,9 +59,15 @@ function mesecon.lightstone_add(name, base_item, texture_off, texture_on)
end
mesecon.lightstone_add("red", "dye:red", "jeija_lightstone_red_off.png", "jeija_lightstone_red_on.png")
mesecon.lightstone_add("green", "dye:green", "jeija_lightstone_green_off.png", "jeija_lightstone_green_on.png")
mesecon.lightstone_add("blue", "dye:blue", "jeija_lightstone_blue_off.png", "jeija_lightstone_blue_on.png")
mesecon.lightstone_add("gray", "dye:grey", "jeija_lightstone_gray_off.png", "jeija_lightstone_gray_on.png")
mesecon.lightstone_add("darkgray", "dye:dark_grey", "jeija_lightstone_darkgray_off.png", "jeija_lightstone_darkgray_on.png")
mesecon.lightstone_add("yellow", "dye:yellow", "jeija_lightstone_yellow_off.png", "jeija_lightstone_yellow_on.png")
mesecon.lightstone_add("red", "dye:red", "jeija_lightstone_red_off.png", "jeija_lightstone_red_on.png", "Red Lightstone")
mesecon.lightstone_add("green", "dye:green", "jeija_lightstone_green_off.png", "jeija_lightstone_green_on.png", "Green Lightstone")
mesecon.lightstone_add("blue", "dye:blue", "jeija_lightstone_blue_off.png", "jeija_lightstone_blue_on.png", "Blue Lightstone")
mesecon.lightstone_add("gray", "dye:grey", "jeija_lightstone_gray_off.png", "jeija_lightstone_gray_on.png", "Grey Lightstone")
mesecon.lightstone_add("darkgray", "dye:dark_grey", "jeija_lightstone_darkgray_off.png", "jeija_lightstone_darkgray_on.png", "Dark Grey Lightstone")
mesecon.lightstone_add("yellow", "dye:yellow", "jeija_lightstone_yellow_off.png", "jeija_lightstone_yellow_on.png", "Yellow Lightstone")
mesecon.lightstone_add("orange", "dye:orange", "jeija_lightstone_orange_off.png", "jeija_lightstone_orange_on.png", "Orange Lightstone")
mesecon.lightstone_add("white", "dye:white", "jeija_lightstone_white_off.png", "jeija_lightstone_white_on.png", "White Lightstone")
mesecon.lightstone_add("pink", "dye:pink", "jeija_lightstone_pink_off.png", "jeija_lightstone_pink_on.png", "Pink Lightstone")
mesecon.lightstone_add("magenta", "dye:magenta", "jeija_lightstone_magenta_off.png", "jeija_lightstone_magenta_on.png", "Magenta Lightstone")
mesecon.lightstone_add("cyan", "dye:cyan", "jeija_lightstone_cyan_off.png", "jeija_lightstone_cyan_on.png", "Cyan Lightstone")
mesecon.lightstone_add("violet", "dye:violet", "jeija_lightstone_violet_off.png", "jeija_lightstone_violet_on.png", "Violet Lightstone")

Binary file not shown.

After

Width:  |  Height:  |  Size: 443 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 663 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 633 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 641 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 546 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 633 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 B

View File

@ -1,5 +1,7 @@
The luacontroller is an advanced programmable component.
You can simply code it in the language mesecons uses itself: Lua!
The Luacontroller is an advanced programmable component.
You can simply code it in the language Mesecons uses itself: Lua!
All the code runs in a sandbox, so it's completely safe (but I won't guarantee that for absolute certainty!).
It works fine in an unloaded block, loading the block when the program needs to run.
Events are properly delivered after a server restart.
<a href="http://mesecons.net/luacontroller/">Documentation is available here!</a>

View File

@ -12,9 +12,11 @@
-- 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
-- run(pos): runs the code in the controller at pos
-- reset_meta(pos, code, errmsg): performs a software-reset, installs new code and prints error messages
-- set_port_states(pos, ports): 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
--
-- The Sandbox
@ -141,7 +143,7 @@ local function set_port_states(pos, ports)
-- Solution / Workaround:
-- Remember which output was turned off and ignore next "off" event.
local meta = minetest.get_meta(pos)
local ign = minetest.deserialize(meta:get_string("ignore_offevents")) or {}
local ign = minetest.deserialize(meta:get_string("ignore_offevents"), true) or {}
if ports.a and not vports.a and not mesecon.is_powered(pos, rules.a) then ign.A = true end
if ports.b and not vports.b and not mesecon.is_powered(pos, rules.b) then ign.B = true end
if ports.c and not vports.c and not mesecon.is_powered(pos, rules.c) then ign.C = true end
@ -183,7 +185,7 @@ end
local function ignore_event(event, meta)
if event.type ~= "off" then return false end
local ignore_offevents = minetest.deserialize(meta:get_string("ignore_offevents")) or {}
local ignore_offevents = minetest.deserialize(meta:get_string("ignore_offevents"), true) or {}
if ignore_offevents[event.pin.name] then
ignore_offevents[event.pin.name] = nil
meta:set_string("ignore_offevents", minetest.serialize(ignore_offevents))
@ -196,7 +198,11 @@ end
-------------------------
local function safe_print(param)
local string_meta = getmetatable("")
local sandbox = string_meta.__index
string_meta.__index = string -- Leave string sandbox temporarily
print(dump(param))
string_meta.__index = sandbox -- Restore string sandbox
end
local function safe_date()
@ -219,7 +225,7 @@ end
local function safe_string_find(...)
if (select(4, ...)) ~= true then
debug.sethook() -- Clear hook
error("string.find: 'plain' (fourth parameter) must always be true in a LuaController")
error("string.find: 'plain' (fourth parameter) must always be true in a Luacontroller")
end
return string.find(...)
@ -232,10 +238,11 @@ local function remove_functions(x)
end
-- Make sure to not serialize the same table multiple times, otherwise
-- writing mem.test = mem in the LuaController will lead to infinite recursion
-- writing mem.test = mem in the Luacontroller will lead to infinite recursion
local seen = {}
local function rfuncs(x)
if x == nil then return end
if seen[x] then return end
seen[x] = true
if type(x) ~= "table" then return end
@ -259,50 +266,180 @@ local function remove_functions(x)
return x
end
local function get_interrupt(pos)
-- itbl: Flat table of functions to run after sandbox cleanup, used to prevent various security hazards
local function get_interrupt(pos, itbl, send_warning)
-- iid = interrupt id
local function interrupt(time, iid)
-- NOTE: This runs within string metatable sandbox, so don't *rely* on anything of the form (""):y
-- Hence the values get moved out. Should take less time than original, so totally compatible
if type(time) ~= "number" then return end
local luac_id = minetest.get_meta(pos):get_int("luac_id")
mesecon.queue:add_action(pos, "lc_interrupt", {luac_id, iid}, time, iid, 1)
table.insert(itbl, function ()
-- Outside string metatable sandbox, can safely run this now
local luac_id = minetest.get_meta(pos):get_int("luac_id")
-- Check if IID is dodgy, so you can't use interrupts to store an infinite amount of data.
-- Note that this is safe from alter-after-free because this code gets run after the sandbox has ended.
-- This runs outside of the timer and *shouldn't* harm perf. unless dodgy data is being sent in the first place
iid = remove_functions(iid)
local msg_ser = minetest.serialize(iid)
if #msg_ser <= mesecon.setting("luacontroller_interruptid_maxlen", 256) then
mesecon.queue:add_action(pos, "lc_interrupt", {luac_id, iid}, time, iid, 1)
else
send_warning("An interrupt ID was too large!")
end
end)
end
return interrupt
end
local function get_digiline_send(pos)
if not digiline then return end
-- Given a message object passed to digiline_send, clean it up into a form
-- which is safe to transmit over the network and compute its "cost" (a very
-- rough estimate of its memory usage).
--
-- The cleaning comprises the following:
-- 1. Functions (and userdata, though user scripts ought not to get hold of
-- those in the first place) are removed, because they break the model of
-- Digilines as a network that carries basic data, and they could exfiltrate
-- references to mutable objects from one Luacontroller to another, allowing
-- inappropriate high-bandwidth, no-wires communication.
-- 2. Tables are duplicated because, being mutable, they could otherwise be
-- modified after the send is complete in order to change what data arrives
-- at the recipient, perhaps in violation of the previous cleaning rule or
-- in violation of the message size limit.
--
-- The cost indication is only approximate; its not a perfect measurement of
-- the number of bytes of memory used by the message object.
--
-- Parameters:
-- msg -- the message to clean
-- back_references -- for internal use only; do not provide
--
-- Returns:
-- 1. The cleaned object.
-- 2. The approximate cost of the object.
local function clean_and_weigh_digiline_message(msg, back_references)
local t = type(msg)
if t == "string" then
-- Strings are immutable so can be passed by reference, and cost their
-- length plus the size of the Lua object header (24 bytes on a 64-bit
-- platform) plus one byte for the NUL terminator.
return msg, #msg + 25
elseif t == "number" then
-- Numbers are passed by value so need not be touched, and cost 8 bytes
-- as all numbers in Lua are doubles.
return msg, 8
elseif t == "boolean" then
-- Booleans are passed by value so need not be touched, and cost 1
-- byte.
return msg, 1
elseif t == "table" then
-- Tables are duplicated. Check if this table has been seen before
-- (self-referential or shared table); if so, reuse the cleaned value
-- of the previous occurrence, maintaining table topology and avoiding
-- infinite recursion, and charge zero bytes for this as the object has
-- already been counted.
back_references = back_references or {}
local bref = back_references[msg]
if bref then
return bref, 0
end
-- Construct a new table by cleaning all the keys and values and adding
-- up their costs, plus 8 bytes as a rough estimate of table overhead.
local cost = 8
local ret = {}
back_references[msg] = ret
for k, v in pairs(msg) do
local k_cost, v_cost
k, k_cost = clean_and_weigh_digiline_message(k, back_references)
v, v_cost = clean_and_weigh_digiline_message(v, back_references)
if k ~= nil and v ~= nil then
-- Only include an element if its key and value are of legal
-- types.
ret[k] = v
end
-- If we only counted the cost of a table element when we actually
-- used it, we would be vulnerable to the following attack:
-- 1. Construct a huge table (too large to pass the cost limit).
-- 2. Insert it somewhere in a table, with a function as a key.
-- 3. Insert it somewhere in another table, with a number as a key.
-- 4. The first occurrence doesnt pay the cost because functions
-- are stripped and therefore the element is dropped.
-- 5. The second occurrence doesnt pay the cost because its in
-- back_references.
-- By counting the costs regardless of whether the objects will be
-- included, we avoid this attack; it may overestimate the cost of
-- some messages, but only those that wont be delivered intact
-- anyway because they contain illegal object types.
cost = cost + k_cost + v_cost
end
return ret, cost
else
return nil, 0
end
end
-- itbl: Flat table of functions to run after sandbox cleanup, used to prevent various security hazards
local function get_digiline_send(pos, itbl, send_warning)
if not minetest.global_exists("digilines") then return end
local chan_maxlen = mesecon.setting("luacontroller_digiline_channel_maxlen", 256)
local maxlen = mesecon.setting("luacontroller_digiline_maxlen", 50000)
return function(channel, msg)
minetest.after(0, function()
digiline:receptor_send(pos, digiline.rules.default, channel, msg)
-- 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
if type(channel) == "string" then
if #channel > chan_maxlen then
send_warning("Channel string too long.")
return false
end
elseif (type(channel) ~= "string" and type(channel) ~= "number" and type(channel) ~= "boolean") then
send_warning("Channel must be string, number or boolean.")
return false
end
local msg_cost
msg, msg_cost = clean_and_weigh_digiline_message(msg)
if msg == nil or msg_cost > maxlen then
send_warning("Message was too complex, or contained invalid data.")
return false
end
table.insert(itbl, function ()
-- Runs outside of string metatable sandbox
local luac_id = minetest.get_meta(pos):get_int("luac_id")
mesecon.queue:add_action(pos, "lc_digiline_relay", {channel, luac_id, msg})
end)
return true
end
end
local safe_globals = {
-- Don't add pcall/xpcall unless willing to deal with the consequences (unless very careful, incredibly likely to allow killing server indirectly)
"assert", "error", "ipairs", "next", "pairs", "select",
"tonumber", "tostring", "type", "unpack", "_VERSION"
}
local function create_environment(pos, mem, event)
local function create_environment(pos, mem, event, itbl, send_warning)
-- Gather variables for the environment
local vports = minetest.registered_nodes[minetest.get_node(pos).name].virtual_portstates
local vports_copy = {}
for k, v in pairs(vports) do vports_copy[k] = v end
local rports = get_real_port_states(pos)
-- Create new library tables on each call to prevent one LuaController
-- from breaking a library and messing up other LuaControllers.
-- Create new library tables on each call to prevent one Luacontroller
-- from breaking a library and messing up other Luacontrollers.
local env = {
pin = merge_port_states(vports, rports),
port = vports_copy,
event = event,
mem = mem,
heat = minetest.get_meta(pos):get_int("heat"),
heat = mesecon.get_heat(pos),
heat_max = mesecon.setting("overheat_max", 20),
print = safe_print,
interrupt = get_interrupt(pos),
digiline_send = get_digiline_send(pos),
interrupt = get_interrupt(pos, itbl, send_warning),
digiline_send = get_digiline_send(pos, itbl, send_warning),
string = {
byte = string.byte,
char = string.char,
@ -390,10 +527,11 @@ local function create_sandbox(code, env)
jit.off(f, true)
end
local maxevents = mesecon.setting("luacontroller_maxevents", 10000)
return function(...)
-- NOTE: This runs within string metatable sandbox, so the setting's been moved out for safety
-- Use instruction counter to stop execution
-- after luacontroller_maxevents
local maxevents = mesecon.setting("luacontroller_maxevents", 10000)
debug.sethook(timeout, "", maxevents)
local ok, ret = pcall(f, ...)
debug.sethook() -- Clear hook
@ -404,7 +542,7 @@ end
local function load_memory(meta)
return minetest.deserialize(meta:get_string("lc_memory")) or {}
return minetest.deserialize(meta:get_string("lc_memory"), true) or {}
end
@ -414,6 +552,7 @@ local function save_memory(pos, meta, mem)
if (#memstring <= memsize_max) then
meta:set_string("lc_memory", memstring)
meta:mark_as_private("lc_memory")
else
print("Error: Luacontroller memory overflow. "..memsize_max.." bytes available, "
..#memstring.." required. Controller overheats.")
@ -421,26 +560,42 @@ local function save_memory(pos, meta, mem)
end
end
local function run(pos, event)
-- Returns success (boolean), errmsg (string)
-- run (as opposed to run_inner) is responsible for setting up meta according to this output
local function run_inner(pos, code, event)
local meta = minetest.get_meta(pos)
if overheat(pos) then return end
if ignore_event(event, meta) then return end
-- Note: These return success, presumably to avoid changing LC ID.
if overheat(pos) then return true, "" end
if ignore_event(event, meta) then return true, "" end
-- Load code & mem from meta
local mem = load_memory(meta)
local code = meta:get_string("code")
-- 'Last warning' label.
local warning = ""
local function send_warning(str)
warning = "Warning: " .. str
end
-- Create environment
local env = create_environment(pos, mem, event)
local itbl = {}
local env = create_environment(pos, mem, event, itbl, send_warning)
-- Create the sandbox and execute code
local f, msg = create_sandbox(code, env)
if not f then return msg end
if not f then return false, msg end
-- Start string true sandboxing
local onetruestring = getmetatable("")
-- If a string sandbox is already up yet inconsistent, something is very wrong
assert(onetruestring.__index == string)
onetruestring.__index = env.string
local success, msg = pcall(f)
if not success then return msg end
onetruestring.__index = string
-- End string true sandboxing
if not success then return false, msg end
if type(env.port) ~= "table" then
return "Ports set are invalid."
return false, "Ports set are invalid."
end
-- Actually set the ports
@ -448,8 +603,58 @@ local function run(pos, event)
-- Save memory. This may burn the luacontroller if a memory overflow occurs.
save_memory(pos, meta, env.mem)
-- 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)
meta:set_string("code", code)
meta:mark_as_private("code")
code = minetest.formspec_escape(code or "")
errmsg = minetest.formspec_escape(tostring(errmsg or ""))
meta:set_string("formspec", "size[12,10]"
.."background[-0.2,-0.25;12.4,10.75;jeija_luac_background.png]"
.."label[0.1,8.3;"..errmsg.."]"
.."textarea[0.2,0.2;12.2,9.5;code;;"..code.."]"
.."image_button[4.75,8.75;2.5,1;jeija_luac_runbutton.png;program;]"
.."image_button_exit[11.72,-0.25;0.425,0.4;jeija_close_window.png;exit;]"
)
end
local function reset_meta(pos, code, errmsg)
local meta = minetest.get_meta(pos)
reset_formspec(meta, code, errmsg)
meta:set_int("luac_id", math.random(1, 65535))
end
-- Wraps run_inner with LC-reset-on-error
local function run(pos, event)
local meta = minetest.get_meta(pos)
local code = meta:get_string("code")
local ok, errmsg = run_inner(pos, code, event)
if not ok then
reset_meta(pos, code, errmsg)
else
reset_formspec(meta, code, errmsg)
end
return ok, errmsg
end
local function reset(pos)
set_port_states(pos, {a=false, b=false, c=false, d=false})
end
-----------------------
-- A.Queue callbacks --
-----------------------
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
@ -457,25 +662,14 @@ mesecon.queue:add_function("lc_interrupt", function (pos, luac_id, iid)
run(pos, {type="interrupt", iid = iid})
end)
local function reset_meta(pos, code, errmsg)
local meta = minetest.get_meta(pos)
meta:set_string("code", code)
code = minetest.formspec_escape(code or "")
errmsg = minetest.formspec_escape(errmsg or "")
meta:set_string("formspec", "size[10,8]"..
"background[-0.2,-0.25;10.4,8.75;jeija_luac_background.png]"..
"textarea[0.2,0.6;10.2,5;code;;"..code.."]"..
"image_button[3.75,6;2.5,1;jeija_luac_runbutton.png;program;]"..
"image_button_exit[9.72,-0.25;0.425,0.4;jeija_close_window.png;exit;]"..
"label[0.1,5;"..errmsg.."]")
meta:set_int("heat", 0)
meta:set_int("luac_id", math.random(1, 65535))
end
local function reset(pos)
set_port_states(pos, {a=false, b=false, c=false, d=false})
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
-- The actual work
digiline:receptor_send(pos, digiline.rules.default, channel, msg)
end)
-----------------------
-- Node Registration --
@ -502,10 +696,23 @@ local digiline = {
receptor = {},
effector = {
action = function(pos, node, channel, msg)
msg = clean_and_weigh_digiline_message(msg)
run(pos, {type = "digiline", channel = channel, msg = msg})
end
}
}
local function get_program(pos)
local meta = minetest.get_meta(pos)
return meta:get_string("code")
end
local function set_program(pos, code)
reset(pos)
reset_meta(pos, code)
return run(pos, {type="program"})
end
local function on_receive_fields(pos, form_name, fields, sender)
if not fields.program then
return
@ -515,12 +722,10 @@ local function on_receive_fields(pos, form_name, fields, sender)
minetest.record_protection_violation(pos, name)
return
end
reset(pos)
reset_meta(pos, fields.code)
local err = run(pos, {type="program"})
if err then
print(err)
reset_meta(pos, fields.code, err)
local ok, err = set_program(pos, fields.code)
if not ok then
-- it's not an error from the server perspective
minetest.log("action", "Lua controller programming error: " .. tostring(err))
end
end
@ -574,11 +779,15 @@ for d = 0, 1 do
receptor = {
state = mesecon.state.on,
rules = output_rules[cid]
}
},
luacontroller = {
get_program = get_program,
set_program = set_program,
},
}
minetest.register_node(node_name, {
description = "LuaController",
description = "Luacontroller",
drawtype = "nodebox",
tiles = {
top,
@ -590,6 +799,7 @@ for d = 0, 1 do
},
inventory_image = top,
paramtype = "light",
is_ground_content = false,
groups = groups,
drop = BASENAME.."0000",
sunlight_propagates = true,
@ -609,9 +819,11 @@ for d = 0, 1 do
d = d == 1,
},
after_dig_node = function (pos, node)
mesecon.do_cooldown(pos)
mesecon.receptor_off(pos, output_rules)
end,
is_luacontroller = true,
on_blast = mesecon.on_blastnode,
})
end
end
@ -619,7 +831,7 @@ end
end
------------------------------
-- Overheated LuaController --
-- Overheated Luacontroller --
------------------------------
minetest.register_node(BASENAME .. "_burnt", {
@ -635,6 +847,7 @@ minetest.register_node(BASENAME .. "_burnt", {
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},
drop = BASENAME.."0000",
sunlight_propagates = true,
@ -652,6 +865,7 @@ minetest.register_node(BASENAME .. "_burnt", {
end,
},
},
on_blast = mesecon.on_blastnode,
})
------------------------

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 718 B

Some files were not shown because too many files have changed in this diff Show More