Compare commits

...

27 Commits

Author SHA1 Message Date
AFCMS a62788b728 stuff 2022-01-02 18:19:55 +01:00
AFCMS 63b415b6bf stuff 2021-12-27 23:25:25 +01:00
AFCMS 201ee1ac81 stuff 2021-12-27 23:25:20 +01:00
AFCMS c7f808a2ce Merge branch 'master' into chat-command-builder-2 2021-12-23 21:21:47 +01:00
AFCMS 2d7ee83d3e Merge branch 'master' into chat-command-builder-2 2021-07-12 20:54:54 +02:00
AFCMS a65acd3ab8 Merge branch 'master' into chat-command-builder-2 2021-07-06 11:07:18 +02:00
AFCMS 3718bb6f80 mcl_death_drop: allow mods to overide default behaviour 2021-07-02 18:07:29 +02:00
AFCMS 19745cafbc fix wrong API def 2021-07-02 01:06:01 +02:00
AFCMS bc2e122652 Merge branch 'master' into chat-command-builder-2 2021-07-02 01:03:45 +02:00
AFCMS b0ca506688 basic proof of concept 2021-07-02 01:03:25 +02:00
AFCMS 0a57666771 performance improvement 2021-06-25 10:22:12 +02:00
AFCMS 07513d8e52 fixes 2021-06-25 10:09:52 +02:00
AFCMS a6e0605b7c testing stuff 2021-06-24 23:58:17 +02:00
AFCMS cc0ed855a9 fixes 2021-06-24 23:45:47 +02:00
AFCMS 658028e90a fixes 2021-06-24 23:39:15 +02:00
AFCMS 3f1b12e327 fixes and testing stuff 2021-06-24 22:50:44 +02:00
AFCMS e436f1a6af remove a stupic crash xD 2021-06-24 18:04:20 +02:00
AFCMS d19f88d226 add some other types 2021-06-24 14:12:37 +02:00
AFCMS 6d1bc8f865 add float type 2021-06-24 14:04:15 +02:00
AFCMS 102f45336b move mcl_privs to CORE 2021-06-24 12:35:40 +02:00
AFCMS be75150725 add help formspec refactoring to the TODO list 2021-06-24 12:15:29 +02:00
AFCMS 0619d480dd integrate translation system 2021-06-24 12:07:13 +02:00
AFCMS 0129fdafef basic target selector system 2021-06-24 12:03:56 +02:00
AFCMS 5ddc3cf99a add some type definitions 2021-06-24 11:54:15 +02:00
AFCMS 84900b39ad basic API setup 2021-06-24 11:40:35 +02:00
AFCMS 528f1cb81f move some files to the right location 2021-06-24 10:03:05 +02:00
AFCMS bb576148f4 move mcl_commands to CORE folder 2021-06-24 10:00:52 +02:00
36 changed files with 583 additions and 65 deletions

View File

@ -0,0 +1,55 @@
### `mcl_commands.register_command(name, def)`
#### Complex commands (WIP):
```
mcl_commands.register_command("test", {
type = "complex",
priv_level = 4,
func = function(context)
if self.match_type("int", {min=1, max=255} then
print(self.last_type)
end
end,
})
```
#### Basic commands:
* `context.type` is the context of execution: `player` or `commandblock`
* `context.commander` is the executor/commander of the command
* `context.pos` is the position where the command is executed
This param allow the use of position target selectors
```
mcl_commands.register_command("test", {
type = "basic",
priv_level = 4,
func = function(context)
if context.commander then
print(context.pos)
print("--------") --this "concept" param allow to run command correctly
--(with target selector and logging) from mods or command blocks
return true, S("Succesfull")
end
end,
})
```
### `mcl_commands.execute_command(name, params, context)`
#### As a player:
```
mcl_commands.execute_command("test", "foo bar true 1", {type="player", commander=player, pos=player:get_pos()})
```
#### As a command block:
```
mcl_commands.execute_command("test", "foo bar true 1", {type="commandblock", commander=commander, pos=node_pos})
```
### `mcl_commands.get_target_selector(target_selector)`
This function allow mods and commands to get the result of a given target selector in that form: `@e[gamemode=creative,limit=5]`
This function returns a code indicating the success and a table of ObjectRefs (can be empty)

