Permalink
Browse files

Resolves #7 - Wrapper for Commands

The tab completion and type conversion now also sends the sender along so that other plugins can contribute better context sensitive completion/conversion
Fixed a bit of an oversight that command dispatch wasn't sending the sender to the command function.
Added a clj.tabtest command as a demo/test of the tab completions possible
Fixed the repl command to use the configured repl.port instead of hardcoded 4005
  • Loading branch information...
1 parent f9b16db commit 599a66ea24bda376bf4c95ea743e3ac1ac5490a5 @CmdrDats committed Dec 8, 2012
Showing with 77 additions and 34 deletions.
  1. +3 −0 README.md
  2. +1 −1 src/cljminecraft/bukkit.clj
  3. +56 −28 src/cljminecraft/commands.clj
  4. +10 −3 src/cljminecraft/core.clj
  5. +2 −1 src/cljminecraft/util.clj
  6. +5 −1 src/plugin.yml
View
@@ -23,6 +23,9 @@ understand what changes are made and adjust your plugins accordingly.
Changelog:
+08 December 2012:
+ - Finished work on Commands
+
07 December 2012:
- Lots of work on the command abstractions:
o Common type autocompletion
@@ -19,7 +19,7 @@
(.getWorlds (server)))
(defn online-players []
- (.getOnlinePlayers (server)))
+ (seq (.getOnlinePlayers (server))))
(defn broadcast [fmt & args]
(.broadcastMessage (server) (apply format fmt args)))
@@ -7,52 +7,80 @@
(defn respond
[sender fmt & args]
- ;; This needs tweaking to handle responding with info if sender is console?
- (plr/send-msg sender fmt args))
+ (.sendMessage sender (apply format fmt args)))
-(defmulti convert-type (fn [x _] (if (coll? x) (first x) x)))
+(defmulti convert-type (fn [_ x _] (if (coll? x) (first x) x)))
-(defmethod convert-type :string [type arg] arg)
+(defmethod convert-type :string [sender type arg] arg)
-(defmethod convert-type :int [type arg]
+(defmethod convert-type :int [sender type arg]
(try
(Integer/parseInt arg)
- (catch Exception e nil)))
+ (catch Exception e (util/throw-runtime "Invalid integer: %s" arg))))
-(defmethod convert-type :player [type arg]
- (plr/get-player arg))
+(defmethod convert-type :player [sender type arg]
+ (let [result (plr/get-player arg)]
+ (if (nil? result) (util/throw-runtime "Invalid player: %s" arg)
+ result)))
-(defmethod convert-type :material [type arg]
- (get plr/materials (keyword arg)))
+(defmethod convert-type :material [sender type arg]
+ (let [result (get plr/materials (keyword arg))]
+ (if (nil? result) (util/throw-runtime "Invalid material: %s" arg)
+ result)))
-(defmethod convert-type :long [type arg]
+(defmethod convert-type :long [sender type arg]
(try
(Long/parseLong arg)
- (catch Exception e nil)))
+ (catch Exception e (util/throw-runtime "Invalid long: %s" arg))))
-(defmethod convert-type :double [type arg]
+(defmethod convert-type :double [sender type arg]
(try
(Double/parseDouble arg)
- (catch Exception e nil)))
+ (catch Exception e (util/throw-runtime "Invalid double: %s" arg))))
-(defmethod convert-type :keyword [type arg]
- (log/info "Keywording %s" arg)
+(defmethod convert-type :keyword [sender type arg]
(keyword arg))
-(defmethod convert-type :default [type arg]
- (log/info "Default conversion of %s %s" type arg)
+(defmethod convert-type :default [sender type arg]
(str arg))
-(defn command [f param-types sender command alias args]
- (log/info "Running command %s" command)
- (let [converted (map convert-type param-types args)
- {:keys [msg] :as response} (apply f converted)]
- (if msg (respond sender msg))) true)
+(defn arity-split [args]
+ (split-with #(not= '& %) args))
-(defmulti param-type-tabcomplete (fn [x _] x))
+(defn arity-count-match [cnt args]
+ (let [[req opt] (arity-split args)]
+ (if (empty? opt)
+ (= cnt (count req))
+ (>= cnt (count req)))))
-(defmethod param-type-tabcomplete :player [type arg]
- ["hello"])
+(defn check-arity [f arg-count]
+ (let [arglists (:arglists (meta f))
+ count-matched (map (partial #'arity-count-match arg-count) arglists)]
+ (empty? (filter false? count-matched))))
+
+(defn command [f param-types sender command alias args]
+ (log/info "Running command %s" command)
+ (try
+ (cond
+ (not (check-arity f (count (concat [sender] args)))) (respond sender "Incorrect number of arguments for %s: %d" alias (count args))
+ :else
+ (let [converted (map (partial convert-type sender) param-types args)
+ {:keys [msg] :as response} (apply f sender converted)]
+ (if msg (respond sender msg))))
+ (catch RuntimeException e (respond sender (.getMessage e))))
+ true)
+
+(defmulti param-type-tabcomplete (fn [_ x _] x))
+
+(defmethod param-type-tabcomplete :player [sender type arg]
+ (let [lower (.toLowerCase arg)]
+ (map #(.getDisplayName %)
+ (filter #(.startsWith (.toLowerCase (org.bukkit.ChatColor/stripColor (.getDisplayName %))) lower)
+ (bk/online-players)))))
+
+(defmethod param-type-tabcomplete :material [sender type arg]
+ (let [lower (.toLowerCase arg)]
+ (filter #(.startsWith % lower) (map name (keys plr/materials)))))
(defn to-string-seq [a]
(for [i (seq a)] (if (keyword? i) (name i) (str i))))
@@ -66,8 +94,8 @@
(cond
(nil? param) nil
(coll? opts) (to-string-seq opts)
- (fn? opts) (opts sender command alias args)
- :else (param-type-tabcomplete (or type param) (last args)))))
+ (or (var? opts) (fn? opts)) (opts sender command alias args)
+ :else (param-type-tabcomplete sender (or type param) (last args)))))
(defn build-executor [f param-types]
(proxy [TabExecutor] []
View
@@ -47,17 +47,24 @@
(defonce clj-plugin (atom nil))
-(defn repl-command [cmd & [port]]
+(defn repl-command [sender cmd & [port]]
(log/info "Running repl command with %s %s" cmd port)
(case cmd
- :start (start-repl "0.0.0.0" (or port 4005))
+ :start (start-repl "0.0.0.0" (or port (cfg/get-int @clj-plugin "repl.port")))
:stop (stop-repl)))
+(defn tabtest-command [sender & args]
+ (.sendMessage sender (apply str args)))
+
+(defn tabcomplete-reverse-first [sender command alias args]
+ [(apply str (reverse (first args)))])
+
(defn start
"onEnable cljminecraft"
[plugin]
(reset! clj-plugin plugin)
- (cmd/register-command @clj-plugin "repl" #'repl-command [:keyword [:start :stop]] [:int [4005]])
+ (cmd/register-command @clj-plugin "clj.repl" #'repl-command [:keyword [:start :stop]] [:int [(cfg/get-int plugin "repl.port")]])
+ (cmd/register-command @clj-plugin "clj.tabtest" #'tabtest-command :player :material [:keyword [:start :stop]] [:string #'tabcomplete-reverse-first])
(start-repl-if-needed plugin))
(defn stop
@@ -63,4 +63,5 @@
)
)
-
+(defn throw-runtime [fmt & args]
+ (throw (java.lang.RuntimeException. (apply format fmt args))))
View
@@ -5,13 +5,17 @@ website: https://github.com/CmdrDats/clj-minecraft
author: CmdrDats & aiscott
description: Bukkit + clojure integration library
commands:
- repl:
+ clj.repl:
description: Start or stop the Clojure REPL
permission: clojure.repl
usage: |
/<command> start/stop [port]
Example: /<command> start - starts a REPL on the default 4005 port
Example: /<command> start 5006 - starts a REPL on the 5006 port
Example: /<command> stop - stop the current REPL
+ clj.tabtest:
+ description: Just a test of the tabcompletion
+ permission: clojure.tabtest
+ usage: /<command> player material (start|stop) tab-reverse-of-player-name

0 comments on commit 599a66e

Please sign in to comment.