Permalink
Browse files

Work on commands, repl command and small code cleanups

Commands: Type conversion, autocomplete, dispatch
Repl command: /repl start|stop 4005 for sufficient permission users
  • Loading branch information...
CmdrDats committed Dec 7, 2012
1 parent 71261b3 commit 39c29fabc9c4f07dcc35e3ae5d16091f0372d0e3
Showing with 128 additions and 45 deletions.
  1. +9 −0 README.md
  2. +81 −6 src/cljminecraft/commands.clj
  3. +36 −37 src/cljminecraft/core.clj
  4. +2 −2 src/cljminecraft/player.clj
View
@@ -23,6 +23,15 @@ understand what changes are made and adjust your plugins accordingly.
Changelog:
+07 December 2012:
+ - Lots of work on the command abstractions:
+ o Common type autocompletion
+ o Hardcoded list autocompletion
+ o Function result autocompleion
+ o On command dispatch, convert to known types before calling command function
+ o NOTE: The command autocompletion and type conversion will change slightly soon to allow for a more customized open system where plugins can contribute with their own defmulti's
+ - Implemented the /repl start|stop [port] command
+
05 December 2012:
- Rename project to be cljminecraft instead of clj-minecraft for consistency with ns
- #2 Fix broadcastMessage typo
@@ -1,14 +1,80 @@
(ns cljminecraft.commands
(:require [cljminecraft.bukkit :as bk]
[cljminecraft.util :as util]
- [cljminecraft.logging :as log])
+ [cljminecraft.logging :as log]
+ [cljminecraft.player :as plr])
(:import [org.bukkit.command TabExecutor]))
+(defn respond
+ [sender fmt & args]
+ ;; This needs tweaking to handle responding with info if sender is console?
+ (plr/send-msg sender fmt args))
+
+(defmulti convert-type (fn [x _] (if (coll? x) (first x) x)))
+
+(defmethod convert-type :string [type arg] arg)
+
+(defmethod convert-type :int [type arg]
+ (try
+ (Integer/parseInt arg)
+ (catch Exception e nil)))
+
+(defmethod convert-type :player [type arg]
+ (plr/get-player arg))
+
+(defmethod convert-type :material [type arg]
+ (get plr/materials (keyword arg)))
+
+(defmethod convert-type :long [type arg]
+ (try
+ (Long/parseLong arg)
+ (catch Exception e nil)))
+
+(defmethod convert-type :double [type arg]
+ (try
+ (Double/parseDouble arg)
+ (catch Exception e nil)))
+
+(defmethod convert-type :keyword [type arg]
+ (log/info "Keywording %s" arg)
+ (keyword arg))
+
+(defmethod convert-type :default [type arg]
+ (log/info "Default conversion of %s %s" 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)
+
+(defmulti param-type-tabcomplete (fn [x _] x))
+
+(defmethod param-type-tabcomplete :player [type arg]
+ ["hello"])
+
+(defn to-string-seq [a]
+ (for [i (seq a)] (if (keyword? i) (name i) (str i))))
+
+(defn tabcomplete [f param-types sender command alias args]
+ (log/info "Tab completing for command %s, %s - %s" command param-types (seq args))
+ (let [args (seq args)
+ param (get (into [] param-types) (dec (count args)))
+ [type opts] (if (coll? param) param [])]
+ (log/info "For param %s %s %s %s" param type opts (get (into [] param-types) (dec (count args))))
+ (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)))))
+
(defn build-executor [f param-types]
- (proxy TabExecutor []
- (onCommand [sender command alias args]
- (apply f args))
- (onTabComplete [sender command alias args])))
+ (proxy [TabExecutor] []
+ (onCommand [sender cmd alias args]
+ (command f param-types sender cmd alias args))
+ (onTabComplete [sender cmd alias args]
+ (tabcomplete f param-types sender cmd alias args))))
(defn register-command
"Registers a command function, providing a function name, type and parameter types"
@@ -26,4 +92,13 @@
;; The types here infer validation rules and tab-completion - The
;; receiving function will be guarenteed to be passed a Player
;; object and an integer instead of two strings.
- (register-command plugin "transfer" #'give-money :player :int))
+ (register-command plugin "transfer" #'give-money :player :int))
+
+
+
+
+
+
+
+
+
View
@@ -4,61 +4,60 @@
[cljminecraft.util :as util]
[cljminecraft.logging :as log]
[cljminecraft.config :as cfg]
+ [cljminecraft.commands :as cmd]
[cljminecraft.files]
- [clojure.tools.nrepl.server :refer (start-server stop-server)])
-)
+ [clojure.tools.nrepl.server :refer (start-server stop-server)]))
(def repl-handle (atom nil))
(defn start-repl [host port]
(log/info "Starting repl on host: %s, port %s" host port)
- (cond (compare-and-set! repl-handle nil (start-server :host host :port port))
- (log/info "Started repl on host: %s, port %s" host port)
- :else
- ;I guess we don't allow multiple running REPLs this way, do we want more than 1 ie. on different host/port?
- (log/bug "you tried to start a(nother) repl while one was already started")
- )
- )
+ (cond
+ @repl-handle
+ {:msg "you tried to start a(nother) repl while one was already started"}
+ (util/port-in-use? port host)
+ {:msg (format "REPL already started or port %s:%s is in use" host port)}
+ :else
+ (do
+ (reset! repl-handle (start-server :host host :port port))
+ {:msg (format "Started repl on host: %s, port %s" host port)})))
(defn stop-repl
[]
- (cond @repl-handle
- (try
- (do
- (stop-server @repl-handle)
- (log/info "REPL stopped")
- )
- (finally
- (reset! repl-handle nil)
- )
- )
- :else
- (log/bug "you tried to stop REPL when it was not running")
- )
- )
-
+ (cond
+ (nil? @repl-handle) {:msg "you tried to stop REPL when it was not running"}
+ :else
+ (try
+ (do
+ (stop-server @repl-handle)
+ {:msg "REPL stopped"})
+ (finally
+ (reset! repl-handle nil)))))
(defn start-repl-if-needed [plugin]
- (let [
- repl-enabled (cfg/get-boolean plugin "repl.enabled")
- repl-host (cfg/get-string plugin "repl.host")
- repl-port (cfg/get-int plugin "repl.port")
- ]
- (when repl-enabled
- (cond (util/port-in-use? repl-port repl-host)
- (log/warn "REPL already started or port %s:%s is in use" repl-host repl-port)
- :else
- (do
- (log/info "Repl options: %s %s %s" repl-enabled repl-host repl-port)
- (start-repl repl-host repl-port)
- )))))
+ (let [repl-host (cfg/get-string plugin "repl.host")
+ repl-port (cfg/get-int plugin "repl.port")]
+ (cond
+ (not (cfg/get-boolean plugin "repl.enabled"))
+ (log/info "REPL Disabled")
+ :else
+ (let [{:keys [msg] :as response} (start-repl repl-host repl-port)]
+ (log/info "Repl options: %s %s" repl-host repl-port)
+ (if msg (log/info msg))))))
(defonce clj-plugin (atom nil))
+(defn repl-command [cmd & [port]]
+ (log/info "Running repl command with %s %s" cmd port)
+ (case cmd
+ :start (start-repl "0.0.0.0" (or port 4005))
+ :stop (stop-repl)))
+
(defn start
"onEnable cljminecraft"
[plugin]
(reset! clj-plugin plugin)
+ (cmd/register-command @clj-plugin "repl" #'repl-command [:keyword [:start :stop]] [:int [4005]])
@CmdrDats

CmdrDats Dec 8, 2012

Owner

You have a good point, I'll make it default to the repl.port - thanks for noticing!

(start-repl-if-needed plugin))
(defn stop
@@ -52,8 +52,8 @@
HasPlayer
(get-player [this] this))
-(defn send-msg [player & msg]
- (.sendMessage (get-player player) (apply str msg)))
+(defn send-msg [player fmt & args]
+ (.sendMessage (get-player player) (apply format fmt args)))
(defn give [player material-key & [qty]]
(let [stack (org.bukkit.inventory.ItemStack. (get materials material-key) (int (or qty 1)))

1 comment on commit 39c29fa

Owner

CmdrDats commented on 39c29fa Dec 8, 2012

Glad you like them - I'm busy implementing arity checking now so that if you don't pass the correct number of arguments, it automatically complains :)

The Bukkit wiki has a bunch of 'how to write safe commands' - I'm trying my best to encapsulate all of those points in a way that makes it hard not to automatically have

Please sign in to comment.