From 3d6b29994f30c445b8f804e51574c264f69af360 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Fri, 17 Jun 2016 01:08:44 +0100 Subject: [PATCH] Complex chat commands --- _data/links.yml | 18 +++-- chapters/chat.md | 15 ++++ chapters/chat_complex.md | 161 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 187 insertions(+), 7 deletions(-) create mode 100644 chapters/chat_complex.md diff --git a/_data/links.yml b/_data/links.yml index 9846485..c9b9d14 100644 --- a/_data/links.yml +++ b/_data/links.yml @@ -43,36 +43,40 @@ num: 9 link: chapters/chat.html -- title: Player Physics +- title: Complex Chat Commands num: 10 + link: chapters/chat_complex.html + +- title: Player Physics + num: 11 link: chapters/player_physics.html - title: Formspecs - num: 11 + num: 12 link: chapters/formspecs.html - title: HUD - num: 12 + num: 13 link: chapters/hud.html - hr: true - title: ItemStacks - num: 13 + num: 14 link: chapters/itemstacks.html - title: Inventories - num: 14 + num: 15 link: chapters/inventories.html - hr: true - title: Releasing a Mod - num: 15 + num: 16 link: chapters/releasing.html - title: Read More - num: 16 + num: 17 link: chapters/readmore.html - hr: true diff --git a/chapters/chat.md b/chapters/chat.md index 78eed17..83d162e 100644 --- a/chapters/chat.md +++ b/chapters/chat.md @@ -12,6 +12,7 @@ sending messages, intercepting messages and registering chat commands. * Send a message to all players. * Send a message to a certain player. * Chat commands. +* Complex subcommands. * Intercepting messages. ## Send a message to all players @@ -95,6 +96,20 @@ or any other function that requires an ingame player. `minetest.show_formspec` w not work for IRC players, so you should provide a text only version. For example, the email mod allows both `/inbox` to show the formspec, and `/inbox text` to send to chat. +## Complex subcommands + +It is often required to make complex chat commands, such as: + +* /msg +* /team join +* /team leave +* /team list + +Traditionally mods implemented this using Lua patterns. However, a much easier +way is to use a mod library that I wrote to do this for you. +See [Complex Chat Commands](chat_complex.html). + + ## Intercepting messages You can use register_on_chat_message, like so: diff --git a/chapters/chat_complex.md b/chapters/chat_complex.md new file mode 100644 index 0000000..5f5405f --- /dev/null +++ b/chapters/chat_complex.md @@ -0,0 +1,161 @@ +--- +title: Complex Chat Commands +layout: default +root: ../ +--- + +## Introduction + +This chapter will show you how to make complex chat commands, such as +`/msg `, `/team join ` or `/team leave `. + +## Why ChatCmdBuilder? + +Traditionally mods implemented these complex commands using Lua patterns. +I however find Lua patterns annoying to write and unreadable. +Because of this, I created a library to do this for you. + +{% highlight lua %} +ChatCmdBuilder.new("sethp", function(cmd) + cmd:sub(":target :hp:int", function(name, target, hp) + local player = minetest.get_player_by_name(target) + if player then + player:set_hp(hp) + return true, "Killed " .. target + else + return false, "Unable to find " .. target + end + end) +end, { + description = "Set hp of player", + privs = { + kick = true + -- ^ probably better to register a custom priv + } +}) +{% endhighlight %} + +`ChatCmdBuilder.new(name, setup_func, def)` creates a new chat command called +`name`. It then calls the function passed to it (`setup_func`), which then creates +sub commands. Each `cmd:sub(route, func)` is a sub command. + +A sub command is a particular response to an input param. When a player runs +the chat command, the first sub command that matches their input will be run, +and no others. If no subcommands match then the user will be told of the invalid +syntax. For example, in the above code snippet if a player +types something of the form `/sethp username 12` then the function passed +to cmd:sub will be called. If they type `/sethp 12 bleh` then a wrong +input message will appear. + +`:name :hp:int` is a route. It describes the format of the param passed to /teleport. + +## Routes + +A route is made up of terminals and variables. Terminals must always be there. +For example, `join` in `/team join :username :teamname`. The spaces also count +as terminals. + +Variables can change value depending on what the user types. For example, `:username` +and `:teamname`. + +Variables are defined as `:name:type`. The `name` is used in the help documention. +The `type` is used to match the input. If the type is not given, then the type is +`word`. + +Valid types are: + +* `word` - default. Any string without spaces. +* `int` - Any integer/whole number, no decimals. +* `number` - Any number, including ints and decimals. +* `pos` - 1,2,3 or 1.1,2,3.4567 or (1,2,3) or 1.2, 2 ,3.2 +* `text` - Any string. There can only ever be one text variable, + no variables or terminals can come afterwards. + +In `:name :hp:int`, there are two variables there: + +* `name` - type of `word` as no type is specified. Accepts any string without spaces. +* `hp` - type of `int` + +## Subcommand functions + +The first argument is the caller's name. The variables are then passed to the +function in order. + +{% highlight lua %} +cmd:sub(":target :hp:int", function(name, target, hp) + -- subcommand function +end) +{% endhighlight %} + +## Installing ChatCmdBuilder + +There are two ways to install: + +1. Install ChatCmdBuilder as a mod and depend on it. +2. Include the init.lua file in ChatCmdBuilder as chatcmdbuilder.lua in your mod, + and dofile it. + +## Admin complex command + +Here is an example that creates a chat command that allows us to do this: + +* `/admin kill ` - kill user +* `/admin move to ` - teleport user +* `/admin log ` - show report log +* `/admin log ` - log to report log + +{% highlight lua %} +local admin_log +local function load() + admin_log = {} +end +local function save() + -- todo +end +load() + +ChatCmdBuilder.new("admin", function(cmd) + cmd:sub("kill :name", 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 :name 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) + + cmd:sub("log :username", function(name, target) + local log = admin_log[target] + if log then + return true, table.concat(log, "\n") + else + return false, "No entries for " .. target + end + end) + + cmd:sub("log :username :message", function(name, target, message) + local log = admin_log[target] or {} + table.insert(log, message) + admin_log[target] = log + save() + return true, "Logged" + end) +end, { + description = "Admin tools", + privs = { + kick = true, + ban = true + } +}) +{% endhighlight %}