Skip to content

Commit

Permalink
Container tool (#63)
Browse files Browse the repository at this point in the history
* (#55) Container tool

* (#55) Very basic configuration for technic chests

* (#55) Color handling for technic chests

* (#55) Container tool

* (#55) Very basic configuration for technic chests

* (#55) Color handling for technic chests

* (#55) Cleaner handling for chests, default handler for split, keys, channel

* (#55) Shared chest, digiline chest

* Add simple tool behavior tests

* Containertool recipe

* Container tool tests

* Add blacklisted nodes and tool behavior tests for common_defaults

* Test owned unprotected read/write

Co-authored-by: SX <sx@minetest>
  • Loading branch information
S-S-X and SX authored Nov 24, 2020
1 parent 017dd15 commit 34f3c9e
Show file tree
Hide file tree
Showing 19 changed files with 977 additions and 21 deletions.
8 changes: 6 additions & 2 deletions .github/workflows/busted.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ on: [push, pull_request]

jobs:
build:

runs-on: ubuntu-latest

steps:
Expand All @@ -15,6 +14,11 @@ jobs:
run: sudo apt-get install -y luarocks
- name: busted install
run: luarocks install --local busted
- name: busted run

- name: busted run metatool
working-directory: ./metatool
run: $HOME/.luarocks/bin/busted

- name: busted run containertool
working-directory: ./containertool
run: $HOME/.luarocks/bin/busted
7 changes: 5 additions & 2 deletions .luacheckrc
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ globals = {
}

read_globals = {
-- Engine
"minetest",
"default",
"pipeworks",
"vector",
"ItemStack",
"Settings",
"dump",
-- Mods
"default",
"pipeworks",
"technic",
}
39 changes: 39 additions & 0 deletions containertool/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
## Container tool basics

Container tool is made available for copying container settings from one node to another.
For example it can be used to copy settings from one container to another.

#### Copy settings from compatible nodes

Hold tool in your hand and point node that you want to copy settings from, hold special or sneak button and left click on node to copy settings.
Chat will display confirmation message when settings is copied into tool memory.

#### Apply copied settings to compatible nodes

Hold tool containing desired settings in you hand and point node that you want apply settings to.
Left click with tool to apply new settings, chat will display confirmation message when settings is applied to pointed node.

## Nodes compatible with Container tool

r = ability to read
w = ability to write

* technic chests (r/w)
* technic self contained injector (r/w)
* technic machines with inventory (r/w)
* default wooden chests (r/w)
* more_chests:shared (r/w)
* digilines:chest (r/w)

## Minetest protection checks (default settings)

Protection checks are done automatically for all tool uses, node registration does not need any protection checks.
Tool can be used to read settings from protected nodes but it cannot be used to write settings to protected nodes.

## Configuration

Container tool configuration keys with default values (where * is any containertool node):

```
metatool:containertool:nodes:*:protection_bypass_read = interact
```
122 changes: 122 additions & 0 deletions containertool/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
--
-- metatool:containertool is in game tool that allows cloning container configuration
--

local tool = metatool:register_tool('containertool', {
description = 'Container tool',
name = 'Container tool',
texture = 'containertool.png',
recipe = {
{ '', '', 'default:mese_crystal' },
{ '', 'default:chest', '' },
{ 'default:skeleton_key', '', '' }
},
settings = {
copy_key_lock_secret = true,
copy_digiline_channel = false,
},
})

local function has_digiline(name)
local nodedef = minetest.registered_nodes[name]
return nodedef and nodedef.digiline and nodedef.digiline.receptor
end

local function has_key_lock(name)
local nodedef = minetest.registered_nodes[name]
return nodedef and type(nodedef.on_skeleton_key_use) == "function"
end

local function is_tubedevice(name, pos)
local nodedef = minetest.registered_nodes[name]
if nodedef and nodedef.groups and nodedef.groups.tubedevice_receiver then
if nodedef.tube and (not pos or nodedef.tube.input_inventory) then
return true
elseif pos then
local formspec = minetest.get_meta(pos):get("formspec")
return formspec and formspec:find("fs_helpers_cycling:%d+:splitstacks")
end
end
end

local function description(meta, node, pos)
local nicename = meta:get("infotext") or minetest.registered_nodes[node.name].description or node.name
return ("%s at %s"):format(nicename, minetest.pos_to_string(pos))
end

local function get_digiline_channel(meta, node)
if tool.settings.copy_digiline_channel and has_digiline(node.name) then
return meta:get_string("channel")
end
end

local function get_key_lock_secret(meta, player, owner)
if tool.settings.copy_key_lock_secret and player:get_player_name() == owner then
return meta:get("key_lock_secret")
end
end

local function get_splitstacks(meta, node, pos)
return is_tubedevice(node.name, pos) and meta:get_int("splitstacks")
end

tool:ns({
description = description,
is_tubedevice = is_tubedevice,
has_digiline = has_digiline,
get_digiline_channel = get_digiline_channel,
get_common_attributes = function(meta, node, pos, player)
local owner = meta:get("owner")
return {
description = description(meta, node, pos),
owner = owner,
key_lock_secret = get_key_lock_secret(meta, player, owner),
channel = get_digiline_channel(meta, node),
splitstacks = get_splitstacks(meta, node),
}
end,
set_digiline_meta = function(meta, data, node)
if has_digiline(node.name) then
for key, value in pairs(data) do
if key ~= "channel" or tool.settings.copy_digiline_channel then
if type(value) == "string" then
meta:set_string(key, value)
elseif type(value) == "number" then
meta:set_int(key, value)
end
end
end
end
end,
set_key_lock_secret = function(meta, data, node)
if tool.settings.copy_key_lock_secret and data.key_lock_secret and has_key_lock(node.name) then
meta:set_string("key_lock_secret", data.key_lock_secret)
end
end,
set_splitstacks = function(meta, data, node, pos)
if type(data.splitstacks) == "number" and is_tubedevice(node.name, pos) then
meta:set_int("splitstacks", data.splitstacks)
end
end,
get_int = function(meta, key)
local value = meta:get(key)
return value and tonumber(value)
end,
set_int = function(meta, key, value)
if value then meta:set_int(key, value) end
end,
set_string = function (meta, key, value)
if value then meta:set_string(key, value) end
end,
})

-- nodes
local modpath = minetest.get_modpath('containertool')
tool:load_node_definition(dofile(modpath .. '/nodes/technic_chests.lua'))
tool:load_node_definition(dofile(modpath .. '/nodes/more_chests_shared.lua'))
tool:load_node_definition(dofile(modpath .. '/nodes/digilines_chest.lua'))

-- Register after everything else, default behavior for nodes that seems to be compatible
minetest.register_on_mods_loaded(function()
tool:load_node_definition(dofile(modpath .. '/nodes/common_defaults.lua'))
end)
4 changes: 4 additions & 0 deletions containertool/mod.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
name=containertool
description=Provides metatool:containertool to copy/paste container settings
depends=metatool
optional_depends=default,technic_chests,more_chests,digilines
82 changes: 82 additions & 0 deletions containertool/nodes/common_defaults.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
--
-- Register rest of compatible nodes for Container tool
--

local ns = metatool.ns('containertool')

-- Node feature checker
local is_tubedevice = ns.is_tubedevice
-- Base metadata reader
local get_common_attributes = ns.get_common_attributes
-- Special metadata setters
local set_key_lock_secret = ns.set_key_lock_secret
local set_digiline_meta = ns.set_digiline_meta
local set_splitstacks = ns.set_splitstacks

-- Blacklist some nodes
local tubedevice_blacklist = {
"^technic:.*_battery_box",
"^technic:.*tool_workshop",
"^pipeworks:dispenser",
"^pipeworks:nodebreaker",
"^pipeworks:deployer",
"^digtron:",
"^jumpdrive:",
"^vacuum:",
}
local function blacklisted(name)
for _,value in ipairs(tubedevice_blacklist) do
if name:find(value) then return true end
end
end

-- Collect nodes and on_receive_fields callback functions
local nodes = {}
local on_receive_fields = {}
for nodename, nodedef in pairs(minetest.registered_nodes) do print(nodename)
if is_tubedevice(nodename) and not blacklisted(nodename) then
-- Match found, add to registration list
table.insert(nodes, nodename)
if nodedef.on_receive_fields then
on_receive_fields[nodename] = nodedef.on_receive_fields
end
end
end

local definition = {
name = 'common_container',
nodes = nodes,
group = 'container',
protection_bypass_read = "interact",
}

function definition:before_write(pos, player)
-- Stay safe and check both owner and protection for unknown nodes
local meta = minetest.get_meta(pos)
local owner = meta:get("owner")
local owner_check = owner == nil or owner == player:get_player_name()
if not owner_check then
minetest.record_protection_violation(pos, player:get_player_name())
end
return owner_check and metatool.before_write(self, pos, player)
end

function definition:copy(node, pos, player)
-- Read common data like owner, splitstacks, channel etc.
return get_common_attributes(minetest.get_meta(pos), node, pos, player)
end

function definition:paste(node, pos, player, data)
local meta = minetest.get_meta(pos)
set_key_lock_secret(meta, data, node)
set_splitstacks(meta, data, node, pos)
set_digiline_meta(meta, {channel = data.channel}, node)
-- Yeah, sorry... everyone just keeps their internal stuff "protected"
if on_receive_fields[node.name] then
if not pcall(function()on_receive_fields[node.name](pos, "", {}, player)end) then
pcall(function()on_receive_fields[node.name](pos, "", {quit=1}, player)end)
end
end
end

return definition
34 changes: 34 additions & 0 deletions containertool/nodes/digilines_chest.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
--
-- Register digilines chest for containertool
--

local ns = metatool.ns('containertool')
local description = ns.description
local get_digiline_channel = ns.get_digiline_channel

local definition = {
name = 'digilines_chest',
nodes = "digilines:chest",
group = 'container',
protection_bypass_read = "interact",
}

function definition:copy(node, pos, player)
local meta = minetest.get_meta(pos)
local channel = get_digiline_channel(meta, node)
if channel then
return {
description = description(meta, node, pos),
channel = channel,
}
end
end

function definition:paste(node, pos, player, data)
if data.channel and metatool.settings("containertool", "copy_digiline_channel") then
local nodedef = minetest.registered_nodes[node.name]
nodedef.on_receive_fields(pos, "", {channel=data.channel}, player)
end
end

return definition
28 changes: 28 additions & 0 deletions containertool/nodes/more_chests_shared.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
--
-- Register shared chest for containertool
--

local ns = metatool.ns('containertool')
local description = ns.description

local definition = {
name = 'shared_chest',
nodes = "more_chests:shared",
group = 'container',
protection_bypass_read = "interact",
}

function definition:copy(node, pos, player)
local meta = minetest.get_meta(pos)
return {
description = description(meta, node, pos),
shared_with = meta:get("shared"),
}
end

function definition:paste(node, pos, player, data)
local nodedef = minetest.registered_nodes[node.name]
nodedef.on_receive_fields(pos, "", {shared=data.shared_with}, player)
end

return definition
Loading

0 comments on commit 34f3c9e

Please sign in to comment.