-
Notifications
You must be signed in to change notification settings - Fork 35
Commands
(:require [cljminecraft.commands :as cmd])
Source: commands.clj
The command framework in cljminecraft builds on top of the Bukkit commands, but implements a more succinct way of providing parameter types (for autocompletion and conversion) and has an open system for you to add your own.
It also automatically handles arity checking to make sure your function is passed the correct number of arguments as well as the correct types and gracefully dies on exceptions (ie, don't boot a player out of the server!) - See: http://wiki.bukkit.org/Plugin_Tutorial#Writing_a_safe_onCommand
First, you need to declare all your commands in your plugin.yml file as describes here: http://wiki.bukkit.org/Plugin_Tutorial#Adding_your_Command_to_the_Plugin.yml
Once you have that down you can create a function to handle the command and register it:
(defn give-money [sender receiver amount]
{:msg (format "Taking %s money from %s and giving to %s" amount sender receiver)})
(cmd/register-command plugin "transfer" #'give-money :player :int)
You'll notice that the give-money
function above returns a map {:msg ....}, this will automatically be seen as a response to the sender so that you don't need to explicitly send the sender the message with something like (respond sender "..")
, thereby letting the framework decide whether it can actually respond or not and handle it gracefully.
register-command
expects the plugin (which you obtain from your plugin's start
function), the command name, a callback function and a variable number of argument types.
If any of the arguments are a vector, the first item will be the type and the second will be the finite options for tabcompletions, for instance: If you wanted a /server start|stop command, you'd write something like:
(cmd/register-command plugin "server" #'server [:keyword [:start :stop]])
Keywords are generally preferred if you're going to deal with it directly in code, but if you're going to use it for something a list of names:
(cmd/register-command plugin "strangecommand" #'strange [:string ["Bob" "Jim" "Mary"]])
The arguments for a command automatically convert to it's correct class, for instance, a :player argument will get to the command function as a Player object and similar for materials, entities, longs, ints and keyword
:player
as an argument will autocomplete and convert to any online player
:material
will autocomplete and convert to the corresponding MaterialData involved, see [Item functions](Item functions)
:event
will autocomplete to the string representation of any of the event types, see [Event handling](Event handling)
:entity
will autocomplete and convert to any of the entity types, see [Entity handling](Entity handling)
:permission
will autocomplete to any of the declared permissions (in the permission: and command: blocks) of all the plugins on the server.
The other types (:string
, :long
, :double
, :int
) don't autocomplete unless you provide it with a finite set, eg [:string ["Bob" "Joe"]] or [:int [1000 2000]]
To extend the argument types to provide your own tab completion for your specific plugin is a matter of implementing two multimethods: cmd/convert-type
and cmd/param-type-tabcomplete
Imagine you keep track of a list of players that are special in some way and you want to only autocomplete by that list, maybe special-players
. You could do this:
(def special-players (atom #{}))
(defmethod cmd/convert-type :special-player [sender type arg]
(if (contains? @special-players arg)
(plr/get-player arg)))
(defmethod cmd/param-type-tabcomplete :special-player [sender type arg]
(filter #(.startsWith % arg) @special-players))
That would let you (and every other clojure plugin) register a command using it like so:
(cmd/register-command plugin "lookup-special" #'lookup-special :special-player)
And you're done.