View File

@ -0,0 +1,7 @@
mcl_commands.alias_command("?", "help", false)
mcl_commands.alias_command("pardon", "unban", false)
mcl_commands.rename_command("stop", "shutdown", false)
mcl_commands.alias_command("tell", "msg", false)
mcl_commands.alias_command("w", "msg", false)
mcl_commands.alias_command("tp", "teleport", false)
mcl_commands.rename_command("clear", "clearinv", false)

View File

@ -0,0 +1,82 @@
local S = minetest.get_translator(minetest.get_current_modname())
local C = minetest.colorize
local ipairs = ipairs
local string = string
mcl_commands.types = {
word = {
},
}
local old_minetest_register_chatcommand = minetest.register_chatcommand
function minetest.register_chatcommand(cmd, def)
minetest.log("warning", "[mcl_commands] DEPRECATED")
old_minetest_register_chatcommand(cmd, def)
end
mcl_commands.registered_commands = {}
function mcl_commands.register_command(name, def)
--TODO: do sanity checks first
local params_count = 0
for _,_ in ipairs(def.def) do
params_count = params_count + 1
end
def.def._params_count = params_count
mcl_commands.registered_commands[name] = def
--TODO: move registration on mods loaded
old_minetest_register_chatcommand(name, {
description = def.description,
func = function(pname, params)
--TODO: make sure player is online
return mcl_commands.handle_command(name, params, {
type = "player",
name = pname,
object = nil, --FIXME
op_level = 4,
})
end,
})
end
local function match_param(param, str)
local p_source = string.find("", str)
end
function mcl_commands.handle_command(name, params, context)
local cdef = mcl_commands.registered_commands[name]
if cdef.def._params_count == 0 then
return cdef.def.execute(context)
else
local paramtable = {}
for _,p in ipairs(cdef.def) do
end
end
end
--0: succesfull, table
--1: not connected player, nil
--2: invalid target selector, nil
function mcl_commands.get_target_selector(target_selector)
if minetest.player_exists(target_selector) then
local obj = minetest.get_player_by_name(target_selector)
if obj then
return 0, {obj}
else
return 1, nil
end
elseif string.sub(1, 1) == "@" then
local selector_type = string.sub(2, 2)
if selector_type == "a" then
return 0, minetest.get_connected_players()
end
end
end

View File

