Add chatcmdbuilder
This commit is contained in:
parent
730f0906de
commit
3f552c3ef8
80
chatcmdbuilder/.gitignore
vendored
Normal file
80
chatcmdbuilder/.gitignore
vendored
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
|
||||||
|
# Created by https://www.gitignore.io/api/lin,linux,windows,lua
|
||||||
|
|
||||||
|
#!! ERROR: lin is undefined. Use list command to see defined gitignore types !!#
|
||||||
|
|
||||||
|
### Linux ###
|
||||||
|
*~
|
||||||
|
|
||||||
|
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||||
|
.fuse_hidden*
|
||||||
|
|
||||||
|
# KDE directory preferences
|
||||||
|
.directory
|
||||||
|
|
||||||
|
# Linux trash folder which might appear on any partition or disk
|
||||||
|
.Trash-*
|
||||||
|
|
||||||
|
|
||||||
|
### Windows ###
|
||||||
|
# Windows image file caches
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
Desktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Windows Installer files
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# Windows shortcuts
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
|
||||||
|
### Lua ###
|
||||||
|
# Compiled Lua sources
|
||||||
|
luac.out
|
||||||
|
|
||||||
|
# luarocks build files
|
||||||
|
*.src.rock
|
||||||
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
|
|
||||||
|
# Object files
|
||||||
|
*.o
|
||||||
|
*.os
|
||||||
|
*.ko
|
||||||
|
*.obj
|
||||||
|
*.elf
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Libraries
|
||||||
|
*.lib
|
||||||
|
*.a
|
||||||
|
*.la
|
||||||
|
*.lo
|
||||||
|
*.def
|
||||||
|
*.exp
|
||||||
|
|
||||||
|
# Shared objects (inc. Windows DLLs)
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
*.i*86
|
||||||
|
*.x86_64
|
||||||
|
*.hex
|
19
chatcmdbuilder/LICENSE
Normal file
19
chatcmdbuilder/LICENSE
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2016-17 rubenwardy
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
89
chatcmdbuilder/README.md
Normal file
89
chatcmdbuilder/README.md
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
# ChatCmdBuilder
|
||||||
|
|
||||||
|
Easily create complex chat commands with no regex.
|
||||||
|
Created by rubenwardy
|
||||||
|
License: MIT
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
## Registering Chat Commands
|
||||||
|
|
||||||
|
`ChatCmdBuilder.new(name, setup)` registers a new chat command called `name`.
|
||||||
|
Setup is called immediately after calling `new` to initialise subcommands.
|
||||||
|
|
||||||
|
You can set values in the chat command definition by using def:
|
||||||
|
`ChatCmdBuilder.new(name, setup, def)`.
|
||||||
|
|
||||||
|
Here is an example:
|
||||||
|
|
||||||
|
```Lua
|
||||||
|
ChatCmdBuilder.new("admin", function(cmd)
|
||||||
|
cmd:sub("kill :target", function(name, target)
|
||||||
|
local player = minetest.get_player_by_name(target)
|
||||||
|
if player then
|
||||||
|
player:set_hp(0)
|
||||||
|
return true, "Killed " .. target
|
||||||
|
else
|
||||||
|
return false, "Unable to find " .. target
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
cmd:sub("move :target to :pos:pos", function(name, target, pos)
|
||||||
|
local player = minetest.get_player_by_name(target)
|
||||||
|
if player then
|
||||||
|
player:setpos(pos)
|
||||||
|
return true, "Moved " .. target .. " to " .. minetest.pos_to_string(pos)
|
||||||
|
else
|
||||||
|
return false, "Unable to find " .. target
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end, {
|
||||||
|
description = "Admin tools",
|
||||||
|
privs = {
|
||||||
|
kick = true,
|
||||||
|
ban = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
A player could then do `/admin kill player1` to kill player1,
|
||||||
|
or `/admin move player1 to 0,0,0` to teleport a user.
|
||||||
|
|
||||||
|
## Introduction to Routing
|
||||||
|
|
||||||
|
A route is a string. Let's look at `move :target to :pos:pos`:
|
||||||
|
|
||||||
|
* `move` and `to` are constants. They need to be there in order to match.
|
||||||
|
* `:target` and `:pos:pos` are parameters. They're passed to the function.
|
||||||
|
* The second `pos` in `:pos:pos` after `:` is the param type. `:target` has an implicit
|
||||||
|
type of `word`.
|
||||||
|
|
||||||
|
## Param Types
|
||||||
|
|
||||||
|
* `word` - default. Any string without spaces.
|
||||||
|
* `number` - Any number, including decimals
|
||||||
|
* `int` - Any integer, no decimals
|
||||||
|
* `text` - Any string
|
||||||
|
* `pos` - 1,2,3 or 1.1,2,3.4567 or (1,2,3) or 1.2, 2 ,3.2
|
||||||
|
|
||||||
|
## Build chat command function
|
||||||
|
|
||||||
|
If you don't want to register the chatcommand at this point, you can just generate
|
||||||
|
a function using `ChatCmdBuilder.build`.
|
||||||
|
|
||||||
|
For example, this is the full definition of ChatCmdBuilder.new:
|
||||||
|
|
||||||
|
```Lua
|
||||||
|
function ChatCmdBuilder.new(name, func, def)
|
||||||
|
def = def or {}
|
||||||
|
def.func = ChatCmdBuilder.build(name, func)
|
||||||
|
minetest.register_chatcommand(name, def)
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Run tests
|
||||||
|
|
||||||
|
```Bash
|
||||||
|
sudo apt-get install luajit
|
||||||
|
luajit init.lua
|
||||||
|
```
|
1
chatcmdbuilder/description.txt
Normal file
1
chatcmdbuilder/description.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
A library to make registering chat commands easier
|
230
chatcmdbuilder/init.lua
Normal file
230
chatcmdbuilder/init.lua
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
ChatCmdBuilder = {}
|
||||||
|
|
||||||
|
function ChatCmdBuilder.new(name, func, def)
|
||||||
|
def = def or {}
|
||||||
|
local cmd = ChatCmdBuilder.build(func)
|
||||||
|
cmd.def = def
|
||||||
|
def.func = cmd.run
|
||||||
|
minetest.register_chatcommand(name, def)
|
||||||
|
return cmd
|
||||||
|
end
|
||||||
|
|
||||||
|
local STATE_READY = 1
|
||||||
|
local STATE_PARAM = 2
|
||||||
|
local STATE_PARAM_TYPE = 3
|
||||||
|
local bad_chars = {}
|
||||||
|
bad_chars["("] = true
|
||||||
|
bad_chars[")"] = true
|
||||||
|
bad_chars["."] = true
|
||||||
|
bad_chars["%"] = true
|
||||||
|
bad_chars["+"] = true
|
||||||
|
bad_chars["-"] = true
|
||||||
|
bad_chars["*"] = true
|
||||||
|
bad_chars["?"] = true
|
||||||
|
bad_chars["["] = true
|
||||||
|
bad_chars["^"] = true
|
||||||
|
bad_chars["$"] = true
|
||||||
|
local function escape(char)
|
||||||
|
if bad_chars[char] then
|
||||||
|
return "%" .. char
|
||||||
|
else
|
||||||
|
return char
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ChatCmdBuilder.build(func)
|
||||||
|
local cmd = {
|
||||||
|
_subs = {}
|
||||||
|
}
|
||||||
|
function cmd:sub(route, func, def)
|
||||||
|
print("Parsing " .. route)
|
||||||
|
|
||||||
|
def = def or {}
|
||||||
|
if string.trim then
|
||||||
|
route = string.trim(route)
|
||||||
|
end
|
||||||
|
|
||||||
|
local sub = {
|
||||||
|
pattern = "^",
|
||||||
|
params = {},
|
||||||
|
func = func
|
||||||
|
}
|
||||||
|
|
||||||
|
-- End of param reached: add it to the pattern
|
||||||
|
local param = ""
|
||||||
|
local param_type = ""
|
||||||
|
local should_be_eos = false
|
||||||
|
local function finishParam()
|
||||||
|
if param ~= "" and param_type ~= "" then
|
||||||
|
print(" - Found param " .. param .. " type " .. param_type)
|
||||||
|
|
||||||
|
if param_type == "pos" then
|
||||||
|
sub.pattern = sub.pattern .. "%(? *(%-?[%d.]+) *, *(%-?[%d.]+) *, *(%-?[%d.]+) *%)?"
|
||||||
|
elseif param_type == "text" then
|
||||||
|
sub.pattern = sub.pattern .. "(*+)"
|
||||||
|
should_be_eos = true
|
||||||
|
elseif param_type == "number" then
|
||||||
|
sub.pattern = sub.pattern .. "([%d.]+)"
|
||||||
|
elseif param_type == "int" then
|
||||||
|
sub.pattern = sub.pattern .. "([%d]+)"
|
||||||
|
else
|
||||||
|
if param_type ~= "word" then
|
||||||
|
print("Unrecognised param_type=" .. param_type .. ", using 'word' type instead")
|
||||||
|
param_type = "word"
|
||||||
|
end
|
||||||
|
sub.pattern = sub.pattern .. "([^ ]+)"
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(sub.params, param_type)
|
||||||
|
|
||||||
|
param = ""
|
||||||
|
param_type = ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Iterate through the route to find params
|
||||||
|
local state = STATE_READY
|
||||||
|
for i = 1, #route do
|
||||||
|
local c = route:sub(i, i)
|
||||||
|
if should_be_eos then
|
||||||
|
error("Should be end of string. Nothing is allowed after a param of type text.")
|
||||||
|
end
|
||||||
|
|
||||||
|
if state == STATE_READY then
|
||||||
|
if c == ":" then
|
||||||
|
print(" - Found :, entering param")
|
||||||
|
state = STATE_PARAM
|
||||||
|
param_type = "word"
|
||||||
|
else
|
||||||
|
sub.pattern = sub.pattern .. escape(c)
|
||||||
|
end
|
||||||
|
elseif state == STATE_PARAM then
|
||||||
|
if c == ":" then
|
||||||
|
print(" - Found :, entering param type")
|
||||||
|
state = STATE_PARAM_TYPE
|
||||||
|
param_type = ""
|
||||||
|
elseif c:match("%W") then
|
||||||
|
print(" - Found nonalphanum, leaving param")
|
||||||
|
state = STATE_READY
|
||||||
|
finishParam()
|
||||||
|
sub.pattern = sub.pattern .. escape(c)
|
||||||
|
else
|
||||||
|
param = param .. c
|
||||||
|
end
|
||||||
|
elseif state == STATE_PARAM_TYPE then
|
||||||
|
if c:match("%W") then
|
||||||
|
print(" - Found nonalphanum, leaving param type")
|
||||||
|
state = STATE_READY
|
||||||
|
finishParam()
|
||||||
|
sub.pattern = sub.pattern .. escape(c)
|
||||||
|
else
|
||||||
|
param_type = param_type .. c
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print(" - End of route")
|
||||||
|
finishParam()
|
||||||
|
sub.pattern = sub.pattern .. "$"
|
||||||
|
print("Pattern: " .. sub.pattern)
|
||||||
|
|
||||||
|
table.insert(self._subs, sub)
|
||||||
|
end
|
||||||
|
|
||||||
|
if func then
|
||||||
|
func(cmd)
|
||||||
|
end
|
||||||
|
|
||||||
|
cmd.run = function(name, param)
|
||||||
|
for i = 1, #cmd._subs do
|
||||||
|
local sub = cmd._subs[i]
|
||||||
|
local res = { string.match(param, sub.pattern) }
|
||||||
|
if #res > 0 then
|
||||||
|
local pointer = 1
|
||||||
|
local params = { name }
|
||||||
|
for j = 1, #sub.params do
|
||||||
|
local param = sub.params[j]
|
||||||
|
if param == "pos" then
|
||||||
|
local pos = {
|
||||||
|
x = tonumber(res[pointer]),
|
||||||
|
y = tonumber(res[pointer + 1]),
|
||||||
|
z = tonumber(res[pointer + 2])
|
||||||
|
}
|
||||||
|
table.insert(params, pos)
|
||||||
|
pointer = pointer + 3
|
||||||
|
elseif param == "number" or param == "int" then
|
||||||
|
table.insert(params, tonumber(res[pointer]))
|
||||||
|
pointer = pointer + 1
|
||||||
|
else
|
||||||
|
table.insert(params, res[pointer])
|
||||||
|
pointer = pointer + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return sub.func(unpack(params))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print("No matches")
|
||||||
|
end
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
end
|
||||||
|
|
||||||
|
local function run_tests()
|
||||||
|
if not (ChatCmdBuilder.build(function(cmd)
|
||||||
|
cmd:sub("bar :one and :two:word", function(name, one, two)
|
||||||
|
if name == "singleplayer" and one == "abc" and two == "def" then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end))("singleplayer", "bar abc and def") then
|
||||||
|
error("Test 1 failed")
|
||||||
|
end
|
||||||
|
|
||||||
|
local move = ChatCmdBuilder.build(function(cmd)
|
||||||
|
cmd:sub("move :target to :pos:pos", function(name, target, pos)
|
||||||
|
if name == "singleplayer" and target == "player1" and
|
||||||
|
pos.x == 0 and pos.y == 1 and pos.z == 2 then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
if not move("singleplayer", "move player1 to 0,1,2") then
|
||||||
|
error("Test 2 failed")
|
||||||
|
end
|
||||||
|
if not move("singleplayer", "move player1 to (0,1,2)") then
|
||||||
|
error("Test 3 failed")
|
||||||
|
end
|
||||||
|
if not move("singleplayer", "move player1 to 0, 1,2") then
|
||||||
|
error("Test 4 failed")
|
||||||
|
end
|
||||||
|
if not move("singleplayer", "move player1 to 0 ,1, 2") then
|
||||||
|
error("Test 5 failed")
|
||||||
|
end
|
||||||
|
if not move("singleplayer", "move player1 to 0, 1, 2") then
|
||||||
|
error("Test 6 failed")
|
||||||
|
end
|
||||||
|
if not move("singleplayer", "move player1 to 0 ,1 ,2") then
|
||||||
|
error("Test 7 failed")
|
||||||
|
end
|
||||||
|
if not move("singleplayer", "move player1 to ( 0 ,1 ,2)") then
|
||||||
|
error("Test 7 failed")
|
||||||
|
end
|
||||||
|
if move("singleplayer", "move player1 to abc,def,sdosd") then
|
||||||
|
error("Test 8 failed")
|
||||||
|
end
|
||||||
|
if move("singleplayer", "move player1 to abc def sdosd") then
|
||||||
|
error("Test 8 failed")
|
||||||
|
end
|
||||||
|
|
||||||
|
if not (ChatCmdBuilder.build(function(cmd)
|
||||||
|
cmd:sub("does :one:int plus :two:int equal :three:int", function(name, one, two, three)
|
||||||
|
if name == "singleplayer" and one + two == three then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end))("singleplayer", "does 1 plus 2 equal 3") then
|
||||||
|
error("Test 9 failed")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not minetest then
|
||||||
|
run_tests()
|
||||||
|
end
|
7
chatcmdbuilder/mod.conf
Normal file
7
chatcmdbuilder/mod.conf
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
name = lib_chatcmdbuilder
|
||||||
|
title = Chat Command Builder
|
||||||
|
author = rubenwardy
|
||||||
|
description = A library to make registering chat commands easier
|
||||||
|
license = MIT
|
||||||
|
forum = https://forum.minetest.net/viewtopic.php?t=14899
|
||||||
|
version = 0.1.0
|
Loading…
x
Reference in New Issue
Block a user