rolling-12

master
Lars Mueller 2020-03-23 20:20:14 +01:00
parent b2b5d3df9f
commit ae703fb914
4 changed files with 144 additions and 111 deletions

View File

@ -1 +1,6 @@
modlib.mod.init("cmdlib")
modlib.mod.init("cmdlib")
modlib.mod.extend("cmdlib", "override")
-- Tests - only uncomment if testing stuff
--[[
--dofile(modlib.mod.get_resource("cmdlib", "test.lua"))
]]

196
main.lua
View File

@ -3,25 +3,21 @@ error_format = minetest.get_color_escape_sequence("#FF0000") .. "%s"
success_format = minetest.get_color_escape_sequence("#00FF00") .. "%s"
function scope_func(scope)
return function()
return false, "Not a chatcommand, but a category. For a list of subcommands do /help " .. scope .. "."
return false,
"Not a chatcommand, but a category. For a list of subcommands do /help " ..
scope .. "."
end
end
chatcommands = trie.new()
chatcommand_info = {}
error = function(str)
return string.format(error_format, str)
end
success = function(str)
return string.format(success_format, str)
end
format_error = function(str) return string.format(error_format, str) end
format_success = function(str) return string.format(success_format, str) end
function validate_privs(required, actual)
local missing, to_lose = {}, {}
for priv, expected in pairs(required) do
if expected then
if not actual[priv] then
table.insert(missing, priv)
end
if not actual[priv] then table.insert(missing, priv) end
elseif actual[priv] then
table.insert(to_lose, priv)
end
@ -31,14 +27,10 @@ end
function validate_privs_ipairs(required, forbidden, actual)
local missing, to_lose = {}, {}
for _, priv in ipairs(required) do
if not actual[priv] then
table.insert(missing, priv)
end
if not actual[priv] then table.insert(missing, priv) end
end
for _, priv in ipairs(forbidden) do
if actual[priv] then
table.insert(to_lose, priv)
end
if actual[priv] then table.insert(to_lose, priv) end
end
return missing, to_lose
end
@ -46,13 +38,16 @@ function sufficient_privs(required, playername)
local missing, to_lose = validate_privs(required, minetest.get_player_privs(playername))
local str
if not modlib.table.is_empty(missing) then
str = string.format("Missing privilege%s: ", ("s" and #missing > 1) or "") .. table.concat(missing)
str = string.format("Missing privilege%s: ",
("s" and #missing > 1) or "") ..
table.concat(missing)
end
if not modlib.table.is_empty(to_lose) then
str =
(str or "") ..
string.format("%srivilege%s which need to be lost: ", (str and ", p") or "P", ("s" and #to_lose > 1) or "") ..
table.concat(to_lose)
str = (str or "") ..
string.format("%srivilege%s which need to be lost: ",
(str and ", p") or "P",
("s" and #to_lose > 1) or "") ..
table.concat(to_lose)
end
return str
end
@ -60,18 +55,21 @@ function build_param_parser(syntax)
local params = modlib.text.split_without_limit(syntax, " ")
local required_params, optional_params, list_param = {}, {}
local i = 1
while i <= #params and params[i]:sub(1, 1) == "<" and params[i]:sub(params[i]:len()) == ">" do
while i <= #params and params[i]:sub(1, 1) == "<" and
params[i]:sub(params[i]:len()) == ">" do
table.insert(required_params, params[i]:sub(2, params[i]:len() - 1))
i = i + 1
end
while i <= #params and params[i]:sub(1, 1) == "[" and params[i]:sub(params[i]:len()) == "]" do
while i <= #params and params[i]:sub(1, 1) == "[" and
params[i]:sub(params[i]:len()) == "]" do
table.insert(optional_params, params[i]:sub(2, params[i]:len() - 1))
i = i + 1
end
if i <= #params then
-- check for list param
if i == #params and params[i]:sub(1, 1) == "{" and params[i]:sub(params[i]:len()) == "}" then
if i == #params and params[i]:sub(1, 1) == "{" and
params[i]:sub(params[i]:len()) == "}" then
list_param = params[i]:sub(2, params[i]:len() - 1)
else
return -- Failure
@ -79,26 +77,20 @@ function build_param_parser(syntax)
end
local limit = #required_params + #optional_params
if list_param then
limit = nil
end
if list_param then limit = nil end
local minimum = #required_params
local paramlist = required_params
modlib.table.append(paramlist, optional_params)
return function(param)
local params = modlib.text.split(param, " ", limit)
for i, param in modlib.table.rpairs(params) do
if param == "" then
table.remove(params, i)
end
if param == "" then table.remove(params, i) end
end
if #params < minimum then
return "Too few parameters given! At least " ..
minimum ..
" " ..
((minimum == 1 and "is") or "are") ..
" required. The following parameters are missing: " ..
table.concat({unpack(required_params, #params + 1)})
return "Too few parameters given! At least " .. minimum .. " " ..
((minimum == 1 and "is") or "are") ..
" required. The following parameters are missing: " ..
table.concat({unpack(required_params, #params + 1)})
end
local paramtable = {}
for index, name in ipairs(paramlist) do
@ -115,9 +107,7 @@ function build_func(def)
return function(invokername, params)
if def.privs then
local error = sufficient_privs(def.privs, invokername)
if error then
return false, error
end
if error then return false, error end
end
return def.fnc(invokername, params)
end
@ -125,14 +115,10 @@ function build_func(def)
return function(invokername, params)
if def.privs then
local error = sufficient_privs(def.privs, invokername)
if error then
return false, error
end
if error then return false, error end
end
local error, params = def.param_parser(params)
if error then
return false, error
end
if error then return false, error end
return def.fnc(invokername, params)
end
end
@ -145,9 +131,7 @@ function register_chatcommand(name, def, override)
implicit_call = def.implicit_call,
fnc = def.func or error("/" .. name .. ": No function given")
}
if definition.params then
definition.implicit_call = true
end
if definition.params then definition.implicit_call = true end
if not definition.custom_syntax then
definition.param_parser = build_param_parser(definition.params or "")
end
@ -157,9 +141,13 @@ function register_chatcommand(name, def, override)
chatcommand_info[name] = modlib.table.tablecopy(definition)
trie.insert(chatcommands, name, definition, override)
else
local supercommand, super_info = trie.get(chatcommands, scopes[1]), chatcommand_info[scopes[1]]
local supercommand, super_info = trie.get(chatcommands, scopes[1]),
chatcommand_info[scopes[1]]
if not supercommand then
supercommand = {subcommands = trie.new(), func = scope_func(scopes[1])}
supercommand = {
subcommands = trie.new(),
func = scope_func(scopes[1])
}
trie.insert(chatcommands, scopes[1], supercommand)
super_info = {subcommands = {}}
chatcommand_info[scopes[1]] = super_info
@ -172,26 +160,63 @@ function register_chatcommand(name, def, override)
if not super_info.subcommands then
super_info.subcommands = {}
end
local subcommand = {subcommands = trie.new(), func = scope_func(scopes[1])}
local prevval = trie.insert(supercommand.subcommands, scopes[i], subcommand)
modlib.table.add_all(inherited_privs, (prevval and prevval.privs) or {})
local subcommand = {
subcommands = trie.new(),
func = scope_func(scopes[1])
}
local prevval = trie.insert(supercommand.subcommands, scopes[i],
subcommand)
modlib.table.add_all(inherited_privs,
(prevval and prevval.privs) or {})
supercommand = prevval or subcommand
super_info.subcommands[scopes[i]] = super_info.subcommands[scopes[i]] or {subcommands = {}}
super_info.subcommands[scopes[i]] =
super_info.subcommands[scopes[i]] or {subcommands = {}}
super_info = super_info.subcommands[scopes[i]]
end
modlib.table.add_all(inherited_privs, def.privs or {})
if not supercommand.subcommands then
supercommand.subcommands = trie.new()
end
if not super_info.subcommands then
super_info.subcommands = {}
end
if not super_info.subcommands then super_info.subcommands = {} end
definition.privs = next(inherited_privs) and inherited_privs
super_info.subcommands[scopes[#scopes]] = modlib.table.tablecopy(definition)
trie.insert(supercommand.subcommands, scopes[#scopes], definition, override)
super_info.subcommands[scopes[#scopes]] =
modlib.table.tablecopy(definition)
trie.insert(supercommand.subcommands, scopes[#scopes], definition,
override)
end
end
wrap_text = function(text, max)
local function name_comparator(info, name)
return modlib.table.default_comparator(info.name, name)
end
local binary_search_name = modlib.table.binary_search_comparator(name_comparator)
function unregister_chatcommand(name)
local function get(info, name)
return info[(#chatcommand_info ~= 0 and binary_search(info, name)) or name]
end
local scopes = modlib.text.split_without_limit(name, " ")
local super_info = chatcommand_info
local super_trie = chatcommands
local head_trie = chatcommands
local head_info, head_name = chatcommand_info, scopes[1]
for i = 2, #scopes do
super_info = get(super_info, scopes[i-1]).subcommands
super_trie = trie.get(super_trie, scopes[i-1]).subcommands
local reset_head = next(super_info, next(super_info)) or super_info.implicit_call
if reset_head then
head_info, head_name = super_info, scopes[i]
head_trie = super_trie
end
end
trie.remove(head_trie, head_name)
if #chatcommand_info ~= 0 then
head_name = binary_search_name(head_info, head_name)
end
head_info[head_name] = nil
end
function wrap_text(text, max)
max = max or 80
local res = {text}
while res[#res]:len() > max do
@ -219,19 +244,21 @@ function handle_chat_message(sendername, message)
end
cmd, suggestion, _ = trie.search(command_trie, command_name)
if not cmd then
minetest.chat_send_player(
sendername,
string.format(
error_format,
"No such chatcommand. " ..
((suggestion and 'Did you mean "' .. message:sub(1, last_space - 1) .. suggestion .. '" ?') or
"")
)
)
minetest.chat_send_player(sendername,
string.format(error_format,
"No such chatcommand. " ..
((suggestion and
'Did you mean "' ..
message:sub(1,
last_space -
1) ..
suggestion ..
'" ?') or "")))
return true
elseif cmd.subcommands and not cmd.implicit_call then
command_trie = cmd.subcommands
last_space, next_space = next_space + 1, message:find(" ", next_space + 1)
last_space, next_space = next_space + 1,
message:find(" ", next_space + 1)
else
last_space = next_space + 1
break
@ -241,9 +268,11 @@ function handle_chat_message(sendername, message)
local success, response = cmd.func(sendername, params)
if response then
if success == true then
minetest.chat_send_player(sendername, string.format(success_format, response))
minetest.chat_send_player(sendername, string.format(
success_format, response))
elseif success == false then
minetest.chat_send_player(sendername, string.format(error_format, response))
minetest.chat_send_player(sendername,
string.format(error_format, response))
else
minetest.chat_send_player(sendername, response)
end
@ -254,11 +283,6 @@ end
table.insert(core.registered_on_chat_messages, 1, handle_chat_message)
modlib.mod.extend("cmdlib", "override")
-- Tests - only uncomment if testing stuff
--[[
dofile("test.lua")
]]
function build_info(chatcommands)
local new_info = {}
for name, def in pairs(chatcommands) do
@ -272,6 +296,7 @@ function build_info(chatcommands)
table.insert(newforbiddenprivs, priv)
end
end
newdef.implicit_call = def.implicit_call
newdef.description = def.description or ""
newdef.descriptions = wrap_text(def.description or "", 60)
newdef.privs = next(newprivs) and newprivs
@ -283,18 +308,11 @@ function build_info(chatcommands)
newdef.subcommands = build_info(newdef.subcommands)
end
end
table.sort(
new_info,
function(d1, d2)
return d1.name < d2.name
end
)
table.sort(new_info, function(d1, d2) return d1.name < d2.name end)
return new_info
end
minetest.register_on_mods_loaded(
function()
modlib.mod.extend("cmdlib", "help")
chatcommand_info = build_info(chatcommand_info)
end
)
minetest.register_on_mods_loaded(function()
modlib.mod.extend("cmdlib", "help")
chatcommand_info = build_info(chatcommand_info)
end)

View File

@ -1,16 +1,21 @@
minetest.original_register_chatcommand = minetest.register_chatcommand
minetest.original_override_chatcommand = minetest.override_chatcommand
minetest.original_unregister_chatcommand = minetest.unregister_chatcommand
local minetest_register_chatcommand = function(name, def)
register_chatcommand(name, {
description = def.description,
privs = def.privs,
params = def.params,
custom_syntax = true,
func = def.func
})
function minetest_register_chatcommand_generator(override)
return function(name, def, override)
register_chatcommand(name, {
description = def.description,
privs = def.privs,
params = def.params,
custom_syntax = true,
func = def.func
}, override)
end
end
local minetest_register_chatcommand = minetest_register_chatcommand_generator()
for name, def in pairs(minetest.registered_chatcommands) do
minetest_register_chatcommand(name, def)
end
@ -20,7 +25,10 @@ minetest.register_chatcommand = function(name, def)
minetest.original_register_chatcommand(name, def)
end
local minetest_override_chatcommand = minetest_register_chatcommand_generator(true)
minetest.override_chatcommand = function(name, def)
minetest_register_chatcommand(name, def)
minetest_override_chatcommand(name, def)
minetest.original_override_chatcommand(name, def)
end
end
minetest.unregister_chatcommand = unregister_chatcommand

View File

@ -6,14 +6,19 @@ function test_format()
end
function test_chatcommands()
cmdlib.register_chatcommand("cmdlib_test", {
--[[cmdlib.register_chatcommand("cmdlib_test", {
params = "<param1>",
privs = {fast = true},
description = "Test command for cmdlib.",
func = function(sendername, params)
return true, "You shouted "..(params.param1)
end
})]]
cmdlib.register_chatcommand("this", {
params = "<param1>",
func = function() end
})
cmdlib.unregister_chatcommand("this")
cmdlib.register_chatcommand("cmdlib_test say", {
params = "<param1>",
privs = {fast = true, noclip = false},
@ -22,23 +27,23 @@ function test_chatcommands()
return true, "You said "..(params.param1)
end
})
-- TODO fix error when invoking without params (should say params required ?)
cmdlib.register_chatcommand("cmdlib_test bark", {
cmdlib.register_chatcommand("cmdlib_test repeat", {
params = "<param1>",
privs = {fast = true},
description = "Test command for cmdlib.",
func = function(sendername, params)
return true, "You barked "..(params.param1)
return true, "Param1: "..(params.param1)
end
})
cmdlib.register_chatcommand("cmdlib_test bark loud", {
cmdlib.register_chatcommand("cmdlib_test shout loud", {
params = "[param1]",
privs = {fast = true},
description = "Test command : cmdlib.",
description = "Test command with a different description.",
func = function(sendername, params)
return true, "You BARKED "..(params.param1 or "IDK")
return true, "You SHOUTED "..(params.param1 or "IDK")
end
})
cmdlib.unregister_chatcommand("cmdlib_test shout loud")
end
function test_trie()
@ -46,7 +51,6 @@ function test_trie()
trie.insert(t, "help")
trie.insert(t, "heap")
trie.insert(t, "me")
--trie.insert(t, "heap")
print(trie.search(t, "hewp"))
trie.remove(t, "heap")
print(trie.search(t, "help"))
@ -62,6 +66,4 @@ end
test_chatcommands()
-- test_format()
-- test_trie()
-- test_info()
--minetest.register_node("cmdlib:item", {description = minetest.get_color_escape_sequence("#FF0000").."✗ Looks like there's an error !"})
-- test_info()