Added wireless digilines and added attacked_node group to pressure plates
parent
a54d64f317
commit
422075e6e5
|
@ -0,0 +1,36 @@
|
|||
This is digiterm: a [Minetest](http://minetest.net/) mod that provides a couple terminal nodes for providing user interface in [Digilines](https://forum.minetest.net/viewtopic.php?id=5263) networks.
|
||||
|
||||
The first of two nodes that this mod provides is a regular digiterm. To use it, simply place it down and right-click it to set the channel that it will operate on. After setup, you'll see two boxes and two buttons. Any messages on the digiline network sent on the channel that this digiterm is set to will show up in the large box, and messages can be sent out to the digiline network by typing them in to the bottom box and pressing the 'submit' button. The 'update' button is necessary because there is no way of automatically updating formspecs without clobbering any in-progress input; pressing it will submit the form and then re-display with updated output and the current (now saved) input. Normal digiterms send and receive their messages as simple strings, do not append newlines automatically on either end of transmission, and store their state on a per-node basis. One can also issue control codes as a table containing a 'code' key; the only one supported thus far is `{code='cls'}` which clears the output area.
|
||||
|
||||
As an example, a simple chat machine can be constructed using two digiterms set to the channels `term1` and `term2` and one luacontroller with the following code:
|
||||
|
||||
```lua
|
||||
if event.type == 'digiline' then
|
||||
if event.channel == 'term1' then digiline_send('term2', event.msg..'\n');
|
||||
elseif event.channel == 'term2' then digiline_send('term1', event.msg..'\n');
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
The second node defined by this mod is the secure digiterm. Secure digiterms appear almost exactly the same as their "unsecure" counterparts, but their inner workings are almost entirely different. Secure digiterms manage each user's session on a given node separately, so leftover information from one user will not be visible to another user that logs on after them, and two people can use one secure digiterm simultaneously. Instead of sending messages as plain strings, secure digiterms submit user information in the form of a function that returns a table such as `{player="", seq=0, msg=""}`, where 'player' is a string containing the name of the player sending the message, 'msg' is a string containing the actual message, and 'seq' is a unique integer that is incremented with each message. Secure digiterms will also notify the digiline network when a player starts a session by sending a message in the form of a function returning something like `{player="", seq=0, code='init'}` where 'player' and 'seq' have the same meanings as above and 'code' is always 'init' (to catch responses to this, one will have to press 'update' after opening the terminal). Secure digiterms also take their input in the form of plain strings (in which case messages are routed to the last seen player's session), but, if you want to make sure that you're message gets across, you can send it as a table of the form `{player="", msg=""}`, where 'player' is the name of the intended recipient and 'msg' is the message you wish to send them. Secure digiterms also take control codes; these are tables that look like `{player="", code=""}`, where 'player' is the name of recipient and 'code' is one of the same control codes used by regular digiterms (still only 'cls' at the moment).
|
||||
|
||||
What makes secure digiterms secure is their leverage of the fact that luacontrollers cannot define functions. A server receiving messages from a secure digiterm can know that they're authentic because they cannot be forged by an imposter luacontroller (as they cannot define functions) and that they haven't been tampered with since functions are opaque and immutable. Would-be cyber-griefers can store function messages sent across the network and redeploy them later (to a certain extent; functions are impossible to serialize), but they can't increment the 'seq' member, thus well-written servers will be able to easily filter out the re-used messages.
|
||||
|
||||
As a demonstration of secure digiterms, if the following code is loaded onto a luacontroller connected to a secure digiterm set to the 'term' channel, it will show users a welcome message and then echo everything that they enter back to them, with their name and sequence number:
|
||||
|
||||
```lua
|
||||
if event.type == 'digiline' and event.channel == 'term' then
|
||||
local msg = event.msg();
|
||||
local seq = mem[msg.player..'_seq'];
|
||||
if seq and seq >= msg.seq then return end
|
||||
mem[msg.player..'_seq'] = msg.seq;
|
||||
if msg.code == 'init' then
|
||||
digiline_send('term', {player=msg.player, code='cls'});
|
||||
digiline_send('term', {player=msg.player, msg='Hello, '..msg.player..'; welcome to the digiterm demonstration!\n'});
|
||||
else
|
||||
digiline_send('term', {player=msg.player, msg='<'..msg.player..'('..tostring(msg.seq)..')> '..msg.msg..'\n'});
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
This mod is released under the same licensing as the digilines mod (LGPL/WTFPL).
|
|
@ -0,0 +1 @@
|
|||
digilines
|
|
@ -0,0 +1,278 @@
|
|||
|
||||
--a function for producing a digiterm form with specified output and input
|
||||
local function digiterm_formspec(output, input)
|
||||
return 'size[10,11] textarea[.25,.25;10,10.5;output;;'..output..'] button[0,9.5;10,1;update;update] field[.25,10.75;9,1;input;;'..input..'] button[9,10.5;1,1;submit;submit]'
|
||||
end
|
||||
|
||||
--extremely hacky
|
||||
--but neccessary since there doesn't seem to be any better way to refresh a formspec
|
||||
local function hacky_quote_new_digiterm_formspec(startspace)
|
||||
return (startspace and ' ' or '')..digiterm_formspec('${output}', '${input}');
|
||||
end
|
||||
|
||||
--a basic digiterm!
|
||||
minetest.register_node('digiterm:digiterm', {
|
||||
description = 'digiterm',
|
||||
|
||||
--set graphics
|
||||
tiles = {'digiterm_side.png', 'digiterm_side.png', 'digiterm_side.png', 'digiterm_side.png', 'digiterm_side.png', 'digiterm_front.png'},
|
||||
paramtype2 = 'facedir',
|
||||
|
||||
--it's kind of stone-like
|
||||
groups = {cracky = 2},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
|
||||
--digiline stuff (mostly based on other digiline things as suggested)
|
||||
digiline = {
|
||||
receptor = {},
|
||||
effector = {
|
||||
action = function(pos, node, channel, msg)
|
||||
local meta = minetest.get_meta(pos);
|
||||
|
||||
--ignore anything that isn't our channel
|
||||
if channel ~= meta:get_string('channel') then return end
|
||||
|
||||
--if it's a string, append to the end of our output string
|
||||
if type(msg) == 'string' then meta:set_string('output', meta:get_string('output')..msg);
|
||||
|
||||
--it may also be a control code; check if it's a table with a 'code' member
|
||||
elseif type(msg) == 'table' and msg.code then
|
||||
|
||||
--the code 'cls' clears out the output
|
||||
if msg.code == 'cls' then meta:set_string('output', ''); end
|
||||
end
|
||||
end
|
||||
},
|
||||
},
|
||||
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos);
|
||||
|
||||
--initialize the input and output buffers
|
||||
meta:set_string('output', '');
|
||||
meta:set_string('input', '');
|
||||
|
||||
--set an initial formspec for specifying the channel
|
||||
meta:set_string('formspec', 'field[channel;channel;${channel}]');
|
||||
|
||||
--start on an empty channel
|
||||
meta:set_string('channel', '');
|
||||
end,
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
local meta = minetest.get_meta(pos);
|
||||
|
||||
--are we to set the channel?
|
||||
if fields.channel then
|
||||
|
||||
--if so, set it
|
||||
meta:set_string('channel', fields.channel);
|
||||
|
||||
--replace with the operating formspec
|
||||
meta:set_string('formspec', hacky_quote_new_digiterm_formspec(false));
|
||||
|
||||
--and disregard the rest of this callback
|
||||
return;
|
||||
|
||||
end
|
||||
|
||||
--has the user touched the submit button?
|
||||
if fields.submit then
|
||||
|
||||
--if so, submit and reset the input field
|
||||
digiline:receptor_send(pos, digiline.rules.default, meta:get_string('channel'), fields.input);
|
||||
meta:set_string('input', '');
|
||||
else
|
||||
|
||||
--otherwise, update the input so that it doesn't change
|
||||
meta:set_string('input', fields.input);
|
||||
end
|
||||
|
||||
--refresh the formspec hackishly
|
||||
meta:set_string('formspec', hacky_quote_new_digiterm_formspec(meta:get_string('formspec'):sub(0, 1) ~= ' '));
|
||||
end,
|
||||
});
|
||||
|
||||
--add a craft recipe for digiterms
|
||||
minetest.register_craft({
|
||||
output = 'digiterm:digiterm',
|
||||
recipe = {
|
||||
{'default:glass', 'default:glass', 'default:glass'},
|
||||
{'digilines:wire_std_00000000', 'mesecons_luacontroller:luacontroller0000', 'digilines:wire_std_00000000'},
|
||||
{'default:stone', 'default:steel_ingot', 'default:stone'},
|
||||
},
|
||||
});
|
||||
|
||||
--creation and usage of node positions encoded into formspec names
|
||||
local function make_secure_digiterm_formspec_name(pos)
|
||||
return 'digitermspec{x='..tostring(pos.x)..',y='..tostring(pos.y)..',z='..tostring(pos.z)..'}';
|
||||
end
|
||||
local function retrieve_secure_digiterm_pos(formname)
|
||||
|
||||
--try to pull the vector out of the form name
|
||||
local possstr;
|
||||
posstr = formname:match('^digitermspec({x=%-?%d*%.?%d*,y=%-?%d*%.?%d*,z=%-?%d*%.?%d*})$');
|
||||
|
||||
--if we didn't get one, give up
|
||||
if not posstr then return; end
|
||||
|
||||
--otherwise, grab the position
|
||||
return loadstring('return '..posstr)();
|
||||
end
|
||||
|
||||
--a secure digiterm!
|
||||
minetest.register_node('digiterm:digiterm_secure', {
|
||||
description = 'secure digiterm',
|
||||
|
||||
--set graphics
|
||||
tiles = {'digiterm_side.png', 'digiterm_side.png', 'digiterm_side.png', 'digiterm_side.png', 'digiterm_side.png', 'digiterm_secure_front.png'},
|
||||
paramtype2 = 'facedir',
|
||||
|
||||
--it's kind of stone-like
|
||||
groups = {cracky = 2},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
|
||||
--digiline stuff (mostly based on other digiline things as suggested)
|
||||
digiline = {
|
||||
receptor = {},
|
||||
effector = {
|
||||
action = function(pos, node, channel, msg)
|
||||
local meta = minetest.get_meta(pos);
|
||||
|
||||
--ignore anything that isn't our channel
|
||||
if channel ~= meta:get_string('channel') then return end
|
||||
|
||||
--try to find some id
|
||||
local player;
|
||||
if type(msg) == 'table' and msg.player then
|
||||
|
||||
--attempt to grab the player name if it's a string
|
||||
player = msg.player == 'string' and msg.player;
|
||||
|
||||
--if the id is passed correctly and the field msg exists, than that's the actual msg
|
||||
msg = msg.msg or msg;
|
||||
end
|
||||
|
||||
--otherwise, assume the last encountered player is the target of the message
|
||||
if not player then player = meta:get_string('last_player'); end
|
||||
|
||||
--if there is no such player, give up
|
||||
if not player then return; end
|
||||
|
||||
--if the player in question doesn't have a session here, give up
|
||||
if not meta:get_int(player..'_seq') then return; end
|
||||
|
||||
--for strings, append at the end of the player's output
|
||||
if type(msg) == 'string' then meta:set_string(player..'_output', meta:get_string(player..'_output')..msg);
|
||||
|
||||
--handle control codes too
|
||||
elseif type(msg) == 'table' and msg.code then
|
||||
|
||||
--the code 'cls' clears out the output
|
||||
if msg.code == 'cls' then meta:set_string(player..'_output', ''); end
|
||||
end
|
||||
end
|
||||
},
|
||||
},
|
||||
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos);
|
||||
|
||||
--set an initial formspec for specifying the channel
|
||||
meta:set_string('formspec', 'field[channel;channel;${channel}]');
|
||||
|
||||
--start on an empty channel
|
||||
meta:set_string('channel', '');
|
||||
end,
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
local meta = minetest.get_meta(pos);
|
||||
|
||||
--are we to set the channel?
|
||||
if fields.channel then
|
||||
|
||||
--if so, set it
|
||||
meta:set_string('channel', fields.channel);
|
||||
|
||||
--and dump the formspec
|
||||
meta:set_string('formspec', '');
|
||||
end
|
||||
end,
|
||||
|
||||
on_rightclick = function(pos, node, player, itemstack)
|
||||
local meta = minetest.get_meta(pos);
|
||||
|
||||
--grab the player's name
|
||||
local name = player:get_player_name();
|
||||
|
||||
--log this person as the last visitor
|
||||
meta:set_string('last_player', name);
|
||||
|
||||
--grab the input, output, and current message sequence number for this player
|
||||
local output, input, seq = meta:get_string(name..'_output'), meta:get_string(name..'_input'), meta:get_int(name..'_seq');
|
||||
|
||||
--if any are nil, re-initialize all
|
||||
if not output or not input or not seq then
|
||||
output, input, seq = '', '', 0;
|
||||
meta:set_string(name..'_output', output);
|
||||
meta:set_string(name..'_input', input);
|
||||
meta:set_int(name..'_seq', seq);
|
||||
end
|
||||
|
||||
--send a message to let the network so that it can send a greeting
|
||||
digiline:receptor_send(pos, digiline.rules.default, meta:get_string('channel'), function() return {player=name, seq=seq, code='init'} end);
|
||||
|
||||
--bump the sequence number
|
||||
meta:set_int(name..'_seq', seq + 1);
|
||||
|
||||
--show them the formspec
|
||||
minetest.show_formspec(name, make_secure_digiterm_formspec_name(pos), digiterm_formspec(output, input));
|
||||
|
||||
--we don't need to do anything with itemstack
|
||||
return itemstack;
|
||||
end,
|
||||
});
|
||||
|
||||
--we need to register something to pick up our formspec submissions
|
||||
minetest.register_on_player_receive_fields(function (player, formname, fields)
|
||||
|
||||
--we shall attempt to obtain the node coordinates from the formname
|
||||
local pos = retrieve_secure_digiterm_pos(formname);
|
||||
|
||||
--if that failed, then we pass execution on
|
||||
if not pos then return false; end
|
||||
|
||||
--otherwise, grab some metadata and the player name
|
||||
local meta = minetest.get_meta(pos);
|
||||
local name = player:get_player_name();
|
||||
|
||||
--has the user touched the submit button?
|
||||
if fields.submit then
|
||||
|
||||
--if so, submit and reset the input field
|
||||
local seq = meta:get_int(name..'_seq');
|
||||
digiline:receptor_send(pos, digiline.rules.default, meta:get_string('channel'), function() return {player=name, seq=seq, msg=fields.input} end);
|
||||
meta:set_string(name..'_input', '');
|
||||
|
||||
--and bump the sequence number
|
||||
meta:set_int(name..'_seq', seq + 1);
|
||||
else
|
||||
|
||||
--otherwise, update the input so that it doesn't change
|
||||
meta:set_string(name..'_input', fields.input);
|
||||
end
|
||||
|
||||
--re-show the formspec
|
||||
minetest.show_formspec(name, make_secure_digiterm_formspec_name(pos), digiterm_formspec(meta:get_string(name..'_output'), meta:get_string(name..'_input')));
|
||||
|
||||
end);
|
||||
|
||||
--add a craft recipe for the secure variant
|
||||
minetest.register_craft({
|
||||
output = 'digiterm:digiterm_secure',
|
||||
recipe = {
|
||||
{'', 'default:steel_ingot', ''},
|
||||
{'default:steel_ingot', 'digiterm:digiterm', 'default:steel_ingot'},
|
||||
{'', 'default:steel_ingot', ''},
|
||||
},
|
||||
});
|
Binary file not shown.
After Width: | Height: | Size: 181 B |
Binary file not shown.
After Width: | Height: | Size: 186 B |
Binary file not shown.
After Width: | Height: | Size: 159 B |
|
@ -63,7 +63,7 @@ function mesecon:register_pressure_plate(offstate, onstate, description, texture
|
|||
paramtype = "light",
|
||||
selection_box = pp_box_off,
|
||||
node_box = pp_box_off,
|
||||
groups = {snappy = 2, oddly_breakable_by_hand = 3},
|
||||
groups = {snappy = 2, oddly_breakable_by_hand = 3, attached_node=1},
|
||||
description = description,
|
||||
pressureplate = ppspec,
|
||||
on_timer = pp_on_timer,
|
||||
|
|
Loading…
Reference in New Issue