mirror of
https://codeberg.org/AntumLuanti/mod-cleaner.git
synced 2025-04-30 14:11:44 -04:00
Compare commits
126 commits
Author | SHA1 | Date | |
---|---|---|---|
|
c72b710c56 | ||
|
209008b88d | ||
|
f35e2f1808 | ||
|
ecf5201220 | ||
|
0d42a1bdd8 | ||
|
7d47f8ff5d | ||
|
93fa96d6c2 | ||
|
9ca550703b | ||
|
b261dda15c | ||
|
06e4408b91 | ||
|
6b3220048b | ||
|
f0004e3c7b | ||
|
e3959a815f | ||
|
45e59dbf8b | ||
|
0098ef5da4 | ||
|
51a812fa86 | ||
|
5efafec098 | ||
|
0ab5d51584 | ||
|
319e671fc2 | ||
|
dd61f9f9cf | ||
|
8645c7aa49 | ||
|
3ac5558a9e | ||
|
50c7a8cd98 | ||
|
f9acae8bc7 | ||
|
93767a2723 | ||
|
da24ace035 | ||
|
e5e0d7f620 | ||
|
b6ce523957 | ||
|
c00ee93e79 | ||
|
bb528d74a8 | ||
|
7f9e645900 | ||
|
fd6032a879 | ||
|
28f1f355c0 | ||
|
df81a7755b | ||
|
689f911cb3 | ||
|
9c80a957e3 | ||
|
29f772dc92 | ||
|
9972213c9b | ||
|
0dbd7b1243 | ||
|
0acbd3318b | ||
|
95adf18f23 | ||
|
09d6475ce0 | ||
|
1277f7a784 | ||
|
44f643195c | ||
|
657ba25890 | ||
|
df515241ed | ||
|
f4b1053064 | ||
|
cbc6235f25 | ||
|
9052543301 | ||
|
970d8b3aab | ||
|
c41f6e8a0a | ||
|
f4b3e2e7c2 | ||
|
c13f78b471 | ||
|
99544d286f | ||
|
fbcfe1646a | ||
|
4d959e69cb | ||
|
c98f6a656e | ||
|
3a708a03b6 | ||
|
e0bf9a56e6 | ||
|
4c5069447c | ||
|
64f31da3f2 | ||
|
c5ae889ee5 | ||
|
cc11d749c2 | ||
|
a708add44d | ||
|
182726e999 | ||
|
982a746e23 | ||
|
14394db3ea | ||
|
4f9e5a6434 | ||
|
43dadcb706 | ||
|
ca65bafb5f | ||
|
d3082948b1 | ||
|
cf01b532f0 | ||
|
d4ec55be08 | ||
|
9dfa90292c | ||
|
6c1104d19b | ||
|
6bbc63dd19 | ||
|
a657e566d5 | ||
|
b9455caeb2 | ||
|
6b211cf5f6 | ||
|
84d3960664 | ||
|
a221c11ec6 | ||
|
b1fd6df8ce | ||
|
5568a1e91a | ||
|
d2209675ed | ||
|
cad47e4c88 | ||
|
4bb6bd0300 | ||
|
9365fd9515 | ||
|
9fa1b2b4ff | ||
|
9230e0c392 | ||
|
3080612409 | ||
|
2c9a44fbc1 | ||
|
7a482de659 | ||
|
5fcc824d0e | ||
|
e81ed836dc | ||
|
9592b1df73 | ||
|
f8fd1506eb | ||
|
cc5856b561 | ||
|
4cece82cfa | ||
|
df3428059c | ||
|
50db695476 | ||
|
1495ebea16 | ||
|
02489ec530 | ||
|
bb36a723d3 | ||
|
efc5ef2660 | ||
|
37ca2b7dab | ||
|
d065c3f7ae | ||
|
ad2fc046a1 | ||
|
13a6fd04be | ||
|
2d7c5aee1f | ||
|
69f87b6b73 | ||
|
7f042330b1 | ||
|
7d5f61756e | ||
|
bf37b2e4a5 | ||
|
0d4b29573c | ||
|
2afbdd55e2 | ||
|
fe80682515 | ||
|
bb94fa86a2 | ||
|
958d45775c | ||
|
e75ee0ccd8 | ||
|
261d0ed5e7 | ||
|
774b22a55b | ||
|
7280c2a538 | ||
|
3ee5184958 | ||
|
2c127af4af | ||
|
6b50d1c52a | ||
|
f247118c63 |
28 changed files with 2129 additions and 140 deletions
11
.cdb.json
Normal file
11
.cdb.json
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"type": "MOD",
|
||||||
|
"title": "Cleaner",
|
||||||
|
"short_description": "Remove/Replace unknown entities, nodes, & items.",
|
||||||
|
"license": "MIT",
|
||||||
|
"media_license": "CC0-1.0",
|
||||||
|
"tags": ["world_tools"],
|
||||||
|
"repo": "https://codeberg.org/AntumLuanti/mod-cleaner",
|
||||||
|
"issue_tracker": "https://codeberg.org/AntumLuanti/mod-cleaner/issues",
|
||||||
|
"forums": 18381
|
||||||
|
}
|
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.* export-ignore
|
||||||
|
*.py export-ignore
|
30
.github/workflows/reference.yml
vendored
Normal file
30
.github/workflows/reference.yml
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
|
||||||
|
name: Build Reference
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v[0-9]*'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build Reference
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Setup Lua
|
||||||
|
uses: leafo/gh-actions-lua@v8
|
||||||
|
with:
|
||||||
|
luaVersion: 5.4
|
||||||
|
- name: Setup Lua Rocks
|
||||||
|
uses: leafo/gh-actions-luarocks@v4
|
||||||
|
- name: Setup dependencies
|
||||||
|
run: luarocks install --only-deps https://raw.githubusercontent.com/lunarmodules/LDoc/master/ldoc-scm-3.rockspec
|
||||||
|
- name: Setup LDoc
|
||||||
|
run: git clone --single-branch --branch=custom https://github.com/AntumDeluge/LDoc.git ldoc
|
||||||
|
- name: Checkout & Build Docs
|
||||||
|
run: git clone https://github.com/AntumMT/mod-cleaner.git cleaner && cd cleaner && chmod +x .ldoc/build_versioned_docs.sh && ./.ldoc/build_versioned_docs.sh
|
||||||
|
- name: Deploy
|
||||||
|
uses: peaceiris/actions-gh-pages@v3
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
publish_dir: cleaner/docs/
|
98
.ldoc/build_versioned_docs.sh
Executable file
98
.ldoc/build_versioned_docs.sh
Executable file
|
@ -0,0 +1,98 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# place this file in mod ".ldoc" directory
|
||||||
|
|
||||||
|
|
||||||
|
d_config="$(dirname $(readlink -f $0))"
|
||||||
|
|
||||||
|
cd "${d_config}/.."
|
||||||
|
|
||||||
|
d_root="$(pwd)"
|
||||||
|
d_export="${d_export:-${d_root}/docs/reference}"
|
||||||
|
|
||||||
|
cmd_ldoc="${d_root}/../ldoc/ldoc.lua"
|
||||||
|
if test -f "${cmd_ldoc}"; then
|
||||||
|
if test ! -x "${cmd_ldoc}"; then
|
||||||
|
chmod +x "${cmd_ldoc}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
cmd_ldoc="ldoc"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# clean old files
|
||||||
|
rm -rf "${d_export}"
|
||||||
|
|
||||||
|
# store current branch
|
||||||
|
main_branch="$(git branch --show-current)"
|
||||||
|
|
||||||
|
html_out="<html>\n<head></head>\n\n<body>\n\n<ul>\n"
|
||||||
|
|
||||||
|
# generate new doc files
|
||||||
|
mkdir -p "${d_export}"
|
||||||
|
for vinfo in $(git tag -l --sort=-v:refname | grep "^v[0-9]"); do
|
||||||
|
echo -e "\nbuilding ${vinfo} docs ..."
|
||||||
|
git checkout ${vinfo}
|
||||||
|
d_temp="${d_config}/temp"
|
||||||
|
mkdir -p "${d_temp}"
|
||||||
|
|
||||||
|
# backward compat
|
||||||
|
f_config="${d_root}/docs/config.ld"
|
||||||
|
if test ! -f "${f_config}"; then
|
||||||
|
f_config="${d_config}/config.ld"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test ! -f "${f_config}"; then
|
||||||
|
echo -e "\nLDoc config not available for ${vinfo}, skipping build ..."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
"${cmd_ldoc}" --UNSAFE_NO_SANDBOX --multimodule -c "${f_config}" -d "${d_temp}" "${d_root}"; retval=$?
|
||||||
|
if test ${retval} -ne 0; then
|
||||||
|
echo -e "\nERROR: doc build for ${vinfo} failed!"
|
||||||
|
rm -rf "${d_temp}"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# show version info
|
||||||
|
for html in $(find "${d_temp}" -type f -name "*.html"); do
|
||||||
|
sed -i -e "s|^<h1>[cC]leaner</h1>$|<h1>Cleaner <span style=\"font-size:12pt;\">(${vinfo})</span></h1>|" \
|
||||||
|
"${html}"
|
||||||
|
done
|
||||||
|
|
||||||
|
if test -d "${d_root}/textures"; then
|
||||||
|
# copy textures to data directory
|
||||||
|
echo -e "\ncopying textures ..."
|
||||||
|
d_data="${d_temp}/data"
|
||||||
|
mkdir -p "${d_data}"
|
||||||
|
texture_count=0
|
||||||
|
for png in $(find "${d_root}/textures" -maxdepth 1 -type f -name "*.png"); do
|
||||||
|
t_png="${d_data}/$(basename ${png})"
|
||||||
|
if test -f "${t_png}"; then
|
||||||
|
echo "WARNING: not overwriting existing file: ${t_png}"
|
||||||
|
else
|
||||||
|
cp "${png}" "${d_data}"
|
||||||
|
texture_count=$((texture_count + 1))
|
||||||
|
printf "\rcopied ${texture_count} textures"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
mv "${d_temp}" "${d_export}/${vinfo}"
|
||||||
|
if test -z ${vcur+x}; then
|
||||||
|
vcur="${vinfo}"
|
||||||
|
ln -s "${d_export}/${vinfo}" "${d_export}/current"
|
||||||
|
ln -s "${d_export}/${vinfo}" "${d_export}/latest"
|
||||||
|
html_out="${html_out} <li><a href=\"current/\">current</a></li>\n"
|
||||||
|
html_out="${html_out} <li><a href=\"latest/\">latest</a></li>\n"
|
||||||
|
fi
|
||||||
|
html_out="${html_out} <li><a href=\"${vinfo}/\">${vinfo}</a></li>\n"
|
||||||
|
done
|
||||||
|
|
||||||
|
html_out="${html_out}</ul>\n\n</body></html>"
|
||||||
|
|
||||||
|
cd "${d_root}"
|
||||||
|
git checkout ${main_branch}
|
||||||
|
|
||||||
|
echo -e "${html_out}" > "${d_export}/index.html"
|
||||||
|
|
||||||
|
echo -e "\nDone!"
|
201
.ldoc/config.ld
Normal file
201
.ldoc/config.ld
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
|
||||||
|
local dofile, print, error, type, table, ipairs, string, tostring
|
||||||
|
if import then
|
||||||
|
dofile = import("dofile")
|
||||||
|
print = import("print")
|
||||||
|
error = import("error")
|
||||||
|
type = import("type")
|
||||||
|
table = import("table")
|
||||||
|
ipairs = import("ipairs")
|
||||||
|
string = import("string")
|
||||||
|
tostring = import("tostring")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
project = "Cleaner"
|
||||||
|
title = "Cleaner mod for Luanti"
|
||||||
|
format = "markdown"
|
||||||
|
not_luadoc=true
|
||||||
|
boilerplate = false
|
||||||
|
icon = "textures/cleaner_pencil.png"
|
||||||
|
favicon = "https://www.luanti.org/media/icon.svg"
|
||||||
|
|
||||||
|
file = {
|
||||||
|
"settings.lua",
|
||||||
|
"api.lua",
|
||||||
|
"chat.lua",
|
||||||
|
"tools.lua",
|
||||||
|
".ldoc/config.luadoc",
|
||||||
|
}
|
||||||
|
|
||||||
|
new_type("chatcmd", "Chat Commands")
|
||||||
|
new_type("setting", "Settings")
|
||||||
|
new_type("tool", "Tools")
|
||||||
|
new_type("json", "JSON Configurations")
|
||||||
|
|
||||||
|
|
||||||
|
local function video_frame(src)
|
||||||
|
return '<iframe width="560" height="315" src="' .. src
|
||||||
|
.. '" title="Video Player" frameborder="0"'
|
||||||
|
.. ' allow="fullscreen;"></iframe>'
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local tags
|
||||||
|
tags, custom_tags = dofile(".ldoc/tags.ld")
|
||||||
|
|
||||||
|
|
||||||
|
-- START: handling items to prevent re-parsing
|
||||||
|
|
||||||
|
local registered_items = {}
|
||||||
|
|
||||||
|
local function is_registered(item)
|
||||||
|
if not registered_items[item.type] then return false end
|
||||||
|
|
||||||
|
for _, tbl in ipairs(registered_items[item.type]) do
|
||||||
|
if item == tbl then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function register(item)
|
||||||
|
if not registered_items[item.type] then
|
||||||
|
registered_items[item.type] = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
if not is_registered(item) then
|
||||||
|
table.insert(registered_items[item.type], item)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- END:
|
||||||
|
|
||||||
|
|
||||||
|
local function format_setting_tag(desc, value)
|
||||||
|
return "\n- <span style=\"font-size:80%;\">`" .. desc .. ":`</span> `" .. value .. "`"
|
||||||
|
end
|
||||||
|
|
||||||
|
local function setting_handler(item)
|
||||||
|
if not ipairs or not type then
|
||||||
|
return item
|
||||||
|
end
|
||||||
|
|
||||||
|
local tags = {
|
||||||
|
{"settype", "type"},
|
||||||
|
{"default"},
|
||||||
|
{"min", "minimum value"},
|
||||||
|
{"max", "maximum value"},
|
||||||
|
}
|
||||||
|
|
||||||
|
local def = {
|
||||||
|
["settype"] = format_setting_tag("type", "string"),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, t in ipairs(tags) do
|
||||||
|
local name = t[1]
|
||||||
|
local desc = t[2]
|
||||||
|
if not desc then desc = name end
|
||||||
|
|
||||||
|
local value = item.tags[name]
|
||||||
|
if type(value) == "table" then
|
||||||
|
if #value > 1 then
|
||||||
|
local msg = item.file.filename .. " (line " .. item.lineno
|
||||||
|
.. "): multiple instances of tag \"" .. name .. "\" found"
|
||||||
|
if error then
|
||||||
|
error(msg)
|
||||||
|
elseif print then
|
||||||
|
print("WARNING: " .. msg)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if value[1] then
|
||||||
|
def[name] = format_setting_tag(desc, value[1])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
item.description = item.description .. "\n\n**Definition:**\n" .. def.settype
|
||||||
|
for _, t in ipairs({def.default, def.min, def.max}) do
|
||||||
|
if t then
|
||||||
|
item.description = item.description .. t
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return item
|
||||||
|
end
|
||||||
|
|
||||||
|
local function chatcmd_handler(item)
|
||||||
|
for _, p in ipairs(item.params) do
|
||||||
|
if item.modifiers.param[p].opt then
|
||||||
|
item.name = item.name .. " [" .. p .. "]"
|
||||||
|
else
|
||||||
|
item.name = item.name .. " <" .. p .. ">"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if #item.params > 0 then
|
||||||
|
local pstring = "### Parameters:\n"
|
||||||
|
for k, param in pairs(item.params) do
|
||||||
|
if type(k) == "number" then
|
||||||
|
local value = item.params.map[param]
|
||||||
|
|
||||||
|
pstring = pstring .. '\n- <span class="parameter">'
|
||||||
|
.. param .. '</span>'
|
||||||
|
|
||||||
|
local modifiers = item.modifiers.param[param]
|
||||||
|
if modifiers and modifiers.type then
|
||||||
|
pstring = pstring .. ' <span class="types"><span class="type">`' .. modifiers.type .. '`</span></span>'
|
||||||
|
end
|
||||||
|
|
||||||
|
if value then
|
||||||
|
pstring = pstring .. value
|
||||||
|
end
|
||||||
|
|
||||||
|
if modifiers and modifiers.opt then
|
||||||
|
pstring = pstring .. " *(optional)*"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
item.description = item.description .. "\n\n" .. pstring
|
||||||
|
-- clear parameter list
|
||||||
|
item.params = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
return item
|
||||||
|
end
|
||||||
|
|
||||||
|
function custom_display_name_handler(item, default_handler)
|
||||||
|
if not is_registered(item) then
|
||||||
|
if item.type == "setting" then
|
||||||
|
item = setting_handler(item)
|
||||||
|
elseif item.type == "chatcmd" then
|
||||||
|
item = chatcmd_handler(item)
|
||||||
|
end
|
||||||
|
|
||||||
|
local parse_tags = {"priv", "note"}
|
||||||
|
for _, pt in ipairs(parse_tags) do
|
||||||
|
local tvalues = item.tags[pt]
|
||||||
|
if tvalues then
|
||||||
|
local tstring = ""
|
||||||
|
|
||||||
|
local title = tags.get_title(pt)
|
||||||
|
if title then
|
||||||
|
tstring = tstring .. "\n\n### " .. title .. ":\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, tv in ipairs(tvalues) do
|
||||||
|
tstring = tstring .. "\n- " .. tags.format(pt, tv)
|
||||||
|
end
|
||||||
|
|
||||||
|
item.description = item.description .. tstring
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
register(item)
|
||||||
|
return default_handler(item)
|
||||||
|
end
|
55
.ldoc/config.luadoc
Normal file
55
.ldoc/config.luadoc
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
|
||||||
|
--- World Path Configuration
|
||||||
|
--
|
||||||
|
-- @topic config
|
||||||
|
|
||||||
|
|
||||||
|
--- Main configuration file.
|
||||||
|
--
|
||||||
|
-- Registering items, entities, etc. for cleaning can be done in ***cleaner.json***
|
||||||
|
-- in the world directory (`<world_path>/cleaner.json`). If it does not exist
|
||||||
|
-- it will be created automatically when the server is started.
|
||||||
|
--
|
||||||
|
-- It is formatted as follows:
|
||||||
|
--
|
||||||
|
-- {
|
||||||
|
-- "entities" :
|
||||||
|
-- {
|
||||||
|
-- "remove" : []
|
||||||
|
-- },
|
||||||
|
-- "items" :
|
||||||
|
-- {
|
||||||
|
-- "replace" : {}
|
||||||
|
-- },
|
||||||
|
-- "nodes" :
|
||||||
|
-- {
|
||||||
|
-- "remove" : [],
|
||||||
|
-- "replace" : {}
|
||||||
|
-- },
|
||||||
|
-- "ores" :
|
||||||
|
-- {
|
||||||
|
-- "remove" : []
|
||||||
|
-- }
|
||||||
|
-- }
|
||||||
|
--
|
||||||
|
-- `remove` key works for nodes, entities, & ores. `replace` key works for
|
||||||
|
-- nodes & items. Their functions are self-explanatory.
|
||||||
|
--
|
||||||
|
-- @json cleaner.json
|
||||||
|
-- @usage
|
||||||
|
-- Cleaning nodes example:
|
||||||
|
-- {
|
||||||
|
-- "nodes" :
|
||||||
|
-- {
|
||||||
|
-- "remove" :
|
||||||
|
-- [
|
||||||
|
-- "old:node_1",
|
||||||
|
-- "old:node_2",
|
||||||
|
-- ],
|
||||||
|
-- "replace" :
|
||||||
|
-- {
|
||||||
|
-- "old:node_3" : "new:node_1",
|
||||||
|
-- "old:node_4" : "new:node_2",
|
||||||
|
-- },
|
||||||
|
-- },
|
||||||
|
-- }
|
54
.ldoc/gendoc.sh
Executable file
54
.ldoc/gendoc.sh
Executable file
|
@ -0,0 +1,54 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# place this file in mod ".ldoc" directory
|
||||||
|
|
||||||
|
|
||||||
|
d_ldoc="$(dirname $(readlink -f $0))"
|
||||||
|
f_config="${d_ldoc}/config.ld"
|
||||||
|
|
||||||
|
cd "${d_ldoc}/.."
|
||||||
|
|
||||||
|
d_root="$(pwd)"
|
||||||
|
d_export="${d_export:-${d_root}/docs/reference}"
|
||||||
|
|
||||||
|
cmd_ldoc="${d_ldoc}/ldoc/ldoc.lua"
|
||||||
|
if test ! -x "${cmd_ldoc}"; then
|
||||||
|
cmd_ldoc="ldoc"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# clean old files
|
||||||
|
rm -rf "${d_export}"
|
||||||
|
|
||||||
|
vinfo="v$(grep "^version = " "${d_root}/mod.conf" | head -1 | sed -e 's/version = //')"
|
||||||
|
d_data="${d_export}/${vinfo}/data"
|
||||||
|
|
||||||
|
# generate new doc files
|
||||||
|
"${cmd_ldoc}" --UNSAFE_NO_SANDBOX --multimodule -c "${f_config}" -d "${d_export}/${vinfo}" "${d_root}"; retval=$?
|
||||||
|
|
||||||
|
# check exit status
|
||||||
|
if test ${retval} -ne 0; then
|
||||||
|
echo -e "\nan error occurred (ldoc return code: ${retval})"
|
||||||
|
exit ${retval}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# show version info
|
||||||
|
for html in $(find "${d_export}/${vinfo}" -type f -name "*.html"); do
|
||||||
|
sed -i -e "s|^<h1>[cC]leaner</h1>$|<h1>Cleaner <span style=\"font-size:12pt;\">(${vinfo})</span></h1>|" "${html}"
|
||||||
|
done
|
||||||
|
|
||||||
|
# copy textures to data directory
|
||||||
|
echo -e "\ncopying textures ..."
|
||||||
|
mkdir -p "${d_data}"
|
||||||
|
texture_count=0
|
||||||
|
for png in $(find "${d_root}/textures" -maxdepth 1 -type f -name "*.png"); do
|
||||||
|
t_png="${d_data}/$(basename ${png})"
|
||||||
|
if test -f "${t_png}"; then
|
||||||
|
echo "WARNING: not overwriting existing file: ${t_png}"
|
||||||
|
else
|
||||||
|
cp "${png}" "${d_data}"
|
||||||
|
texture_count=$((texture_count + 1))
|
||||||
|
printf "\rcopied ${texture_count} textures"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo -e "\n\nDone!"
|
81
.ldoc/tags.ld
Normal file
81
.ldoc/tags.ld
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
|
||||||
|
local tags = {}
|
||||||
|
local tag_list = {}
|
||||||
|
local custom_tags = {}
|
||||||
|
|
||||||
|
local register_tag = function(name, tag)
|
||||||
|
local new_tag = {name, title=tag.title, hidden=tag.hidden, format=tag.format}
|
||||||
|
table.insert(custom_tags, new_tag)
|
||||||
|
tag_list[name] = {title=tag.title, format=tag.format}
|
||||||
|
end
|
||||||
|
|
||||||
|
tags.get_title = function(tname)
|
||||||
|
local t = tag_list[tname]
|
||||||
|
if t then
|
||||||
|
return t.title
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
tags.format = function(tname, value)
|
||||||
|
local t = tag_list[tname]
|
||||||
|
if t then
|
||||||
|
if type(t.format) == "function" then
|
||||||
|
value = t.format(value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local new_tags = {
|
||||||
|
["priv"] = {
|
||||||
|
title = "Required Privileges",
|
||||||
|
hidden = true,
|
||||||
|
},
|
||||||
|
["note"] = {
|
||||||
|
title = "Notes",
|
||||||
|
hidden = true,
|
||||||
|
format = function(value)
|
||||||
|
return "*" .. value .. "*"
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
["video"] = {
|
||||||
|
title = "Video",
|
||||||
|
format = video_frame,
|
||||||
|
},
|
||||||
|
["youtube"] = {
|
||||||
|
title = "Video",
|
||||||
|
format = function(value)
|
||||||
|
return video_frame("https://www.youtube.com/embed/" .. value)
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
-- settings
|
||||||
|
["settype"] = {
|
||||||
|
title = "Setting Type",
|
||||||
|
hidden = true,
|
||||||
|
},
|
||||||
|
["default"] = {
|
||||||
|
title = "Default Value",
|
||||||
|
hidden = true,
|
||||||
|
},
|
||||||
|
-- craft items/tools
|
||||||
|
["img"] = {
|
||||||
|
title = "Image",
|
||||||
|
format = function(value)
|
||||||
|
return "<img src=\"../data/" .. value .. "\" style=\"width:32px; height:32px;\" />"
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
-- chat commands
|
||||||
|
["chatparam"] = {
|
||||||
|
title = "Parameters",
|
||||||
|
hidden = true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v in pairs(new_tags) do
|
||||||
|
register_tag(k, v)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return tags, custom_tags
|
88
README.md
88
README.md
|
@ -1,60 +1,86 @@
|
||||||
## Cleaner mod for Minetest
|
## Cleaner mod for Luanti
|
||||||
|
|
||||||
---
|
|
||||||
### Description:
|
### Description:
|
||||||
|
|
||||||
A [Minetest][] mod that can be used to remove/replace unknown entities, nodes, & items. Forked from [PilzAdam's ***clean*** mod][f.pilzadam].
|
A [Luanti (Minetest)][Luanti] mod that can be used to remove/replace unknown entities, nodes, & items. Originally forked from [PilzAdam's ***clean*** mod][f.pilzadam].
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
---
|
|
||||||
### Licensing:
|
### Licensing:
|
||||||
|
|
||||||
[MIT](LICENSE.txt)
|
- Code: [MIT](LICENSE.txt)
|
||||||
|
- Textures: CC0
|
||||||
|
|
||||||
---
|
|
||||||
### Requirements:
|
### Requirements:
|
||||||
|
|
||||||
- Minetest 0.4.16 or newer
|
- Luanti minimum version: 5.0
|
||||||
- Depends: none
|
- Depends: none
|
||||||
|
|
||||||
---
|
|
||||||
### Usage:
|
### Usage:
|
||||||
|
|
||||||
There are three files in the world path that can be edited: `clean_entities.json`, `clean_nodes.json`, & `clean_items.json`. If they do not already exist with the server is started they will be created automatically.
|
Registering items, entities, etc. for cleaning can be done in `cleaner.json` in the world directory. If it does not exist it will be created automatically when the server is started.
|
||||||
|
|
||||||
They are formatted as follows:
|
It is formatted as follows:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"remove":
|
"entities" :
|
||||||
[
|
|
||||||
"creatures:ghost",
|
|
||||||
"creatures:chicken",
|
|
||||||
"creatures:sheep",
|
|
||||||
"creatures:skeleton",
|
|
||||||
"creatures:zombie",
|
|
||||||
"creatures:oerkki",
|
|
||||||
"creatures:shark",
|
|
||||||
],
|
|
||||||
"replace":
|
|
||||||
{
|
{
|
||||||
"biofuel:biofuel":"default:leaves",
|
"remove" : []
|
||||||
"helicopter:heli":"default:copper_lump",
|
},
|
||||||
"spawneggs:ghost":"alternode:key",
|
"items" :
|
||||||
"spawneggs:oerkki":"default:mese_crystal",
|
{
|
||||||
"unifieddyes:airbrush":"default:coal_lump",
|
"replace" : {}
|
||||||
|
},
|
||||||
|
"nodes" :
|
||||||
|
{
|
||||||
|
"remove" : [],
|
||||||
|
"replace" : {}
|
||||||
|
},
|
||||||
|
"ores" :
|
||||||
|
{
|
||||||
|
"remove" : []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Cleaning nodes example:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"nodes" :
|
||||||
|
{
|
||||||
|
"remove" : [
|
||||||
|
"old:node_1",
|
||||||
|
"old:node_2",
|
||||||
|
],
|
||||||
|
"replace" : {
|
||||||
|
"old:node_3" : "new:node_1",
|
||||||
|
"old:node_4" : "new:node_2",
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`remove` key works for nodes & entities. `replace` key works for nodes & items. Their functions are self-explanatory.
|
`remove` key works for nodes, entities, & ores. `replace` key works for nodes & items. Their functions are self-explanatory.
|
||||||
|
|
||||||
|
#### Settings:
|
||||||
|
|
||||||
|
```
|
||||||
|
cleaner.unsafe
|
||||||
|
- Enables unsafe methods & commands (remove_ore).
|
||||||
|
- type: bool
|
||||||
|
- default: false
|
||||||
|
```
|
||||||
|
|
||||||
---
|
|
||||||
### Links:
|
### Links:
|
||||||
|
|
||||||
- [Forum](https://forum.minetest.net/viewtopic.php?t=18381)
|
- [][ContentDB]
|
||||||
|
- [Forum](https://forum.luanti.org/viewtopic.php?t=18381)
|
||||||
- [Git repo](https://github.com/AntumMT/mod-cleaner)
|
- [Git repo](https://github.com/AntumMT/mod-cleaner)
|
||||||
|
- [Reference](https://antummt.github.io/mod-cleaner/reference/latest/)
|
||||||
- [Changelog](changelog.txt)
|
- [Changelog](changelog.txt)
|
||||||
- [TODO](TODO.txt)
|
- [TODO](TODO.txt)
|
||||||
|
|
||||||
|
|
||||||
[Minetest]: http://www.minetest.net/
|
[Luanti]: https://luanti.org/
|
||||||
[f.pilzadam]: https://forum.minetest.net/viewtopic.php?t=2777
|
[f.pilzadam]: https://forum.luanti.org/viewtopic.php?t=2777
|
||||||
|
[ContentDB]: https://content.luanti.org/packages/AntumDeluge/cleaner/
|
||||||
|
|
12
TODO.txt
12
TODO.txt
|
@ -1,2 +1,14 @@
|
||||||
|
|
||||||
TODO:
|
TODO:
|
||||||
|
- update world file when chat commands are used
|
||||||
|
- update inventories when items are replaced:
|
||||||
|
- creative
|
||||||
|
- storage (chests, etc.)
|
||||||
|
- add LBM when removing an item if it is a node
|
||||||
|
- add "radius" option for pencil or "xlen", "ylen", & "zlen" options
|
||||||
|
- add "xrotate" & "zrorate" modes for pencil
|
||||||
|
- don't require "server" priv for "find_unknown_nodes" & "find_neaby_nodes" commands
|
||||||
|
- add chat command to find nodes with specified attributes
|
||||||
|
- may be better to update player inventories on login than add aliases for items
|
||||||
|
- use aliases for unknown nodes instead of LBM
|
||||||
|
- only use LBM when a node to replace is still registered
|
||||||
|
|
194
api.lua
Normal file
194
api.lua
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
|
||||||
|
--- Cleaner API
|
||||||
|
--
|
||||||
|
-- @topic api
|
||||||
|
|
||||||
|
|
||||||
|
local replace_items = {}
|
||||||
|
local replace_nodes = {}
|
||||||
|
|
||||||
|
|
||||||
|
--- Retrieves list of items to be replaced.
|
||||||
|
--
|
||||||
|
-- @treturn table Items to be replaced.
|
||||||
|
function cleaner.get_replace_items()
|
||||||
|
return replace_items
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Retrieves list of nodes to be replaced.
|
||||||
|
--
|
||||||
|
-- @treturn table Nodes to be replaced.
|
||||||
|
function cleaner.get_replace_nodes()
|
||||||
|
return replace_nodes
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Registers an entity to be removed.
|
||||||
|
--
|
||||||
|
-- @tparam string src Entity technical name.
|
||||||
|
function cleaner.register_entity_removal(src)
|
||||||
|
core.register_entity(":" .. src, {
|
||||||
|
on_activate = function(self, ...)
|
||||||
|
self.object:remove()
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Registers a node to be removed.
|
||||||
|
--
|
||||||
|
-- @tparam string src Node technical name.
|
||||||
|
function cleaner.register_node_removal(src)
|
||||||
|
core.register_node(":" .. src, {
|
||||||
|
groups = {to_remove=1},
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function update_list(inv, listname, src, tgt)
|
||||||
|
if not inv then
|
||||||
|
cleaner.log("error", "cannot update list of unknown inventory")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local list = inv:get_list(listname)
|
||||||
|
if not list then
|
||||||
|
cleaner.log("warning", "unknown inventory list: " .. listname)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
for idx, stack in pairs(list) do
|
||||||
|
if stack:get_name() == src then
|
||||||
|
local new_stack = ItemStack(tgt)
|
||||||
|
new_stack:set_count(stack:get_count())
|
||||||
|
inv:set_stack(listname, idx, new_stack)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Replaces an item with another registered item.
|
||||||
|
--
|
||||||
|
-- @tparam string src Technical name of item to be replaced.
|
||||||
|
-- @tparam string tgt Technical name of item to be used in place.
|
||||||
|
-- @tparam[opt] bool update_players `true` updates inventory lists associated with players (default: `false`).
|
||||||
|
function cleaner.replace_item(src, tgt, update_players)
|
||||||
|
update_players = not (update_players ~= true)
|
||||||
|
|
||||||
|
if not core.registered_items[tgt] then
|
||||||
|
return false, S('Cannot use unknown item "@1" as replacement.', tgt)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not core.registered_items[src] then
|
||||||
|
cleaner.log("info", "\"" .. src .. "\" not registered, not unregistering")
|
||||||
|
else
|
||||||
|
cleaner.log("warning", "overriding registered item \"" .. src .. "\"")
|
||||||
|
|
||||||
|
core.unregister_item(src)
|
||||||
|
if core.registered_items[src] then
|
||||||
|
cleaner.log("error", "could not unregister \"" .. src .. "\"")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
core.register_alias(src, tgt)
|
||||||
|
if core.registered_aliases[src] == tgt then
|
||||||
|
cleaner.log("info", "registered alias \"" .. src .. "\" for \"" .. tgt .. "\"")
|
||||||
|
else
|
||||||
|
cleaner.log("error", "could not register alias \"" .. src .. "\" for \"" .. tgt .. "\"")
|
||||||
|
end
|
||||||
|
|
||||||
|
local bags = core.get_modpath("bags") ~= nil
|
||||||
|
local armor = core.get_modpath("3d_armor") ~= nil
|
||||||
|
|
||||||
|
-- update player inventories
|
||||||
|
if update_players then
|
||||||
|
for _, player in ipairs(core.get_connected_players()) do
|
||||||
|
local pinv = player:get_inventory()
|
||||||
|
update_list(pinv, "main", src, tgt)
|
||||||
|
|
||||||
|
if bags then
|
||||||
|
for i = 1, 4 do
|
||||||
|
update_list(pinv, "bag" .. i .. "contents", src, tgt)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if armor then
|
||||||
|
local armor_inv = core.get_inventory({type="detached", name=player:get_player_name() .. "_armor"})
|
||||||
|
update_list(armor_inv, "armor", src, tgt)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Registeres an item to be replaced.
|
||||||
|
--
|
||||||
|
-- @tparam string src Technical name of item to be replaced.
|
||||||
|
-- @tparam string tgt Technical name of item to be used in place.
|
||||||
|
function cleaner.register_item_replacement(src, tgt)
|
||||||
|
replace_items[src] = tgt
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Registers a node to be replaced.
|
||||||
|
--
|
||||||
|
-- @tparam string src Technical name of node to be replaced.
|
||||||
|
-- @tparam string tgt Technical name of node to be used in place.
|
||||||
|
function cleaner.register_node_replacement(src, tgt)
|
||||||
|
core.register_node(":" .. src, {
|
||||||
|
groups = {to_replace=1},
|
||||||
|
})
|
||||||
|
|
||||||
|
replace_nodes[src] = tgt
|
||||||
|
cleaner.register_item_replacement(src, tgt)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Unsafe Methods.
|
||||||
|
--
|
||||||
|
-- Enabled with [cleaner.unsafe](settings.html#cleaner.unsafe) setting.
|
||||||
|
--
|
||||||
|
-- @section unsafe
|
||||||
|
|
||||||
|
|
||||||
|
if cleaner.unsafe then
|
||||||
|
local remove_ores = {}
|
||||||
|
|
||||||
|
--- Retrieves list of ores to be removed.
|
||||||
|
--
|
||||||
|
-- @treturn table Ores to be removed.
|
||||||
|
function cleaner.get_remove_ores()
|
||||||
|
return remove_ores
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Registers an ore to be removed after server startup.
|
||||||
|
--
|
||||||
|
-- @tparam string src Ore technical name.
|
||||||
|
function cleaner.register_ore_removal(src)
|
||||||
|
table.insert(remove_ores, src)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Removes an ore definition.
|
||||||
|
--
|
||||||
|
-- @tparam string src Ore technical name.
|
||||||
|
function cleaner.remove_ore(src)
|
||||||
|
local remove_ids = {}
|
||||||
|
local total_removed = 0
|
||||||
|
local registered = false
|
||||||
|
|
||||||
|
for id, def in pairs(core.registered_ores) do
|
||||||
|
if def.ore == src then
|
||||||
|
table.insert(remove_ids, id)
|
||||||
|
registered = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, id in ipairs(remove_ids) do
|
||||||
|
core.registered_ores[id] = nil
|
||||||
|
if core.registered_ores[id] then
|
||||||
|
cleaner.log("error", "unable to unregister ore " .. id)
|
||||||
|
else
|
||||||
|
total_removed = total_removed + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return registered, total_removed
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,4 +1,40 @@
|
||||||
|
|
||||||
|
2025-01-18
|
||||||
|
----------
|
||||||
|
- fix undeclared global
|
||||||
|
- added nil check after reading world data file
|
||||||
|
|
||||||
|
|
||||||
|
v1.2.1
|
||||||
|
----
|
||||||
|
- use sounds mod for sounds
|
||||||
|
- added nil check after reading world data file
|
||||||
|
|
||||||
|
|
||||||
|
v1.2
|
||||||
|
----
|
||||||
|
- added API
|
||||||
|
- added support for unregistering ores (unsafe)
|
||||||
|
- added setting for enabling "unsafe" methods & commands
|
||||||
|
- all types are loaded from <world_path>/cleaner.json file
|
||||||
|
- added localization support
|
||||||
|
- added Spanish localization
|
||||||
|
- added pencil tool for erasing, adding, & swapping nodes
|
||||||
|
- added chat commands:
|
||||||
|
- remove_entities
|
||||||
|
- remove_nodes
|
||||||
|
- replace_items
|
||||||
|
- replace_nodes
|
||||||
|
- find_unknown_nodes
|
||||||
|
- find_nearby_nodes
|
||||||
|
- remove_ores (unsafe)
|
||||||
|
- ctool (manages wielded cleaner tool settings)
|
||||||
|
|
||||||
|
v1.1
|
||||||
|
----
|
||||||
|
- uses "register_lbm" with "run_at_every_load" instead of "register_abm" to save resources
|
||||||
|
- suggested by bell07 ( https://forum.luanti.org/viewtopic.php?p=325519#p325519 )
|
||||||
|
|
||||||
v1.0
|
v1.0
|
||||||
----
|
----
|
||||||
- changed license to MIT
|
- changed license to MIT
|
||||||
|
|
660
chat.lua
Normal file
660
chat.lua
Normal file
|
@ -0,0 +1,660 @@
|
||||||
|
|
||||||
|
--- Cleaner Chat Commands
|
||||||
|
--
|
||||||
|
-- @topic commands
|
||||||
|
|
||||||
|
|
||||||
|
local S = core.get_translator(cleaner.modname)
|
||||||
|
|
||||||
|
|
||||||
|
local aux = dofile(cleaner.modpath .. "/misc_functions.lua")
|
||||||
|
|
||||||
|
local function pos_list(ppos, radius)
|
||||||
|
local plist = {}
|
||||||
|
|
||||||
|
for x = ppos.x - radius, ppos.x + radius, 1 do
|
||||||
|
for y = ppos.y - radius, ppos.y + radius, 1 do
|
||||||
|
for z = ppos.z - radius, ppos.z + radius, 1 do
|
||||||
|
table.insert(plist, {x=x, y=y, z=z})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return plist
|
||||||
|
end
|
||||||
|
|
||||||
|
local param_def = {
|
||||||
|
radius = {name=S("radius"), desc=S("Search radius.")},
|
||||||
|
entity = {name=S("entity"), desc=S("Entity technical name.")},
|
||||||
|
node = {name=S("node"), desc=S("Node technical name.")},
|
||||||
|
old_node = {name=S("old_node"), desc=S("Technical name of node to be replaced.")},
|
||||||
|
new_node = {name=S("new_node"), desc=S("Technical name of node to be used in place.")},
|
||||||
|
old_item = {name=S("old_item"), desc=S("Technical name of item to be replaced.")},
|
||||||
|
new_item = {name=S("new_item"), desc=S("Technical name of item to be used in place.")},
|
||||||
|
ore = {name=S("ore"), desc=S("Ore technical name.")},
|
||||||
|
action = {name=S("action"),
|
||||||
|
desc=S('Action to execute. Can be one of "@1", "@2", or "@3".', "status", "setmode", "setnode")},
|
||||||
|
value = {name=S("value"), desc=S('Mode or node to be set for tool (not required for "@1" action).', "status")},
|
||||||
|
}
|
||||||
|
|
||||||
|
local cmd_repo = {
|
||||||
|
entity = {
|
||||||
|
cmd = "remove_entities",
|
||||||
|
params = {"entity"},
|
||||||
|
oparams = {radius=100},
|
||||||
|
},
|
||||||
|
rem_node = {
|
||||||
|
cmd = "remove_nodes",
|
||||||
|
params = {"node"},
|
||||||
|
oparams = {radius=5},
|
||||||
|
},
|
||||||
|
rep_node = {
|
||||||
|
cmd = "replace_nodes",
|
||||||
|
params = {"old_node", "new_node"},
|
||||||
|
oparams = {radius=5},
|
||||||
|
},
|
||||||
|
find_node = {
|
||||||
|
cmd = "find_unknown_nodes",
|
||||||
|
oparams = {radius=100},
|
||||||
|
},
|
||||||
|
near_node = {
|
||||||
|
cmd = "find_nearby_nodes",
|
||||||
|
oparams = {radius=5},
|
||||||
|
},
|
||||||
|
item = {
|
||||||
|
cmd = "replace_items",
|
||||||
|
params = {"old_item", "new_item"},
|
||||||
|
},
|
||||||
|
ore = {
|
||||||
|
cmd = "remove_ores",
|
||||||
|
params = {"ore"},
|
||||||
|
},
|
||||||
|
tool = {
|
||||||
|
cmd = "ctool",
|
||||||
|
params = {"action", "value"},
|
||||||
|
},
|
||||||
|
param = {
|
||||||
|
missing = S("Missing parameter."),
|
||||||
|
excess = S("Too many parameters."),
|
||||||
|
mal_radius = S("Radius must be a number."),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, def in pairs(cmd_repo) do
|
||||||
|
if k ~= "param" then
|
||||||
|
local cmd_help = {
|
||||||
|
param_string = "",
|
||||||
|
usage_string = "/" .. def.cmd,
|
||||||
|
}
|
||||||
|
|
||||||
|
if def.params or def.oparams then
|
||||||
|
if def.params then
|
||||||
|
local params = {}
|
||||||
|
for _, p in ipairs(def.params) do
|
||||||
|
-- translate
|
||||||
|
table.insert(params, S(p))
|
||||||
|
end
|
||||||
|
|
||||||
|
cmd_help.param_string = "<" .. table.concat(params, "> <") .. ">"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if def.oparams then
|
||||||
|
for k, v in pairs(def.oparams) do
|
||||||
|
local op = k
|
||||||
|
if type(op) == "number" then
|
||||||
|
op = v
|
||||||
|
end
|
||||||
|
|
||||||
|
cmd_help.param_string = cmd_help.param_string .. " [" .. S(op) .. "]"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if cmd_help.param_string ~= "" then
|
||||||
|
cmd_help.usage_string = cmd_help.usage_string .. " " .. cmd_help.param_string
|
||||||
|
end
|
||||||
|
|
||||||
|
cmd_repo[k].help = cmd_help
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_cmd_def(cmd)
|
||||||
|
for k, v in pairs(cmd_repo) do
|
||||||
|
if v.cmd == cmd then return v end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function format_usage(cmd)
|
||||||
|
local def = get_cmd_def(cmd)
|
||||||
|
if def then
|
||||||
|
return S("Usage:") .. "\n " .. def.help.usage_string
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function format_params(cmd)
|
||||||
|
local def = get_cmd_def(cmd)
|
||||||
|
|
||||||
|
local param_count
|
||||||
|
-- FIXME: unused?
|
||||||
|
local all_params = {}
|
||||||
|
if def.params then
|
||||||
|
for _, p in ipairs(def.params) do
|
||||||
|
table.insert(all_params, p)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if def.oparams then
|
||||||
|
for k, v in pairs(def.oparams) do
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local retval = ""
|
||||||
|
local p_count = 0
|
||||||
|
|
||||||
|
if def.params then
|
||||||
|
for _, p in ipairs(def.params) do
|
||||||
|
if p_count == 0 then
|
||||||
|
retval = retval .. S("Params:")
|
||||||
|
end
|
||||||
|
|
||||||
|
retval = retval .. "\n " .. S(p) .. ": " .. param_def[p].desc
|
||||||
|
|
||||||
|
p_count = p_count + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if def.oparams then
|
||||||
|
for k, v in pairs(def.oparams) do
|
||||||
|
if p_count == 0 then
|
||||||
|
retval = retval .. S("Params:")
|
||||||
|
end
|
||||||
|
|
||||||
|
local p = k
|
||||||
|
local dvalue = v
|
||||||
|
if type(p) == "number" then
|
||||||
|
p = v
|
||||||
|
dvalue = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
retval = retval .. "\n " .. S(p) .. ": " .. param_def[p].desc
|
||||||
|
if dvalue then
|
||||||
|
retval = retval .. " (" .. S("default: @1", dvalue) .. ")"
|
||||||
|
end
|
||||||
|
|
||||||
|
p_count = p_count + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return retval
|
||||||
|
end
|
||||||
|
|
||||||
|
local function format_help(cmd)
|
||||||
|
return format_usage(cmd) .. "\n\n" .. format_params(cmd)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function check_radius(radius, pname)
|
||||||
|
local is_admin = core.check_player_privs(pname, {server=true})
|
||||||
|
|
||||||
|
if not is_admin and radius > 10 then
|
||||||
|
radius = 10
|
||||||
|
return radius, S("You do not have permission to set radius that high. Reduced to @1.", radius)
|
||||||
|
end
|
||||||
|
|
||||||
|
if radius > 100 then
|
||||||
|
radius = 100
|
||||||
|
return radius, S("Radius is too high. Reduced to @1.", radius)
|
||||||
|
end
|
||||||
|
|
||||||
|
return radius
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Removes nearby entities.
|
||||||
|
--
|
||||||
|
-- @chatcmd remove_entities
|
||||||
|
-- @param entity Entity technical name.
|
||||||
|
-- @tparam[opt] int radius Search radius (default: 100).
|
||||||
|
-- @priv server
|
||||||
|
-- @usage
|
||||||
|
-- # remove all mobs:horse entities within a radius of 10 nodes
|
||||||
|
-- /remove_entities mobs:horse 10
|
||||||
|
core.register_chatcommand(cmd_repo.entity.cmd, {
|
||||||
|
privs = {server=true},
|
||||||
|
description = S("Remove an entity from game.") .. "\n\n"
|
||||||
|
.. format_params(cmd_repo.entity.cmd),
|
||||||
|
params = cmd_repo.entity.help.param_string,
|
||||||
|
func = function(name, param)
|
||||||
|
local entity
|
||||||
|
local radius = cmd_repo.entity.oparams.radius
|
||||||
|
if param:find(" ") then
|
||||||
|
entity = param:split(" ")
|
||||||
|
radius = tonumber(entity[2])
|
||||||
|
entity = entity[1]
|
||||||
|
else
|
||||||
|
entity = param
|
||||||
|
end
|
||||||
|
|
||||||
|
local err
|
||||||
|
if not entity or entity:trim() == "" then
|
||||||
|
err = cmd_repo.param.missing
|
||||||
|
elseif not radius then
|
||||||
|
err = cmd_repo.param.mal_radius
|
||||||
|
end
|
||||||
|
|
||||||
|
local radius, msg = check_radius(radius, name)
|
||||||
|
if msg then
|
||||||
|
core.chat_send_player(name, msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
if err then
|
||||||
|
return false, err .. "\n\n" .. format_help(cmd_repo.entity.cmd)
|
||||||
|
end
|
||||||
|
|
||||||
|
local player = core.get_player_by_name(name)
|
||||||
|
|
||||||
|
local total_removed = 0
|
||||||
|
for _, object in ipairs(core.get_objects_inside_radius(player:get_pos(), radius)) do
|
||||||
|
local lent = object:get_luaentity()
|
||||||
|
|
||||||
|
if lent then
|
||||||
|
if lent.name == entity then
|
||||||
|
object:remove()
|
||||||
|
total_removed = total_removed + 1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if object:get_properties().infotext == entity then
|
||||||
|
object:remove()
|
||||||
|
total_removed = total_removed + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true, S("Removed @1 entities.", total_removed)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
--- Removes nearby nodes.
|
||||||
|
--
|
||||||
|
-- @chatcmd remove_nodes
|
||||||
|
-- @param node Node technical name.
|
||||||
|
-- @tparam[opt] int radius Search radius (default: 5).
|
||||||
|
-- @priv server
|
||||||
|
-- @usage
|
||||||
|
-- # remove all default:dirt nodes within a radius of 10
|
||||||
|
-- /remove_nodes default:dirt 10
|
||||||
|
core.register_chatcommand(cmd_repo.rem_node.cmd, {
|
||||||
|
privs = {server=true},
|
||||||
|
description = S("Remove a node from game.") .. "\n\n"
|
||||||
|
.. format_params(cmd_repo.rem_node.cmd),
|
||||||
|
params = cmd_repo.rem_node.help.param_string,
|
||||||
|
func = function(name, param)
|
||||||
|
local nname
|
||||||
|
local radius = cmd_repo.rem_node.oparams.radius
|
||||||
|
if param:find(" ") then
|
||||||
|
nname = param:split(" ")
|
||||||
|
radius = tonumber(nname[2])
|
||||||
|
nname = nname[1]
|
||||||
|
else
|
||||||
|
nname = param
|
||||||
|
end
|
||||||
|
|
||||||
|
local err
|
||||||
|
if not nname or nname:trim() == "" then
|
||||||
|
err = cmd_repo.param.missing
|
||||||
|
elseif not radius then
|
||||||
|
err = cmd_repo.param.mal_radius
|
||||||
|
end
|
||||||
|
|
||||||
|
local radius, msg = check_radius(radius, name)
|
||||||
|
if msg then
|
||||||
|
core.chat_send_player(name, msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
if err then
|
||||||
|
return false, err .. "\n\n" .. format_help(cmd_repo.rem_node.cmd)
|
||||||
|
end
|
||||||
|
|
||||||
|
local ppos = core.get_player_by_name(name):get_pos()
|
||||||
|
|
||||||
|
local total_removed = 0
|
||||||
|
for _, npos in ipairs(pos_list(ppos, radius)) do
|
||||||
|
local node = core.get_node_or_nil(npos)
|
||||||
|
if node and node.name == nname then
|
||||||
|
core.remove_node(npos)
|
||||||
|
total_removed = total_removed + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true, S("Removed @1 nodes.", total_removed)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
--- Replaces an item.
|
||||||
|
--
|
||||||
|
-- @chatcmd replace_items
|
||||||
|
-- @param old_item Technical name of item to replace.
|
||||||
|
-- @param new_item Technical name of item to be used in place.
|
||||||
|
-- @priv server
|
||||||
|
-- @usage
|
||||||
|
-- # replace default:sword_wood with default:sword_mese
|
||||||
|
-- /replace_items default:sword_wood default:sword_mese
|
||||||
|
core.register_chatcommand(cmd_repo.item.cmd, {
|
||||||
|
privs = {server=true},
|
||||||
|
description = S("Replace an item in game.") .. "\n\n"
|
||||||
|
.. format_params(cmd_repo.item.cmd),
|
||||||
|
params = cmd_repo.item.help.param_string,
|
||||||
|
func = function(name, param)
|
||||||
|
if not param:find(" ") then
|
||||||
|
return false, cmd_repo.param.missing .. "\n\n" .. format_help(cmd_repo.item.cmd)
|
||||||
|
end
|
||||||
|
|
||||||
|
local src = param:split(" ")
|
||||||
|
local tgt = src[2]
|
||||||
|
src = src[1]
|
||||||
|
|
||||||
|
local retval, msg = cleaner.replace_item(src, tgt, true)
|
||||||
|
if not retval then
|
||||||
|
return false, msg
|
||||||
|
end
|
||||||
|
|
||||||
|
return true, S("Success!")
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
--- Replaces nearby nodes.
|
||||||
|
--
|
||||||
|
-- @chatcmd replace_nodes
|
||||||
|
-- @param old_node Technical name of node to replace.
|
||||||
|
-- @param new_node Technical name of node to be used in place.
|
||||||
|
-- @tparam[opt] int radius Search radius (default: 5).
|
||||||
|
-- @priv server
|
||||||
|
-- @usage
|
||||||
|
-- # replace all default:dirt nodes with default:cobble within a radius of 10
|
||||||
|
-- /replace_nodes default:dirt default:cobble 10
|
||||||
|
core.register_chatcommand(cmd_repo.rep_node.cmd, {
|
||||||
|
privs = {server=true},
|
||||||
|
description = S("Replace a node in game.") .. "\n\n"
|
||||||
|
.. format_params(cmd_repo.rep_node.cmd),
|
||||||
|
params = cmd_repo.rep_node.help.param_string,
|
||||||
|
func = function(name, param)
|
||||||
|
local help = format_help(cmd_repo.rep_node.cmd)
|
||||||
|
|
||||||
|
if not param:find(" ") then
|
||||||
|
return false, cmd_repo.param.missing .. "\n\n" .. help
|
||||||
|
end
|
||||||
|
|
||||||
|
local radius = cmd_repo.rep_node.oparams.radius
|
||||||
|
local params = param:split(" ")
|
||||||
|
|
||||||
|
local src = params[1]
|
||||||
|
local tgt = tostring(params[2])
|
||||||
|
if #params > 2 then
|
||||||
|
radius = tonumber(params[3])
|
||||||
|
end
|
||||||
|
|
||||||
|
if not radius then
|
||||||
|
return false, cmd_repo.param.mal_radius .. "\n\n" .. help
|
||||||
|
end
|
||||||
|
|
||||||
|
local radius, msg = check_radius(radius, name)
|
||||||
|
if msg then
|
||||||
|
core.chat_send_player(name, msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not core.registered_nodes[tgt] then
|
||||||
|
return false, S('Cannot use unknown node "@1" as replacement.', tgt)
|
||||||
|
end
|
||||||
|
|
||||||
|
local total_replaced = 0
|
||||||
|
local ppos = core.get_player_by_name(name):get_pos()
|
||||||
|
for _, npos in ipairs(pos_list(ppos, radius)) do
|
||||||
|
local node = core.get_node_or_nil(npos)
|
||||||
|
if node and node.name == src then
|
||||||
|
if core.swap_node(npos, {name=tgt}) then
|
||||||
|
total_replaced = total_replaced + 1
|
||||||
|
else
|
||||||
|
cleaner.log("error", "could not replace node at " .. core.pos_to_string(npos, 0))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true, S("Replaced @1 nodes.", total_replaced)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
--- Checks for nearby unknown nodes.
|
||||||
|
--
|
||||||
|
-- @chatcmd find_unknown_nodes
|
||||||
|
-- @tparam[opt] int radius Search radius (default: 100).
|
||||||
|
-- @priv server
|
||||||
|
-- @usage
|
||||||
|
-- # print names of all unknown nodes within radius of 10
|
||||||
|
-- /find_unknown_nodes 10
|
||||||
|
core.register_chatcommand(cmd_repo.find_node.cmd, {
|
||||||
|
privs = {server=true},
|
||||||
|
description = S("Find names of unknown nodes.") .. "\n\n"
|
||||||
|
.. format_params(cmd_repo.find_node.cmd),
|
||||||
|
params = cmd_repo.find_node.help.param_string,
|
||||||
|
func = function(name, param)
|
||||||
|
local help = format_help(cmd_repo.find_node.cmd)
|
||||||
|
|
||||||
|
if param:find(" ") then
|
||||||
|
return false, cmd_repo.param.excess .. "\n\n" .. help
|
||||||
|
end
|
||||||
|
|
||||||
|
local radius = cmd_repo.find_node.oparams.radius
|
||||||
|
if param and param:trim() ~= "" then
|
||||||
|
radius = tonumber(param)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not radius then
|
||||||
|
return false, cmd_repo.param.mal_radius .. "\n\n" .. help
|
||||||
|
end
|
||||||
|
|
||||||
|
local radius, msg = check_radius(radius, name)
|
||||||
|
if msg then
|
||||||
|
core.chat_send_player(name, msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
local ppos = core.get_player_by_name(name):get_pos()
|
||||||
|
|
||||||
|
local checked_nodes = {}
|
||||||
|
local unknown_nodes = {}
|
||||||
|
for _, npos in ipairs(pos_list(ppos, radius)) do
|
||||||
|
local node = core.get_node_or_nil(npos)
|
||||||
|
if node and not checked_nodes[node.name] then
|
||||||
|
if not core.registered_nodes[node.name] then
|
||||||
|
table.insert(unknown_nodes, node.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
checked_nodes[node.name] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local node_count = #unknown_nodes
|
||||||
|
if node_count > 0 then
|
||||||
|
msg = S("Found unknown nodes: @1", node_count) .. "\n " .. table.concat(unknown_nodes, ", ")
|
||||||
|
else
|
||||||
|
msg = S("No unknown nodes found.")
|
||||||
|
end
|
||||||
|
|
||||||
|
return true, msg
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
--- Finds names of nearby nodes.
|
||||||
|
--
|
||||||
|
-- @chatcmd find_nearby_nodes
|
||||||
|
-- @tparam[opt] int radius Search radius (default: 5).
|
||||||
|
-- @priv server
|
||||||
|
-- @usage
|
||||||
|
-- # print names of all node types found within radius of 10
|
||||||
|
-- /find_nearby_nodes 10
|
||||||
|
core.register_chatcommand(cmd_repo.near_node.cmd, {
|
||||||
|
privs = {server=true},
|
||||||
|
description = S("Find names of nearby nodes.") .. "\n\n"
|
||||||
|
.. format_params(cmd_repo.near_node.cmd),
|
||||||
|
params = cmd_repo.near_node.help.param_string,
|
||||||
|
func = function(name, param)
|
||||||
|
local help = format_help(cmd_repo.near_node.cmd)
|
||||||
|
|
||||||
|
if param:find(" ") then
|
||||||
|
return false, cmd_repo.param.excess .. "\n\n" .. help
|
||||||
|
end
|
||||||
|
|
||||||
|
local radius = cmd_repo.near_node.oparams.radius
|
||||||
|
if param and param:trim() ~= "" then
|
||||||
|
radius = tonumber(param)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not radius then
|
||||||
|
return false, cmd_repo.param.mal_radius .. "\n\n" .. help
|
||||||
|
end
|
||||||
|
|
||||||
|
local radius, msg = check_radius(radius, name)
|
||||||
|
if msg then
|
||||||
|
core.chat_send_player(name, msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
local ppos = core.get_player_by_name(name):get_pos()
|
||||||
|
|
||||||
|
local node_names = {}
|
||||||
|
for _, npos in ipairs(pos_list(ppos, radius)) do
|
||||||
|
local node = core.get_node_or_nil(npos)
|
||||||
|
if node and not node_names[node.name] then
|
||||||
|
node_names[node.name] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local found_nodes = {}
|
||||||
|
for k, _ in pairs(node_names) do
|
||||||
|
table.insert(found_nodes, k)
|
||||||
|
end
|
||||||
|
|
||||||
|
local msg
|
||||||
|
local node_count = #found_nodes
|
||||||
|
if node_count > 0 then
|
||||||
|
msg = S("Nearby nodes: @1", node_count) .. "\n " .. table.concat(found_nodes, ", ")
|
||||||
|
else
|
||||||
|
msg = S("No nearby nodes found.")
|
||||||
|
end
|
||||||
|
|
||||||
|
return true, msg
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
--- Unsafe Commands.
|
||||||
|
--
|
||||||
|
-- Enabled with [cleaner.unsafe](settings.html#cleaner.unsafe) setting.
|
||||||
|
--
|
||||||
|
-- @section unsafe
|
||||||
|
|
||||||
|
|
||||||
|
if cleaner.unsafe then
|
||||||
|
--- Registers an ore to be removed.
|
||||||
|
--
|
||||||
|
-- @chatcmd remove_ores
|
||||||
|
-- @param ore Ore technical name.
|
||||||
|
-- @priv server
|
||||||
|
-- @note This action is reverted after server restart. To make changes permanent,
|
||||||
|
-- use the [cleaner.json](config.html#cleaner.json) config.
|
||||||
|
-- @usage
|
||||||
|
-- # remove all registered ores that add default:stone_with_iron to world
|
||||||
|
-- /remove_ores default:stone_with_iron
|
||||||
|
core.register_chatcommand(cmd_repo.ore.cmd, {
|
||||||
|
privs = {server=true},
|
||||||
|
description = S("Remove an ore from game.") .. "\n\n"
|
||||||
|
.. format_params(cmd_repo.ore.cmd),
|
||||||
|
params = cmd_repo.ore.help.param_string,
|
||||||
|
func = function(name, param)
|
||||||
|
local err
|
||||||
|
if not param or param:trim() == "" then
|
||||||
|
err = cmd_repo.param.missing
|
||||||
|
elseif param:find(" ") then
|
||||||
|
err = cmd_repo.param.excess
|
||||||
|
end
|
||||||
|
|
||||||
|
if err then
|
||||||
|
return false, err .. "\n\n" .. format_help(cmd_repo.ore.cmd)
|
||||||
|
end
|
||||||
|
|
||||||
|
local success = false
|
||||||
|
local msg
|
||||||
|
local registered, total_removed = cleaner.remove_ore(param)
|
||||||
|
|
||||||
|
if not registered then
|
||||||
|
msg = S('Ore "@1" not found, not unregistering.', param)
|
||||||
|
else
|
||||||
|
msg = S("Unregistered @1 ores (this will be undone after server restart).", total_removed)
|
||||||
|
success = true
|
||||||
|
end
|
||||||
|
|
||||||
|
return success, msg
|
||||||
|
end
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @section end
|
||||||
|
|
||||||
|
|
||||||
|
--- Manages settings for wielded [cleaner tool](tools.html).
|
||||||
|
--
|
||||||
|
-- <h3>Required Privileges:</h3>
|
||||||
|
--
|
||||||
|
-- - server
|
||||||
|
--
|
||||||
|
-- @chatcmd ctool
|
||||||
|
-- @param action Action to execute. Can be "status", "setmode", or "setnode".
|
||||||
|
-- @param value Mode or node to be set for tool (not required for "status" action).
|
||||||
|
-- @usage
|
||||||
|
-- # while cleaner:pencil is wielded, configure to place default:dirt node when used
|
||||||
|
-- /ctool setmode write
|
||||||
|
-- /ctool setnode default:dirt
|
||||||
|
core.register_chatcommand(cmd_repo.tool.cmd, {
|
||||||
|
privs = {server=true},
|
||||||
|
description = S("Manage settings for wielded cleaner tool.") .. "\n\n"
|
||||||
|
.. format_params(cmd_repo.tool.cmd),
|
||||||
|
params = cmd_repo.tool.help.param_string,
|
||||||
|
func = function(name, param)
|
||||||
|
local action, value = param
|
||||||
|
local idx = param:find(" ")
|
||||||
|
if idx then
|
||||||
|
param = string.split(param, " ")
|
||||||
|
action = param[1]
|
||||||
|
value = param[2]
|
||||||
|
end
|
||||||
|
|
||||||
|
local help = format_help(cmd_repo.tool.cmd)
|
||||||
|
|
||||||
|
local player = core.get_player_by_name(name)
|
||||||
|
local stack = player:get_wielded_item()
|
||||||
|
local iname = aux.tool:format_name(stack)
|
||||||
|
local imeta = stack:get_meta()
|
||||||
|
|
||||||
|
if iname ~= "cleaner:pencil" then
|
||||||
|
return false, S("Unrecognized wielded item: @1", iname) .. "\n\n" .. help
|
||||||
|
end
|
||||||
|
|
||||||
|
if action == "status" then
|
||||||
|
core.chat_send_player(name, iname .. ": " .. S("mode") .. "=" .. imeta:get_string("mode")
|
||||||
|
.. ", " .. S("node") .. "=" .. imeta:get_string("node"))
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if not action or not value then
|
||||||
|
return false, S("Missing parameter.") .. "\n\n" .. help
|
||||||
|
end
|
||||||
|
|
||||||
|
if action == "setmode" then
|
||||||
|
stack = aux.tool:set_mode(stack, value, name)
|
||||||
|
elseif action == "setnode" then
|
||||||
|
stack = aux.tool:set_node(stack, value, name)
|
||||||
|
else
|
||||||
|
return false, S("Unrecognized action: @1", action) .. "\n\n" .. help
|
||||||
|
end
|
||||||
|
|
||||||
|
return player:set_wielded_item(stack)
|
||||||
|
end,
|
||||||
|
})
|
50
entities.lua
50
entities.lua
|
@ -1,20 +1,30 @@
|
||||||
|
|
||||||
local misc = dofile(cleaner.modpath .. "/misc_functions.lua")
|
local aux = dofile(cleaner.modpath .. "/misc_functions.lua")
|
||||||
|
|
||||||
-- populate entities list from file in world path
|
-- populate entities list from file in world path
|
||||||
local e_list = {remove={}}
|
local entities_data = aux.get_world_data().entities
|
||||||
|
|
||||||
|
|
||||||
|
-- START: backward compat
|
||||||
|
|
||||||
local e_path = core.get_worldpath() .. "/clean_entities.json"
|
local e_path = core.get_worldpath() .. "/clean_entities.json"
|
||||||
local e_file = io.open(e_path, "r")
|
local e_file = io.open(e_path, "r")
|
||||||
|
|
||||||
if e_file then
|
if e_file then
|
||||||
|
cleaner.log("action", "found deprecated clean_entities.json, updating")
|
||||||
|
|
||||||
local data_in = core.parse_json(e_file:read("*a"))
|
local data_in = core.parse_json(e_file:read("*a"))
|
||||||
e_file:close()
|
e_file:close()
|
||||||
if data_in then
|
if data_in and data_in.remove then
|
||||||
e_list = data_in
|
for _, r in ipairs(data_in.remove) do
|
||||||
|
table.insert(entities_data.remove, r)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- don't read deprecated file again
|
||||||
|
os.rename(e_path, e_path .. ".old")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- backward compat
|
|
||||||
local e_path_old = core.get_worldpath() .. "/clean_entities.txt"
|
local e_path_old = core.get_worldpath() .. "/clean_entities.txt"
|
||||||
e_file = io.open(e_path_old, "r")
|
e_file = io.open(e_path_old, "r")
|
||||||
|
|
||||||
|
@ -25,7 +35,7 @@ if e_file then
|
||||||
for _, e in ipairs(data_in) do
|
for _, e in ipairs(data_in) do
|
||||||
e = e:trim()
|
e = e:trim()
|
||||||
if e ~= "" and e:sub(1, 1) ~= "#" then
|
if e ~= "" and e:sub(1, 1) ~= "#" then
|
||||||
table.insert(e_list.remove, e)
|
table.insert(entities_data.remove, e)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -33,23 +43,17 @@ if e_file then
|
||||||
os.rename(e_path_old, e_path_old .. ".bak") -- don't read deprecated file again
|
os.rename(e_path_old, e_path_old .. ".bak") -- don't read deprecated file again
|
||||||
end
|
end
|
||||||
|
|
||||||
e_list.remove = misc.clean_duplicates(e_list.remove)
|
-- END: backward compat
|
||||||
|
|
||||||
|
|
||||||
|
entities_data.remove = aux.clean_duplicates(entities_data.remove)
|
||||||
|
|
||||||
-- update json file with any changes
|
-- update json file with any changes
|
||||||
e_file = io.open(e_path, "w")
|
aux.update_world_data("entities", entities_data)
|
||||||
if e_file then
|
|
||||||
local data_out = core.write_json(e_list, true):gsub("\"remove\" : null", "\"remove\" : []")
|
|
||||||
e_file:write(data_out)
|
|
||||||
e_file:close()
|
|
||||||
end
|
|
||||||
|
|
||||||
|
core.register_on_mods_loaded(function()
|
||||||
for _, e in ipairs(e_list.remove) do
|
for _, e in ipairs(entities_data.remove) do
|
||||||
cleaner.log("debug", "Cleaning entity: " .. e)
|
cleaner.log("action", "registering entity for removal: " .. e)
|
||||||
|
cleaner.register_entity_removal(e)
|
||||||
core.register_entity(":" .. e, {
|
end
|
||||||
on_activate = function(self, staticdata)
|
end)
|
||||||
self.object:remove()
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
14
init.lua
14
init.lua
|
@ -1,7 +1,3 @@
|
||||||
--[[ Cleaner mod
|
|
||||||
License: MIT
|
|
||||||
]]
|
|
||||||
|
|
||||||
|
|
||||||
cleaner = {}
|
cleaner = {}
|
||||||
cleaner.modname = core.get_current_modname()
|
cleaner.modname = core.get_current_modname()
|
||||||
|
@ -30,11 +26,21 @@ function cleaner.log(lvl, msg)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local aux = dofile(cleaner.modpath .. "/misc_functions.lua")
|
||||||
|
|
||||||
|
-- initialize world file
|
||||||
|
aux.update_world_data()
|
||||||
|
|
||||||
|
|
||||||
local scripts = {
|
local scripts = {
|
||||||
|
"settings",
|
||||||
|
"api",
|
||||||
|
"chat",
|
||||||
|
"tools",
|
||||||
"entities",
|
"entities",
|
||||||
"nodes",
|
"nodes",
|
||||||
"items",
|
"items",
|
||||||
|
"ores",
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, script in ipairs(scripts) do
|
for _, script in ipairs(scripts) do
|
||||||
|
|
64
items.lua
64
items.lua
|
@ -1,55 +1,49 @@
|
||||||
|
|
||||||
local misc = dofile(cleaner.modpath .. "/misc_functions.lua")
|
local aux = dofile(cleaner.modpath .. "/misc_functions.lua")
|
||||||
|
|
||||||
|
-- populate items list from file in world path
|
||||||
|
local items_data = aux.get_world_data().items
|
||||||
|
|
||||||
|
|
||||||
|
-- START: backward compat
|
||||||
|
|
||||||
-- populate nodes list from file in world path
|
|
||||||
local i_list = {replace={}}
|
|
||||||
local i_path = core.get_worldpath() .. "/clean_items.json"
|
local i_path = core.get_worldpath() .. "/clean_items.json"
|
||||||
local i_file = io.open(i_path, "r")
|
local i_file = io.open(i_path, "r")
|
||||||
|
|
||||||
if i_file then
|
if i_file then
|
||||||
|
cleaner.log("action", "found deprecated clean_items.json, updating")
|
||||||
|
|
||||||
local data_in = core.parse_json(i_file:read("*a"))
|
local data_in = core.parse_json(i_file:read("*a"))
|
||||||
i_file:close()
|
i_file:close()
|
||||||
if data_in then
|
if data_in and data_in.replace then
|
||||||
i_list = data_in
|
for k, v in pairs(data_in.replace) do
|
||||||
|
if not items_data.replace[k] then
|
||||||
|
items_data.replace[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- don't read deprecated file again
|
||||||
|
os.rename(i_path, i_path .. ".old")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- update json file with any changes
|
-- END: backward compat
|
||||||
i_file = io.open(i_path, "w")
|
|
||||||
if i_file then
|
|
||||||
local data_out = core.write_json(i_list, true)
|
|
||||||
|
|
||||||
data_out = data_out:gsub("\"replace\" : null", "\"replace\" : {}")
|
|
||||||
|
|
||||||
i_file:write(data_out)
|
aux.update_world_data("items", items_data)
|
||||||
i_file:close()
|
|
||||||
|
for i_old, i_new in pairs(items_data.replace) do
|
||||||
|
cleaner.register_item_replacement(i_old, i_new)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- register actions for after server startup
|
-- register actions for after server startup
|
||||||
core.after(0, function()
|
core.register_on_mods_loaded(function()
|
||||||
for i_old, i_new in pairs(i_list.replace) do
|
for i_old, i_new in pairs(cleaner.get_replace_items()) do
|
||||||
cleaner.log("action", "replacing item \"" .. i_old .. "\" with \"" .. i_new .. "\"")
|
cleaner.log("action", "registering item \"" .. i_old .. "\" to be replaced with \"" .. i_new .. "\"")
|
||||||
|
|
||||||
if not core.registered_items[i_old] then
|
local retval, msg = cleaner.replace_item(i_old, i_new)
|
||||||
cleaner.log("info", "\"" .. i_old .. "\" not registered, not unregistering")
|
if not retval then
|
||||||
else
|
cleaner.log("warning", msg)
|
||||||
cleaner.log("warning", "overriding registered item \"" .. i_old .. "\"")
|
|
||||||
|
|
||||||
core.unregister_item(i_old)
|
|
||||||
if core.registered_items[i_old] then
|
|
||||||
cleaner.log("error", "could not unregister \"" .. i_old .. "\"")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not core.registered_items[i_new] then
|
|
||||||
cleaner.log("warning", "adding alias for unregistered item \"" .. i_new .. "\"")
|
|
||||||
end
|
|
||||||
|
|
||||||
core.register_alias(i_old, i_new)
|
|
||||||
if core.registered_aliases[i_old] == i_new then
|
|
||||||
cleaner.log("info", "registered alias \"" .. i_old .. "\" for \"" .. i_new .. "\"")
|
|
||||||
else
|
|
||||||
cleaner.log("error", "could not register alias \"" .. i_old .. "\" for \"" .. i_new .. "\"")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
66
locale/cleaner.es.tr
Normal file
66
locale/cleaner.es.tr
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
# textdomain:cleaner
|
||||||
|
|
||||||
|
# Translators: Jordan Irwin (AntumDeluge)
|
||||||
|
|
||||||
|
|
||||||
|
# chat commands
|
||||||
|
entity=entidad
|
||||||
|
mode=modo
|
||||||
|
node=nodo
|
||||||
|
radius=radio
|
||||||
|
old_item=objeto_antiguo
|
||||||
|
new_item=objeto_nuevo
|
||||||
|
old_node=nodo_antiguo
|
||||||
|
new_node=nodo_nuevo
|
||||||
|
ore=mineral
|
||||||
|
action=acción
|
||||||
|
value=valor
|
||||||
|
Usage:=Uso:
|
||||||
|
Params:=Parámetros:
|
||||||
|
default: @1=por defecto: @1
|
||||||
|
Search radius.=Radio de búsqueda.
|
||||||
|
Entity technical name.=Nombre técnico de entidad.
|
||||||
|
Node technical name.=Nombre técnico de nodo.
|
||||||
|
Technical name of node to be replaced.=Nombre técnico del nodo reemplazado.
|
||||||
|
Technical name of node to be used in place.=Nombre técnico del nodo de reemplazo.
|
||||||
|
Technical name of item to be replaced.=Nombre técnico del objeto reemplazado.
|
||||||
|
Technical name of item to be used in place.=Nombre técnico del objeto de reemplazo.
|
||||||
|
Ore technical name.=Nombre técnico de mineral.
|
||||||
|
Action to execute. Can be one of "@1", "@2", or "@3".=La acción para ejecutar. Puede ser "@1", "@2", o "@3".
|
||||||
|
Mode or node to be set for tool (not required for "@1" action).=Modo o nodo para configurar a la herramienta (no se requiere para la acción de "@1").
|
||||||
|
Remove an entity from game.=Eliminar una entidad del juego.
|
||||||
|
Remove a node from game.=Eliminar un nodo del juego.
|
||||||
|
Replace an item in game.=Sustituir un objecto del juego.
|
||||||
|
Replace a node in game.=Sustituir un nodo del juego.
|
||||||
|
Find names of unknown nodes.=Descubrir los nombres de nodos desconocidos.
|
||||||
|
Find names of nearby nodes.=Descubrir los nombres de nodos cercanos.
|
||||||
|
Remove an ore from game.=Eliminar un mineral del juego.
|
||||||
|
Missing parameter.=Parámetro extraviado.
|
||||||
|
Too many parameters.=Demasiados parámetros.
|
||||||
|
Radius must be a number.=El radio debe ser un número.
|
||||||
|
Cannot use unknown item "@1" as replacement.=El objeto "@1" es desonocido, no se puede utilizar como sustitución.
|
||||||
|
Cannot use unknown node "@1" as replacement.=El nodo "@1" es desonocido, no se puede utilizar como sustitución.
|
||||||
|
Replaced @1 nodes.=Nodos sustituidos: @1
|
||||||
|
Removed @1 nodes.=Se eliminaron @1 nodos.
|
||||||
|
Removed @1 entities.=Se eliminaron @1 entidades.
|
||||||
|
Found unknown nodes: @1=Se encontraron @1 nodos desconocidos.
|
||||||
|
No unknown nodes found.=No se encontraron nodos desconocidos.
|
||||||
|
Nearby nodes: @1=Nodos cercanos: @1
|
||||||
|
No nearby nodes found.=No se encontraron nodos cercanos.
|
||||||
|
Ore "@1" not found, not unregistering.=No se encontró el mineral "@1", se mantiene registrado.
|
||||||
|
Unregistered @1 ores (this will be undone after server restart).=Se anuló @1 minerales del registro.
|
||||||
|
Success!=¡Éxito!
|
||||||
|
Manage settings for wielded cleaner tool.=Administrar a los ajustes de la herramienta cleaner empuñada.
|
||||||
|
Unrecognized wielded item: @1=Objeto empuñado desconocido: @1
|
||||||
|
Unrecognized action: @1=Acción desconocido: @1
|
||||||
|
You do not have permission to set radius that high. Reduced to @1.=No tienes permiso para poner al radio tan alto. Se reduce a @1.
|
||||||
|
Radius is too high. Reduced to @1.=El radio es demasiado alto. Se reduce a @1.
|
||||||
|
|
||||||
|
# tools:
|
||||||
|
@1: mode set to: @2=@1: modo configurado para: @2
|
||||||
|
@1: node set to: @2=@1: nodo configurado para: @2
|
||||||
|
Modes for tool "@1" not available.=Modos para herramienta "@1" no disponibles.
|
||||||
|
You do not have permission to use this item. Missing privs: @1=No tienes permiso para usar este objeto. Privs que faltan: @1
|
||||||
|
Unknown mode: @1=Modo desconocido: @1
|
||||||
|
Can't place node there.=No se puede poner nodo allí.
|
||||||
|
Cannot place unknown node: @1=No se puede poner nodo desconocido: @1
|
66
locale/template.txt
Normal file
66
locale/template.txt
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
# textdomain:cleaner
|
||||||
|
|
||||||
|
# Translators:
|
||||||
|
|
||||||
|
|
||||||
|
# chat commands
|
||||||
|
entity=
|
||||||
|
mode=
|
||||||
|
node=
|
||||||
|
radius=
|
||||||
|
old_item=
|
||||||
|
new_item=
|
||||||
|
old_node=
|
||||||
|
new_node=
|
||||||
|
ore=
|
||||||
|
action=
|
||||||
|
value=
|
||||||
|
Usage:=
|
||||||
|
Params:=
|
||||||
|
default: @1=
|
||||||
|
Search radius.=
|
||||||
|
Entity technical name.=
|
||||||
|
Node technical name.=
|
||||||
|
Technical name of node to be replaced.=
|
||||||
|
Technical name of node to be used in place.=
|
||||||
|
Technical name of item to be replaced.=
|
||||||
|
Technical name of item to be used in place.=
|
||||||
|
Ore technical name.=
|
||||||
|
Action to execute. Can be one of "@1", "@2", or "@3".=
|
||||||
|
Mode or node to be set for tool (not required for "@1" action).=
|
||||||
|
Remove an entity from game.=
|
||||||
|
Remove a node from game.=
|
||||||
|
Replace an item in game.=
|
||||||
|
Replace a node in game.=
|
||||||
|
Find names of unknown nodes.=
|
||||||
|
Find names of nearby nodes.=
|
||||||
|
Remove an ore from game.=
|
||||||
|
Missing parameter.=
|
||||||
|
Too many parameters.=
|
||||||
|
Radius must be a number.=
|
||||||
|
Cannot use unknown item "@1" as replacement.=
|
||||||
|
Cannot use unknown node "@1" as replacement.=
|
||||||
|
Replaced @1 nodes.=
|
||||||
|
Removed @1 nodes.=
|
||||||
|
Removed @1 entities.=
|
||||||
|
Found unknown nodes: @1=
|
||||||
|
No unknown nodes found.=
|
||||||
|
Nearby nodes: @1=
|
||||||
|
No nearby nodes found.=
|
||||||
|
Ore "@1" not found, not unregistering.=
|
||||||
|
Unregistered @1 ores (this will be undone after server restart).=
|
||||||
|
Success!=
|
||||||
|
Manage settings for wielded cleaner tool.=
|
||||||
|
Unrecognized wielded item: @1=
|
||||||
|
Unrecognized action: @1=
|
||||||
|
You do not have permission to set radius that high. Reduced to @1.=
|
||||||
|
Radius is too high. Reduced to @1.=
|
||||||
|
|
||||||
|
# tools:
|
||||||
|
@1: mode set to: @2=
|
||||||
|
Modes for tool "@1" not available.=
|
||||||
|
@1: node set to: @2=
|
||||||
|
You do not have permission to use this item. Missing privs: @1=
|
||||||
|
Can't place node there.=
|
||||||
|
Unknown mode: @1=
|
||||||
|
Cannot place unknown node: @1=
|
|
@ -1,4 +1,7 @@
|
||||||
|
|
||||||
|
local S = core.get_translator(cleaner.modname)
|
||||||
|
|
||||||
|
|
||||||
--- Cleans duplicate entries from indexed table.
|
--- Cleans duplicate entries from indexed table.
|
||||||
--
|
--
|
||||||
-- @local
|
-- @local
|
||||||
|
@ -19,7 +22,237 @@ local function clean_duplicates(t)
|
||||||
return t
|
return t
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local world_file = core.get_worldpath() .. "/cleaner.json"
|
||||||
|
|
||||||
|
local function get_world_data()
|
||||||
|
local wdata = {}
|
||||||
|
local buffer = io.open(world_file, "r")
|
||||||
|
if buffer then
|
||||||
|
local err
|
||||||
|
wdata, err = core.parse_json(buffer:read("*a"), nil, true)
|
||||||
|
buffer:close()
|
||||||
|
if wdata == nil then
|
||||||
|
cleaner.log("warning", "reading world data file failed: " .. world_file)
|
||||||
|
wdata = {}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local rem_types = {"entities", "nodes", "ores",}
|
||||||
|
local rep_types = {"items", "nodes",}
|
||||||
|
|
||||||
|
for _, t in ipairs(rem_types) do
|
||||||
|
wdata[t] = wdata[t] or {}
|
||||||
|
wdata[t].remove = wdata[t].remove or {}
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, t in ipairs(rep_types) do
|
||||||
|
wdata[t] = wdata[t] or {}
|
||||||
|
wdata[t].replace = wdata[t].replace or {}
|
||||||
|
end
|
||||||
|
|
||||||
|
return wdata
|
||||||
|
end
|
||||||
|
|
||||||
|
local function update_world_data(t, data)
|
||||||
|
local wdata = get_world_data()
|
||||||
|
if t and data then
|
||||||
|
wdata[t].remove = data.remove
|
||||||
|
wdata[t].replace = data.replace
|
||||||
|
end
|
||||||
|
|
||||||
|
local json_string = core.write_json(wdata, true):gsub("\"remove\" : null", "\"remove\" : []")
|
||||||
|
:gsub("\"replace\" : null", "\"replace\" : {}")
|
||||||
|
|
||||||
|
local buffer = io.open(world_file, "w")
|
||||||
|
if buffer then
|
||||||
|
buffer:write(json_string)
|
||||||
|
buffer:close()
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local tool = {
|
||||||
|
modes = {
|
||||||
|
["cleaner:pencil"] = {"erase", "write", "swap"},
|
||||||
|
},
|
||||||
|
|
||||||
|
format_name = function(self, stack)
|
||||||
|
local iname = stack:get_name()
|
||||||
|
if iname == "cleaner:pencil_1" then
|
||||||
|
iname = "cleaner:pencil"
|
||||||
|
end
|
||||||
|
|
||||||
|
return iname
|
||||||
|
end,
|
||||||
|
|
||||||
|
set_mode = function(self, stack, mode, pname)
|
||||||
|
local iname = self:format_name(stack)
|
||||||
|
|
||||||
|
if not self.modes[iname] then
|
||||||
|
if pname then
|
||||||
|
core.chat_send_player(pname, iname .. ": " .. S("unknown mode: @1", mode))
|
||||||
|
end
|
||||||
|
cleaner.log("warning", iname .. ": unknown mode: " .. mode)
|
||||||
|
return stack
|
||||||
|
end
|
||||||
|
|
||||||
|
local imeta = stack:get_meta()
|
||||||
|
imeta:set_string("mode", mode)
|
||||||
|
|
||||||
|
if pname then
|
||||||
|
core.chat_send_player(pname, S("@1: mode set to: @2", iname, imeta:get_string("mode")))
|
||||||
|
end
|
||||||
|
|
||||||
|
local new_stack
|
||||||
|
if mode == "erase" then
|
||||||
|
new_stack = ItemStack("cleaner:pencil_1")
|
||||||
|
else
|
||||||
|
new_stack = ItemStack("cleaner:pencil")
|
||||||
|
end
|
||||||
|
|
||||||
|
local new_meta = new_stack:get_meta()
|
||||||
|
new_meta:from_table(imeta:to_table())
|
||||||
|
|
||||||
|
return new_stack
|
||||||
|
end,
|
||||||
|
|
||||||
|
next_mode = function(self, stack, pname)
|
||||||
|
local iname = self:format_name(stack)
|
||||||
|
local modes = self.modes[iname]
|
||||||
|
|
||||||
|
if not modes then
|
||||||
|
return false, stack, S('Modes for tool "@1" not available.', stack:get_name())
|
||||||
|
end
|
||||||
|
|
||||||
|
local imeta = stack:get_meta()
|
||||||
|
local current_mode = imeta:get_string("mode")
|
||||||
|
if not current_mode or current_mode:trim() == "" then
|
||||||
|
return true, self:set_mode(stack, modes[1], pname)
|
||||||
|
end
|
||||||
|
|
||||||
|
local idx = 1
|
||||||
|
for _, m in ipairs(modes) do
|
||||||
|
if current_mode == m then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
idx = idx + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
return true, self:set_mode(stack, modes[idx+1] or modes[1], pname)
|
||||||
|
end,
|
||||||
|
|
||||||
|
set_node = function(self, stack, node, pname)
|
||||||
|
local imeta = stack:get_meta()
|
||||||
|
imeta:set_string("node", node)
|
||||||
|
|
||||||
|
if pname then
|
||||||
|
core.chat_send_player(pname, S("@1: node set to: @2", stack:get_name(), imeta:get_string("node")))
|
||||||
|
end
|
||||||
|
|
||||||
|
return stack
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
||||||
|
local use_sounds = core.global_exists("sounds")
|
||||||
|
local sound_handle
|
||||||
|
|
||||||
|
tool.on_use = function(stack, user, pointed_thing)
|
||||||
|
if not user:is_player() then return end
|
||||||
|
|
||||||
|
local pname = user:get_player_name()
|
||||||
|
if not core.get_player_privs(pname).server then
|
||||||
|
core.chat_send_player(pname, S("You do not have permission to use this item. Missing privs: @1", "server"))
|
||||||
|
return stack
|
||||||
|
end
|
||||||
|
|
||||||
|
if sound_handle then
|
||||||
|
core.sound_stop(sound_handle)
|
||||||
|
sound_handle = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if pointed_thing.type == "node" then
|
||||||
|
local npos = core.get_pointed_thing_position(pointed_thing)
|
||||||
|
local imeta = stack:get_meta()
|
||||||
|
local mode = imeta:get_string("mode")
|
||||||
|
local new_node_name = imeta:get_string("node")
|
||||||
|
|
||||||
|
if mode == "erase" then
|
||||||
|
core.remove_node(npos)
|
||||||
|
if use_sounds then
|
||||||
|
local sound_handle = sounds.pencil_erase({object=user})
|
||||||
|
end
|
||||||
|
return stack
|
||||||
|
elseif core.registered_nodes[new_node_name] then
|
||||||
|
if mode == "swap" then
|
||||||
|
core.swap_node(npos, {name=new_node_name})
|
||||||
|
if use_sounds then
|
||||||
|
local sound_handle = sounds.pencil_write({object=user})
|
||||||
|
end
|
||||||
|
elseif mode == "write" then
|
||||||
|
local node_above = core.get_node_or_nil(pointed_thing.above)
|
||||||
|
if not node_above or node_above.name == "air" then
|
||||||
|
core.set_node(pointed_thing.above, {name=new_node_name})
|
||||||
|
if use_sounds then
|
||||||
|
local sound_handle = sounds.pencil_write({object=user})
|
||||||
|
end
|
||||||
|
else
|
||||||
|
core.chat_send_player(pname, S("Can't place node there."))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
core.chat_send_player(pname, S("Unknown mode: @1", mode))
|
||||||
|
end
|
||||||
|
|
||||||
|
return stack
|
||||||
|
end
|
||||||
|
|
||||||
|
core.chat_send_player(pname, S("Cannot place unknown node: @1", new_node_name))
|
||||||
|
return stack
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
tool.on_secondary_use = function(stack, user, pointed_thing)
|
||||||
|
if not user:is_player() then return end
|
||||||
|
|
||||||
|
local pname = user:get_player_name()
|
||||||
|
if not core.get_player_privs(pname).server then
|
||||||
|
core.chat_send_player(pname, S("You do not have permission to use this item. Missing privs: @1", "server"))
|
||||||
|
return stack
|
||||||
|
end
|
||||||
|
|
||||||
|
local success, stack, msg = tool.next_mode(tool, stack, pname)
|
||||||
|
if not success then
|
||||||
|
core.chat_send_player(pname, msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
return stack
|
||||||
|
end
|
||||||
|
|
||||||
|
tool.on_place = function(stack, placer, pointed_thing)
|
||||||
|
if not placer:is_player() then return end
|
||||||
|
|
||||||
|
local pname = placer:get_player_name()
|
||||||
|
if not core.get_player_privs(pname).server then
|
||||||
|
core.chat_send_player(pname, S("You do not have permission to use this item. Missing privs: @1", "server"))
|
||||||
|
return stack
|
||||||
|
end
|
||||||
|
|
||||||
|
if pointed_thing.type == "node" then
|
||||||
|
local node = core.get_node_or_nil(core.get_pointed_thing_position(pointed_thing))
|
||||||
|
if node then
|
||||||
|
stack = tool:set_node(stack, node.name, pname)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return stack
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
clean_duplicates = clean_duplicates,
|
clean_duplicates = clean_duplicates,
|
||||||
|
get_world_data = get_world_data,
|
||||||
|
update_world_data = update_world_data,
|
||||||
|
tool = tool,
|
||||||
}
|
}
|
||||||
|
|
4
mod.conf
4
mod.conf
|
@ -1,5 +1,7 @@
|
||||||
name = cleaner
|
name = cleaner
|
||||||
description = A mod that can be used to remove/replace unknown entities, nodes, & items.
|
description = A mod that can be used to remove/replace unknown entities, nodes, & items.
|
||||||
version = 1.0
|
version = 2025-01-18
|
||||||
license = MIT
|
license = MIT
|
||||||
author = PilzAdam, Jordan Irwin (AntumDeluge)
|
author = PilzAdam, Jordan Irwin (AntumDeluge)
|
||||||
|
min_minetest_version = 5.0
|
||||||
|
optional_depends = sounds
|
||||||
|
|
98
nodes.lua
98
nodes.lua
|
@ -1,20 +1,40 @@
|
||||||
|
|
||||||
local misc = dofile(cleaner.modpath .. "/misc_functions.lua")
|
local aux = dofile(cleaner.modpath .. "/misc_functions.lua")
|
||||||
|
|
||||||
-- populate nodes list from file in world path
|
-- populate nodes list from file in world path
|
||||||
local n_list = {remove={}, replace={}}
|
local nodes_data = aux.get_world_data().nodes
|
||||||
|
|
||||||
|
|
||||||
|
-- START: backward compat
|
||||||
|
|
||||||
local n_path = core.get_worldpath() .. "/clean_nodes.json"
|
local n_path = core.get_worldpath() .. "/clean_nodes.json"
|
||||||
local n_file = io.open(n_path, "r")
|
local n_file = io.open(n_path, "r")
|
||||||
|
|
||||||
if n_file then
|
if n_file then
|
||||||
|
cleaner.log("action", "found deprecated clean_nodes.json, updating")
|
||||||
|
|
||||||
local data_in = core.parse_json(n_file:read("*a"))
|
local data_in = core.parse_json(n_file:read("*a"))
|
||||||
n_file:close()
|
n_file:close()
|
||||||
if data_in then
|
if data_in then
|
||||||
n_list = data_in
|
if data_in.remove then
|
||||||
|
for _, r in ipairs(data_in.remove) do
|
||||||
|
table.insert(nodes_data.remove, r)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if data_in.replace then
|
||||||
|
for k, v in pairs(data_in.replace) do
|
||||||
|
if not nodes_data.replace[k] then
|
||||||
|
nodes_data.replace[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- don't read deprecated file again
|
||||||
|
os.rename(n_path, n_path .. ".old")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- backward compat
|
|
||||||
local n_path_old = core.get_worldpath() .. "/clean_nodes.txt"
|
local n_path_old = core.get_worldpath() .. "/clean_nodes.txt"
|
||||||
n_file = io.open(n_path_old, "r")
|
n_file = io.open(n_path_old, "r")
|
||||||
|
|
||||||
|
@ -25,67 +45,53 @@ if n_file then
|
||||||
for _, e in ipairs(data_in) do
|
for _, e in ipairs(data_in) do
|
||||||
e = e:trim()
|
e = e:trim()
|
||||||
if e ~= "" and e:sub(1, 1) ~= "#" then
|
if e ~= "" and e:sub(1, 1) ~= "#" then
|
||||||
table.insert(n_list.remove, e)
|
table.insert(nodes_data.remove, e)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
n_file:close()
|
n_file:close()
|
||||||
os.rename(n_path_old, n_path_old .. ".bak") -- don't read deprecated file again
|
os.rename(n_path_old, n_path_old .. ".old") -- don't read deprecated file again
|
||||||
end
|
end
|
||||||
|
|
||||||
n_list.remove = misc.clean_duplicates(n_list.remove)
|
-- END: backward compat
|
||||||
|
|
||||||
|
|
||||||
|
nodes_data.remove = aux.clean_duplicates(nodes_data.remove)
|
||||||
|
|
||||||
-- update json file with any changes
|
-- update json file with any changes
|
||||||
n_file = io.open(n_path, "w")
|
aux.update_world_data("nodes", nodes_data)
|
||||||
if n_file then
|
|
||||||
local data_out = core.write_json(n_list, true)
|
|
||||||
|
|
||||||
-- FIXME: how to do this with a single regex?
|
core.register_lbm({
|
||||||
data_out = data_out:gsub("\"remove\" : null", "\"remove\" : []")
|
name = "cleaner:remove_nodes",
|
||||||
data_out = data_out:gsub("\"replace\" : null", "\"replace\" : {}")
|
|
||||||
|
|
||||||
n_file:write(data_out)
|
|
||||||
n_file:close()
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, n in ipairs(n_list.remove) do
|
|
||||||
cleaner.log("debug", "Cleaning node: " .. n)
|
|
||||||
|
|
||||||
core.register_node(":" .. n, {
|
|
||||||
groups = {to_remove=1},
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
core.register_abm({
|
|
||||||
nodenames = {"group:to_remove"},
|
nodenames = {"group:to_remove"},
|
||||||
interval = 1,
|
run_at_every_load = true,
|
||||||
chance = 1,
|
|
||||||
action = function(pos, node)
|
action = function(pos, node)
|
||||||
core.remove_node(pos)
|
core.remove_node(pos)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
for n_old, n_new in pairs(n_list.replace) do
|
core.register_lbm({
|
||||||
cleaner.log("debug", "Replacing node \"" .. n_old .. "\" with \"" .. n_new .. "\"")
|
name = "cleaner:replace_nodes",
|
||||||
|
|
||||||
core.register_node(":" .. n_old, {
|
|
||||||
groups = {to_replace=1},
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
core.register_abm({
|
|
||||||
nodenames = {"group:to_replace"},
|
nodenames = {"group:to_replace"},
|
||||||
interval = 1,
|
run_at_every_load = true,
|
||||||
chance = 1,
|
|
||||||
action = function(pos, node)
|
action = function(pos, node)
|
||||||
core.remove_node(pos)
|
local new_node_name = cleaner.get_replace_nodes()[node.name]
|
||||||
|
if core.registered_nodes[new_node_name] then
|
||||||
local new_node_name = n_list.replace[node.name]
|
core.swap_node(pos, {name=new_node_name})
|
||||||
local new_node = core.registered_nodes[new_node_name]
|
|
||||||
if new_node then
|
|
||||||
core.place_node(pos, new_node)
|
|
||||||
else
|
else
|
||||||
cleaner.log("error", "cannot replace with unregistered node \"" .. tostring(new_node_name) .. "\"")
|
cleaner.log("error", "cannot replace with unregistered node \"" .. tostring(new_node_name) .. "\"")
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
core.register_on_mods_loaded(function()
|
||||||
|
for _, n in ipairs(nodes_data.remove) do
|
||||||
|
cleaner.log("action", "registering node for removal: " .. n)
|
||||||
|
cleaner.register_node_removal(n)
|
||||||
|
end
|
||||||
|
|
||||||
|
for n_old, n_new in pairs(nodes_data.replace) do
|
||||||
|
cleaner.log("action", "registering node \"" .. n_old .. "\" to be replaced with \"" .. n_new .. "\"")
|
||||||
|
cleaner.register_node_replacement(n_old, n_new)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
17
ores.lua
Normal file
17
ores.lua
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
if not cleaner.unsafe then return end
|
||||||
|
|
||||||
|
local aux = dofile(cleaner.modpath .. "/misc_functions.lua")
|
||||||
|
|
||||||
|
local ores_data = aux.get_world_data().ores
|
||||||
|
|
||||||
|
for _, ore in ipairs(ores_data.remove) do
|
||||||
|
cleaner.register_ore_removal(ore)
|
||||||
|
end
|
||||||
|
|
||||||
|
core.register_on_mods_loaded(function()
|
||||||
|
for _, ore in ipairs(cleaner.get_remove_ores()) do
|
||||||
|
cleaner.log("action", "unregistering ore: " .. ore)
|
||||||
|
cleaner.remove_ore(ore)
|
||||||
|
end
|
||||||
|
end)
|
BIN
screenshot.png
Normal file
BIN
screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 183 KiB |
70
set_version.py
Executable file
70
set_version.py
Executable file
|
@ -0,0 +1,70 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import sys, os, codecs
|
||||||
|
|
||||||
|
|
||||||
|
f_script = os.path.realpath(__file__)
|
||||||
|
d_root = os.path.dirname(f_script)
|
||||||
|
|
||||||
|
os.chdir(d_root)
|
||||||
|
|
||||||
|
args = sys.argv[1:]
|
||||||
|
if len(args) < 1:
|
||||||
|
print("ERROR: must supply version as parameter")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
new_version = args[0]
|
||||||
|
|
||||||
|
to_update = {
|
||||||
|
"mod.conf": "version =",
|
||||||
|
"changelog.txt": "next",
|
||||||
|
os.path.normpath(".ldoc/config.ld"): "local version =",
|
||||||
|
}
|
||||||
|
|
||||||
|
for f in to_update:
|
||||||
|
f_path = os.path.join(d_root, f)
|
||||||
|
if not os.path.isfile(f_path):
|
||||||
|
print("WARNING: {} not found, skipping ...".format(f))
|
||||||
|
continue
|
||||||
|
|
||||||
|
print("\nsetting version to {} in {}".format(new_version, f_path))
|
||||||
|
|
||||||
|
buffer = codecs.open(f_path, "r", "utf-8")
|
||||||
|
if not buffer:
|
||||||
|
print("WARNING: could not open {} for reading, skipping ...".format(f))
|
||||||
|
continue
|
||||||
|
|
||||||
|
read_in = buffer.read()
|
||||||
|
buffer.close()
|
||||||
|
|
||||||
|
read_in = read_in.replace("\r\n", "\n").replace("\r", "\n")
|
||||||
|
replacement = to_update[f]
|
||||||
|
new_lines = []
|
||||||
|
|
||||||
|
version_set = False
|
||||||
|
for li in read_in.split("\n"):
|
||||||
|
if not version_set:
|
||||||
|
if "=" in replacement and li.startswith(replacement):
|
||||||
|
key = li.split(" = ")[0]
|
||||||
|
li = "{} = {}".format(key, new_version)
|
||||||
|
version_set = True
|
||||||
|
elif li == replacement:
|
||||||
|
li = "v{}".format(new_version)
|
||||||
|
version_set = True
|
||||||
|
|
||||||
|
new_lines.append(li)
|
||||||
|
|
||||||
|
write_out = "\n".join(new_lines)
|
||||||
|
if write_out == read_in:
|
||||||
|
print("no changes for {}, skipping ...".format(f))
|
||||||
|
continue
|
||||||
|
|
||||||
|
buffer = codecs.open(f_path, "w", "utf-8")
|
||||||
|
if not buffer:
|
||||||
|
print("WARNING: could not open {} for writing, skipping ...".format(f))
|
||||||
|
continue
|
||||||
|
|
||||||
|
buffer.write("\n".join(new_lines))
|
||||||
|
buffer.close()
|
||||||
|
|
||||||
|
print("done")
|
15
settings.lua
Normal file
15
settings.lua
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
--- Cleaner Settings
|
||||||
|
--
|
||||||
|
-- @topic settings
|
||||||
|
|
||||||
|
|
||||||
|
--- Enables unsafe methods & chat commands.
|
||||||
|
--
|
||||||
|
-- - `cleaner.remove_ore`
|
||||||
|
-- - `/remove_ores`
|
||||||
|
--
|
||||||
|
-- @setting cleaner.unsafe
|
||||||
|
-- @settype bool
|
||||||
|
-- @default false
|
||||||
|
cleaner.unsafe = core.settings:get_bool("cleaner.unsafe", false)
|
6
settingtypes.txt
Normal file
6
settingtypes.txt
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
# Enables unsafe methods & chat commands.
|
||||||
|
#
|
||||||
|
# - cleaner.remove_ore
|
||||||
|
# - /remove_ores
|
||||||
|
cleaner.unsafe (Enable unsafe methods) bool false
|
BIN
textures/cleaner_pencil.png
Normal file
BIN
textures/cleaner_pencil.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 255 B |
44
tools.lua
Normal file
44
tools.lua
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
|
||||||
|
--- Cleaner Tools
|
||||||
|
--
|
||||||
|
-- @topic tools
|
||||||
|
|
||||||
|
|
||||||
|
local S = core.get_translator(cleaner.modname)
|
||||||
|
|
||||||
|
|
||||||
|
local aux = dofile(cleaner.modpath .. "/misc_functions.lua")
|
||||||
|
|
||||||
|
--- Master Pencil
|
||||||
|
--
|
||||||
|
-- @tool cleaner:pencil
|
||||||
|
-- @img cleaner_pencil.png
|
||||||
|
-- @priv server
|
||||||
|
-- @usage
|
||||||
|
-- place (right-click):
|
||||||
|
-- - when not pointing at a node, changes modes
|
||||||
|
-- - when pointing at a node, sets node to be used
|
||||||
|
--
|
||||||
|
-- use (left-click):
|
||||||
|
-- - executes action for current mode:
|
||||||
|
-- - erase: erases pointed node
|
||||||
|
-- - write: adds node
|
||||||
|
-- - swap: replaces pointed node
|
||||||
|
core.register_tool(cleaner.modname .. ":pencil", {
|
||||||
|
description = S("Master Pencil"),
|
||||||
|
inventory_image = "cleaner_pencil.png",
|
||||||
|
liquids_pointable = true,
|
||||||
|
on_use = aux.tool.on_use,
|
||||||
|
on_secondary_use = aux.tool.on_secondary_use,
|
||||||
|
on_place = aux.tool.on_place,
|
||||||
|
})
|
||||||
|
|
||||||
|
core.register_tool(cleaner.modname .. ":pencil_1", {
|
||||||
|
description = S("Master Pencil"),
|
||||||
|
inventory_image = "cleaner_pencil.png^[transformFXFY",
|
||||||
|
liquids_pointable = true,
|
||||||
|
groups = {not_in_creative_inventory=1},
|
||||||
|
on_use = aux.tool.on_use,
|
||||||
|
on_secondary_use = aux.tool.on_secondary_use,
|
||||||
|
on_place = aux.tool.on_place,
|
||||||
|
})
|
Loading…
Add table
Reference in a new issue