@ -0,0 +1,204 @@
local S = minetest.get_translator(minetest.get_current_modname())
local C = minetest.colorize
--TODO: like mc error message
--TODO: complex command handling
--TODO: mc like help system
mcl_commands.types = {
bool = {
lengh = 1,
msg = S("Invalid boolean"),
func = function(word)
if word == "true" then
return true, true
elseif world == "false" then
return true, false
else
return false, nil
end
end,
},
int = {
lengh = 1,
msg = S("Invalid integer"),
func = function(int)
if tonumber(int) and tonumber(int) == math.round(int) then
return true, tonumber(int)
else
return false, nil
end
end,
},
float = {
lengh = 1,
msg = S("Invalid integer"),
func = function(float)
if tonumber(float) then
return true, tonumber(float)
else
return false, nil
end
end,
},
word = {
lengh = 1,
msg = S("Invalid word"),
func = function(word)
if word then
return true, word
else
return false, nil
end
end,
},
text = {},
pos = {
lengh = 3,
msg = S("Invalid position"),
func = function(x, y, z)
--FIXME
if true then
return true, vector.new(x, y, z)
else
return false, nil
end
end,
},
target = {
lengh = 1,
msg = S("Invalid target selector"),
func = function(target)
--mcl_commands.get_target_selector(target_selector)
if minetest.player_exists(target) then
return true, target
else
return false, nil
end
end,
},
playername = {
lengh = 1,
msg = S("Invalid player name"),
func = function(name)
if minetest.player_exists(name) then
return true, name
else
return false, nil
end
end,
},
}
function mcl_commands.match_param(table, index, type, params)
local typedef = mcl_commands.types[type]
if typedef.lengh > 1 then
return
else
local params = {}
typedef.func()
end
end
mcl_commands.registered_commands = {}
function mcl_commands.register_complex_command()
end
--aims to avoid complexity for basic commands while keeping proper messages and privs management
function mcl_commands.register_basic_command(name, def)
local func
if def.params then
func = function(name, param)
local funcparams = {}
local i = 0
for str in string.gmatch(params, "([^ ]+)") do
i = i + 1
funcparams[i] = str
end
for _,type in pairs(def.params) do
mcl_commands.match_param(funcparams, index, type, params)
end
end
else
mcl_commands.registered_commands[name] = {type = "basic", description = def.desc, privs = def.privs}
func = function(name, param)
if param == "" then
local out, msg = def.func(name)
if out then
return true, C(mcl_colors.GRAY, msg) or C(mcl_colors.GRAY, S("succesful"))
else
return false, C(mcl_colors.RED, msg) or C(mcl_colors.RED, S("failed"))
end
else
return false, C(mcl_colors.RED, S("Invalid command usage"))
end
end
end
minetest.register_chatcommand(name, {
description = def.desc,
privs = def.privs,
func = func,
})
end
--[[
mcl_commands.register_basic_command("test", {
description = S("testing command"),
params = nil,
func = function(name)
end,
})
]]
mcl_commands.register_basic_command("testb", {
description = S("testing command"),
params = {
{type="bool"},
{type="int", params={min=1, max=10}}
},
func = function(name, bool, int)
return true, "test: "..int
end,
})
function mcl_commands.alias_command(alias, original_name, bypass_setting)
if minetest.settings:get_bool("mcl_builtin_commands_overide", true) or bypass_setting then
local def = minetest.registered_chatcommands[original_name]
minetest.register_chatcommand(alias, def)
minetest.log("action", string.format("[mcl_commands] Aliasing [%s] command to [%s]", original_name, alias))
else
minetest.log("action", string.format("[mcl_commands] Aliasing [%s] command to [%s] skipped according to setting", original_name, alias))
end
end
function mcl_commands.rename_command(new_name, original_name, bypass_setting)
if minetest.settings:get_bool("mcl_builtin_commands_overide", true) or bypass_setting then
local def = minetest.registered_chatcommands[original_name]
minetest.register_chatcommand(new_name, def)
minetest.unregister_chatcommand(original_name)
minetest.log("action", string.format("[mcl_commands] Renaming [%s] command to [%s]", original_name, new_name))
else
minetest.log("action", string.format("[mcl_commands] Renaming [%s] command to [%s] skipped according to setting", original_name, new_name))
end
end
--0: succesfull, table
--1: not connected player, nil
--2: invalid target selector, nil
function mcl_commands.get_target_selector(target_selector)
if minetest.player_exists(target_selector) then
local obj = minetest.get_player_by_name(target_selector)
if obj then
return 0, {obj}
else
return 1, nil
end
elseif string.sub(1, 1) == "@" then
local selector_type = string.sub(2, 2)
if selector_type == "a" then
return 0, minetest.get_connected_players()
end
end
end

View File

@ -0,0 +1,18 @@
local modpath = minetest.get_modpath(minetest.get_current_modname())
mcl_commands = {}
dofile(modpath.."/api.lua")
dofile(modpath.."/test_commands.lua")
--[[
dofile(modpath.."/register/kill.lua")
dofile(modpath.."/register/setblock.lua")
dofile(modpath.."/register/seed.lua")
dofile(modpath.."/register/summon.lua")
dofile(modpath.."/register/say.lua")
dofile(modpath.."/register/list.lua")
dofile(modpath.."/register/sound.lua")
dofile(modpath.."/register/banlist.lua")
]]
--dofile(modpath.."/alias.lua")

View File

@ -0,0 +1,56 @@
mcl_commands.register_command("test", {
description = S("testing command"),
def = {
{type="target", name="t"},
{type="subcommand", subcommands = {
{name = "set",
{type="int", name="age", params={min=0,max=100}},
{type="choice", name="other", params={values={"man", "woman"}}},
require = 1,
execute = function(context, params, subcommand_params)
return true, params.t..":"..subcommand_params.age..":"..subcommand_params.other
end,
}
}},
},
})
local exemple_context = {
type = "player",
name = "playername",
object = player, --is ALWAYS a valid object
op_level = get_player_op_level(player),
}
--we need a way for mods to run commands on offline players, but this should be avoided
local exemple_context2 = {
type = "offline_player",
name = "playername",
object = player, --is a valid object if the player is online
op_level = get_player_op_level(player),
pos = player_pos,
}
local exemple_context3 = {
type = "mod", --required
name = "mcl_redstone:commandblock", --valid nodename
op_level = commandblock_op_level, --required
pos = node_pos,
}
mcl_commands.register_command("test", {
description = S("testing command"),
def = {
{type="player", name="player"}, --player object (got by name)
require = 4, --required OP level
execute = function(context, params) --function executed
return true, params.player
end,
},
})
--the command actually registered in minetest.registered_command will be a wrapper around this function
mcl_commands.call_command("test", "@a grant all", exemple_context3)

View File

@ -0,0 +1,16 @@
local S = minetest.get_translator(minetest.get_current_modname())
--[[minetest.register_chatcommand("banlist", {
description = S("List bans"),
privs = minetest.registered_chatcommands["ban"].privs,
func = function(name)
return true, S("Ban list: @1", minetest.get_ban_list())
end,
})]]
mcl_commands.register_basic_command("banlist", {
description = S("List bans"),
func = function(name)
return true, S("Ban list: @1", minetest.get_ban_list())
end,
})

View File

@ -0,0 +1,52 @@
local S = minetest.get_translator(minetest.get_current_modname())
--[[
minetest.register_chatcommand("list", {
description = S("Show who is logged on"),
params = "",
privs = {},
func = function(name)
--local players = ""
--for _, player in ipairs(minetest.get_connected_players()) do
-- players = players..player:get_player_name().."\n"
--end
--minetest.chat_send_player(name, players)
local player_list = minetest.get_connected_players()
local header = S("There are @1/@2 players online:", #player_list, minetest.settings:get("max_users") or "unknown").."\n"
local players = {}
for _, player in ipairs(player_list) do
table.insert(players, player:get_player_name())
end
return true, header..table.concat(players, ", ")
end
})
]]
local max_users = minetest.settings:get("max_users") or "unknown" --TODO: check if the setting is dynamic in mc
local playersstring = ""
local function generate_player_list()
local player_list = minetest.get_connected_players()
local header = S("There are @1/@2 players online:", #player_list, max_users).."\n"
local players = {}
for _, player in ipairs(player_list) do
table.insert(players, player:get_player_name())
end
playersstring = header..table.concat(players, ", ")
end
minetest.register_on_joinplayer(function(player)
generate_player_list()
end)
minetest.register_on_leaveplayer(function(player)
generate_player_list()
end)
mcl_commands.register_basic_command("list", {
description = S("Show who is logged on"),
params = nil,
func = function(name)
return true, playersstring
end,
})

View File

@ -1,10 +1,12 @@
local S = minetest.get_translator(minetest.get_current_modname())
local seed = minetest.get_mapgen_setting("seed")
minetest.register_chatcommand("seed", {
description = S("Displays the world seed"),
params = "",
privs = {},
func = function(name)
minetest.chat_send_player(name, "Seed: "..minetest.get_mapgen_setting("seed"))
minetest.chat_send_player(name, "Seed: "..seed)
end
})

View File

@ -0,0 +1,67 @@
--Similar to the StringParser class of mojang Brigadier library
local string = string
local SYNTAX_DOUBLE_QUOTE = "\""
local SYNTAX_SINGLE_QUOTE = "\'"
local StringParser = {}
function StringParser:init(str)
self.cursor = 1
self.str = str
end
function StringParser:get_string()
return self.str
end
function StringParser:set_cursor(cursor)
self.cursor = cursor
end
function StringParser:get_cursor()
return self.cursor
end
function StringParser:get_remaining_length()
return string.len(self.str) - self.cursor
end
function StringParser:get_total_length()
return string.len(self.str)
end
function StringParser:get_read()
return string.sub(self.str, 1, self.cursor)
end
function StringParser:get_remaining()
return string.sub(self.str, self.cursor)
end
function StringParser:can_read(length)
return (self.cursor + (length or 1)) <= string.len(self.str)
end
function StringParser:peek(offset)
return string.sub(self.cursor + (offset or 0), self.cursor + (offset or 0))
end
function StringParser:read()
local r = string.sub(self.cursor, self.cursor)
self.cursor = self.cursor + 1
return r
end
function StringParser:skip()
self.cursor = self.cursor + 1
end
function StringParser.is_allowed_number()
return false
end
function StringParser.is_quoted_string_start(char)
return char == SYNTAX_DOUBLE_QUOTE or char == SYNTAX_SINGLE_QUOTE
end

View File

@ -0,0 +1,9 @@
mcl_commands.register_command("test1", {
description = "testing command 1",
def = {
{type="player", name="t"},
execute = function(context, params, subcommand_params)
return true, dump(context)
end,
},
})

View File

@ -1,30 +0,0 @@
local S = minetest.get_translator(minetest.get_current_modname())
local function register_chatcommand_alias(alias, cmd)
local def = minetest.chatcommands[cmd]
minetest.register_chatcommand(alias, def)
end
local function rename_chatcommand(newname, cmd)
local def = minetest.chatcommands[cmd]
minetest.register_chatcommand(newname, def)
minetest.unregister_chatcommand(cmd)
end
if minetest.settings:get_bool("mcl_builtin_commands_overide", true) then
register_chatcommand_alias("?", "help")
register_chatcommand_alias("pardon", "unban")
rename_chatcommand("stop", "shutdown")
register_chatcommand_alias("tell", "msg")
register_chatcommand_alias("w", "msg")
register_chatcommand_alias("tp", "teleport")
rename_chatcommand("clear", "clearinv")
minetest.register_chatcommand("banlist", {
description = S("List bans"),
privs = minetest.chatcommands["ban"].privs,
func = function(name)
return true, S("Ban list: @1", minetest.get_ban_list())
end,
})
end

View File

@ -1,11 +0,0 @@
local modpath = minetest.get_modpath(minetest.get_current_modname())
dofile(modpath.."/kill.lua")
dofile(modpath.."/setblock.lua")
dofile(modpath.."/seed.lua")
dofile(modpath.."/summon.lua")
dofile(modpath.."/say.lua")
dofile(modpath.."/list.lua")
dofile(modpath.."/sound.lua")
dofile(modpath.."/alias.lua")

View File

@ -1,14 +0,0 @@
local S = minetest.get_translator(minetest.get_current_modname())
minetest.register_chatcommand("list", {
description = S("Show who is logged on"),
params = "",
privs = {},
func = function(name)
local players = ""
for _, player in ipairs(minetest.get_connected_players()) do
players = players..player:get_player_name().."\n"
end
minetest.chat_send_player(name, players)
end
})

View File

@ -6,15 +6,8 @@ mcl_death_drop = {}
mcl_death_drop.registered_dropped_lists = {}
function mcl_death_drop.register_dropped_list(inv, listname, drop)
table.insert(mcl_death_drop.registered_dropped_lists, {inv = inv, listname = listname, drop = drop})
end
mcl_death_drop.register_dropped_list("PLAYER", "main", true)
mcl_death_drop.register_dropped_list("PLAYER", "craft", true)
mcl_death_drop.register_dropped_list("PLAYER", "armor", true)
minetest.register_on_dieplayer(function(player)
--This function can be overiden by mods
function mcl_death_drop.handler(player)
local keep = minetest.settings:get_bool("mcl_keepInventory", false)
if keep == false then
-- Drop inventory, crafting grid and armor
@ -53,4 +46,16 @@ minetest.register_on_dieplayer(function(player)
end
mcl_armor.update(player)
end
end
function mcl_death_drop.register_dropped_list(inv, listname, drop)
table.insert(mcl_death_drop.registered_dropped_lists, {inv = inv, listname = listname, drop = drop})
end
mcl_death_drop.register_dropped_list("PLAYER", "main", true)
mcl_death_drop.register_dropped_list("PLAYER", "craft", true)
mcl_death_drop.register_dropped_list("PLAYER", "armor", true)
minetest.register_on_dieplayer(function(player)
mcl_death_drop.handler(player)
end)