Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Loads of changes, mostly materials & items.

Resolves #30, #29, #26
  • Loading branch information...
commit 0aa090022fc3ebcf25229a6f1540640e47563591 1 parent 481741d
@CmdrDats authored
View
15 README.md
@@ -23,6 +23,21 @@ understand what changes are made and adjust your plugins accordingly.
Changelog:
+14 December 2012:
+ - Implement material handling types properly
+ o You can now specify [:wood :jungle :north] as a material key for (get-material)
+ o Supports everything down to [:mushroom false :north] for a non-stem north-painted mushroom block
+ - Command Tab completion support for event types and entity types
+ - Add 'spawnentity' and 'addevent' commands to showcase tabcompletion.
+ - Add 'find-entity' function in entity.clj - can use that to lookup the kinds of entities
+ - Tweak the event macro to be a simple function and the register-event to do the actual legwork.
+ - Add 'find-event' and 'describe-event' for making events easier to poke at from the REPL
+ - Moved the materials to items.clj
+ - The item-stack function in items.clj uses get-material
+ o This makes creating a specific itemstack straightforward and consistent (item-stack [:wood :jungle] 2)
+ - Tweak the recipes to use get-material, to make recipe material definitions very precise
+ - Add some class helpers in util.clj
+
09 December 2012:
- Implement first version of recipe wrapper functions
o Support for both shaped and unshaped recipes from the same function.
View
5 javasrc/cljminecraft/BasePlugin.java
@@ -12,10 +12,6 @@
import org.bukkit.plugin.*;
import org.bukkit.plugin.java.*;
-
-
-
-
public abstract class BasePlugin extends JavaPlugin{
protected final static String selfPluginName=ClojurePlugin.class.getPackage().getName();//"cljminecraft";
@@ -324,5 +320,4 @@ public final void onDisable() {//called only when onEnable didn't fail (if we di
+ " clojure Plugin because it wasn't successfully enabled previously" );
}
}
-
}
View
4 project.clj
@@ -5,8 +5,8 @@
[org.clojure/tools.nrepl "0.2.0-RC1"]
[org.bukkit/bukkit "1.4.5-R0.3-SNAPSHOT"]
[clojure-complete "0.2.2"]
- [cheshire "2.0.4"]]
-
+ [cheshire "2.0.4"]
+ [org.reflections/reflections "0.9.8"]]
:javac-options [ "-d" "classes/" "-source" "1.6" "-target" "1.6"]
:java-source-paths ["javasrc"]
View
41 src/cljminecraft/commands.clj
@@ -2,7 +2,10 @@
(:require [cljminecraft.bukkit :as bk]
[cljminecraft.util :as util]
[cljminecraft.logging :as log]
- [cljminecraft.player :as plr])
+ [cljminecraft.player :as plr]
+ [cljminecraft.events :as ev]
+ [cljminecraft.entity :as ent]
+ )
(:import [org.bukkit.command TabExecutor]))
(defn respond
@@ -44,6 +47,30 @@
(defmethod convert-type :default [sender type arg]
(str arg))
+(defmethod convert-type :event [sender type arg]
+ (str arg))
+
+(defmethod convert-type :entity [sender type arg]
+ (str arg))
+
+(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)))))
+
+(defmethod param-type-tabcomplete :event [sender type arg]
+ (ev/find-event arg))
+
+(defmethod param-type-tabcomplete :entity [sender type arg]
+ (ent/find-entity arg))
+
(defn arity-split [args]
(split-with #(not= '& %) args))
@@ -71,18 +98,6 @@
(catch RuntimeException e (.printStackTrace e) (respond sender "An error occurred with this command")))
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))))
View
13 src/cljminecraft/core.clj
@@ -1,6 +1,8 @@
(ns cljminecraft.core
(:require [cljminecraft.bukkit :as bk]
[cljminecraft.events :as events]
+ [cljminecraft.entity :as ent]
+ [cljminecraft.player :as plr]
[cljminecraft.util :as util]
[cljminecraft.logging :as log]
[cljminecraft.config :as cfg]
@@ -60,12 +62,22 @@
(defn tabcomplete-reverse-first [sender command alias args]
[(apply str (reverse (first args)))])
+(defn addevent-command [sender eventname message]
+ (events/register-event @clj-plugin eventname (fn [ev] (.sendMessage sender (str message ": " ev))))
+ {:msg (format "Adding event %s with message %s" eventname message)})
+
+(defn spawn-command [sender entity]
+ (ent/spawn-entity (.getLocation sender) entity)
+ (log/info "Spawning %s in front of %s" entity (.getName sender)))
+
(defn start
"onEnable cljminecraft"
[plugin]
(reset! clj-plugin plugin)
(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])
+ (cmd/register-command @clj-plugin "clj.addevent" #'addevent-command :event :string)
+ (cmd/register-command @clj-plugin "clj.spawnentity" #'spawn-command :entity)
(start-repl-if-needed plugin))
(defn stop
@@ -104,3 +116,4 @@
)
+
View
23 src/cljminecraft/entity.clj
@@ -0,0 +1,23 @@
+(ns cljminecraft.entity
+ (:require [cljminecraft.util :as util]))
+
+(def entitytypes (util/map-enums org.bukkit.entity.EntityType))
+
+(defn find-entity [nm]
+ (let [names (map #(name (first %)) entitytypes)]
+ (filter #(.contains % (.toLowerCase nm)) names)))
+
+(defn spawn-entity [location entityname]
+ (let [type (get entitytypes (keyword entityname))]
+ (when (and type (.isSpawnable type))
+ (.spawn (.getWorld location) location type))))
+
+
+
+
+
+
+
+
+
+
View
47 src/cljminecraft/events.clj
@@ -6,15 +6,16 @@
(defonce priorities (util/map-enums org.bukkit.event.EventPriority))
-(defn register-event [plugin event-class f & [priority-key]]
- (.registerEvent
- (bk/plugin-manager)
- event-class
- (proxy [org.bukkit.event.Listener] [])
- (get priorities (or priority-key :normal))
- (proxy [org.bukkit.plugin.EventExecutor] []
- (execute [l e] (f e)))
- plugin))
+(defn register-event [plugin eventname f & [priority-key]]
+ (let [eventclass (resolve (symbol (util/package-classname "org.bukkit.event" (str eventname "-event"))))]
+ (.registerEvent
+ (bk/plugin-manager)
+ eventclass
+ (proxy [org.bukkit.event.Listener] [])
+ (get priorities (or priority-key :normal))
+ (proxy [org.bukkit.plugin.EventExecutor] []
+ (execute [l e] (f e)))
+ plugin)))
(defonce registered-events
(atom #{}))
@@ -26,14 +27,28 @@
(register-event plugin (:classname ev) (:event-fn ev) (:priority ev))
)))
-(defmacro event
- "Convenience function for registering events, event-name being prefixed with org.bukkit.event.
+(defn event
+ "Convenience function for registering events, eventname being prefixed with org.bukkit.event.
and camelcased so that you can simply call (onevent block.block-break-event [e] (logging/info (bean e)))
to register for the org.bukkit.event.block.BlockBreakEvent and run the body with the BlockBreakEvent as its only
argument"
- [event-name fn & [priority]]
- (let [classname (util/package-classname "org.bukkit.event" (str event-name "-event"))]
- `{:classname ~(resolve (symbol classname))
- :event-fn ~fn
- :priority ~priority}))
+ [eventname fn & [priority]]
+ {:eventname eventname
+ :event-fn fn
+ :priority priority})
+(defn find-event [name]
+ (let [classes (util/find-subclasses "org.bukkit" org.bukkit.event.Event)
+ names (map #(.replaceAll
+ (.replaceAll (util/class-named %) "org.bukkit.event." "")
+ "-event$" "") classes)]
+ (filter #(.contains % (.toLowerCase name)) names)))
+
+(def boring-methods #{"getHandlers" "getHandlerList" "wait" "equals" "toString" "hashCode" "getClass" "notify" "notifyAll" "isAsynchronous"})
+
+(defn describe-event [eventname]
+ (let [classname (util/package-classname "org.bukkit.event" (str eventname "-event"))
+ cl (resolve (symbol classname))]
+ (set
+ (filter #(not (contains? boring-methods %))
+ (map #(:name (bean %)) (seq (:methods (bean cl))))))))
View
220 src/cljminecraft/items.clj
@@ -0,0 +1,220 @@
+(ns cljminecraft.items
+ (:require [cljminecraft.util :as util])
+ (:require [cljminecraft.entity :as ent])
+ (:import [org.bukkit TreeSpecies Material])
+ (:import [org.bukkit.material
+ MaterialData Tree Dispenser Sandstone Bed PoweredRail DetectorRail Wool Chest Crops
+ PistonBaseMaterial PistonExtensionMaterial LongGrass Step Torch Stairs RedstoneWire
+ Furnace Sign Door Ladder Rails Lever RedstoneTorch Button Pumpkin Cake Diode TrapDoor
+ MonsterEggs SmoothBrick Mushroom Vine Gate Cauldron WoodenStep CocoaPlant EnderChest
+ TripwireHook Tripwire Command Skull Coal Dye SpawnEgg FlowerPot])
+ (:import [org.bukkit.block BlockFace])
+ (:import [org.bukkit.inventory ItemStack]))
+
+(def materials (util/map-enums Material))
+(def treespecies (util/map-enums TreeSpecies))
+(def blockfaces (util/map-enums BlockFace))
+(def grassspecies (util/map-enums org.bukkit.GrassSpecies))
+(def sandstonetypes (util/map-enums org.bukkit.SandstoneType))
+(def dyecolors (util/map-enums org.bukkit.DyeColor))
+(def cropstates (util/map-enums org.bukkit.CropState))
+(def cocoaplantsizes (util/map-enums org.bukkit.material.CocoaPlant$CocoaPlantSize))
+(def coaltypes (util/map-enums org.bukkit.CoalType))
+
+(defmulti get-new-data
+ "Returns a new MaterialData depending on Material Type"
+ (fn [t _ _] t))
+
+(defmacro defmat
+ "Convenience macro for the get-new-data multi method, common pattern emerged. I could take it further, but this feels like the right level of abstraction."
+ [type & specs]
+ (let [sn #(symbol (name %))
+ args (map #(sn (second %)) specs)
+ lookup-list (filter #(>= (count %) 3) specs)
+ spec-let (apply concat (map (fn [[_ n c]] [(sn n) `(get ~c ~(sn n))]) lookup-list))
+ spec-set (apply concat (map (fn [[s n]] [`(if ~(sn n) (~s ~(symbol "result") ~(sn n)))]) specs))]
+ `(defmethod get-new-data ~type
+ [~(symbol "type") ~(symbol "material") [~@args]]
+ (let [~@spec-let
+ ~(symbol "result") (~(symbol (str type ".")))]
+ ~@spec-set
+ ~(symbol "result"))
+ )))
+
+(defmacro defdirectional
+ "Plenty of types simply have a '.setFacingDirection', so this is a macro to make it a two-worder."
+ [type]
+ `(defmat ~type [.setFacingDirection ~(symbol "direction") blockfaces]))
+
+(defmethod get-new-data _ [type material values]
+ (let []))
+
+(defmat Tree
+ [.setSpecies species treespecies]
+ [.setDirection direction blockfaces])
+
+(defmat Sandstone
+ [.setType sandstonetype sandstonetypes])
+
+(defmat LongGrass
+ [.setSpecies species grassspecies])
+
+(defmat Wool
+ [.setColor color dyecolors])
+
+(defdirectional Dispenser)
+(defdirectional Bed)
+
+(defmethod get-new-data PoweredRail [type material [direction onslope? powered?]]
+ (let [result (PoweredRail. material)
+ dir (get blockfaces direction)]
+ (if dir (.setDirection result dir (if onslope? true false)))
+ (if powered? (.setPowered result true))
+ result))
+
+(defmethod get-new-data DetectorRail [type material [direction onslope? pressed?]]
+ (let [result (DetectorRail. material)
+ dir (get blockfaces direction)]
+ (if dir (.setDirection result dir (if onslope? true false)))
+ (if pressed? (.setPressed result true))
+ result))
+
+(defmethod get-new-data Rails [type material [direction onslope?]]
+ (let [result (Rail. material)
+ dir (get blockfaces direction)]
+ (if dir (.setDirection result dir (if onslope? true false)))
+ result))
+
+(defmat RedstoneWire
+ [.setPowered powered?])
+
+(defdirectional RedstoneTorch)
+(defmat Diode
+ [.setFacingDirection direction blockfaces]
+ [.setDelay delay])
+
+(defmat PistonBaseMaterial
+ [.setFacingDirection direction blockfaces])
+
+(defmat TripwireHook
+ [.setFacingDirection direction blockfaces]
+ [.setConnected connected?]
+ [.setActivated activated?])
+
+(defmat Tripwire
+ [.setObjectTriggering triggering?]
+ [.setActivated activated?])
+
+(defmat Command
+ [.setPowered powered?])
+
+(defdirectional PistonExtensionMaterial)
+
+(defmat WoodenStep
+ [.setSpecies species treespecies]
+ [.setInverted inverted?])
+
+(defmat Step
+ [.setMaterial texture materials]
+ [.setInverted inverted?])
+
+(defmat Stairs
+ [.setFacingDirection direction blockfaces]
+ [.setInverted inverted?])
+
+(defdirectional Torch)
+(defdirectional Chest)
+(defdirectional EnderChest)
+
+(defmat Crops
+ [.setState state cropstates])
+
+(defdirectional Furnace)
+(defdirectional Sign)
+
+(defmat Door
+ [.setFacingDirection direction blockfaces]
+ [.setOpen open?]
+ [.setTopHalf tophalf?])
+
+(defmat TrapDoor
+ [.setFacingDirection direction blockfaces]
+ [.setOpen open?])
+
+(defmat Gate
+ [.setFacingDirection direction blockfaces]
+ [.setOpen open?])
+
+(defdirectional Ladder)
+
+(defmat Lever
+ [.setFacingDirection direction blockfaces]
+ [.setPowered powered?])
+
+(defmat Button
+ [.setFacingDirection direction blockfaces]
+ [.setPowered powered?])
+
+(defdirectional Pumpkin)
+(defmat Cake
+ [.setSlicesEaten eaten])
+
+(defmat MonsterEggs
+ [.setMaterial texture materials])
+
+(defmat SmoothBrick
+ [.setMaterial texture materials])
+
+(defmethod get-new-data Mushroom [type material [stem? & pntdirs]]
+ (let [result (Mushroom. material)]
+ (if stem? (.setStem true))
+ (doseq [d pntdirs]
+ (let [dir (get blockfaces d)]
+ (.setPaintedFace result dir)))
+ result))
+
+(defmethod get-new-data Vine [type material [stem? pntwest? pnteast? pntnorth? pntsouth? pntup?]]
+ (let [result (Vine. material)]
+ (doseq [d pntdirs]
+ (let [dir (get blockfaces d)]
+ (.putOnFace result dir)))
+ result))
+
+(defmat CocoaPlant
+ [.setFacingDirection direction blockfaces]
+ [.setSize size cocoaplantsizes])
+
+(defmat Coal
+ [.setType type coaltypes])
+
+(defmat Dye
+ [.setColor color dyecolors])
+
+(defmat SpawnEgg
+ [.setSpawnedType type ent/entitytypes])
+
+(declare get-material)
+
+(defmethod get-new-data FlowerPot [type material [content-material]]
+ (let [result (FlowerPot. material)]
+ (if content-material (.setContents (get-material content-material)))
+ result))
+
+;; Whew.
+
+(defn get-material
+ "Gets a material from the material map. If you pass it a keyword, it will do a simple lookup. If you pass it a vector [:wood :jungle] or [:water 2], it will return a MaterialData with the material and data setup correctly - Consult the type's multimethod to find what parameters you should pass in."
+ [material-key]
+ (if (coll? material-key)
+ (let [material (get materials (first material-key))
+ val (second material-key)]
+ (if (number? val)
+ (.getNewData material val)
+ (get-new-data (.getData material) material (rest material-key))))
+ (get materials material-key)))
+
+(defn item-stack [material-key & [qty]]
+ (let [material (get-material material-key)]
+ (if (instance? MaterialData material)
+ (.toItemStack (or qty 1))
+ (ItemStack. material (or qty 1)))))
View
7 src/cljminecraft/player.clj
@@ -1,9 +1,8 @@
(ns cljminecraft.player
(:require [cljminecraft.bukkit :as bk]
- [cljminecraft.util :as util]))
+ [cljminecraft.util :as util]
+ [cljminecraft.items :as items]))
-;; TODO: Move this to a more sensible namespace, issue #29
-(defonce materials (util/map-enums org.bukkit.Material))
;; Various player helper functions
(defn broadcast
@@ -57,6 +56,6 @@
(.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)))
+ (let [stack (items/item-stack material-key qty)
player (get-player player)]
(.addItem (.getInventory (get-player player)) (into-array org.bukkit.inventory.ItemStack [stack]))))
View
15 src/cljminecraft/recipes.clj
@@ -1,18 +1,13 @@
(ns cljminecraft.recipes
(:require [cljminecraft.player :as plr])
(:require [cljminecraft.bukkit :as bk])
+ (:require [cljminecraft.items :as items])
(:import [org.bukkit.inventory ShapedRecipe ShapelessRecipe ItemStack]))
-(def material-map
- {\W :wood \D :dirt \S :stone \O :compass})
-
-(defn item-stack [material-key & [qty]]
- (ItemStack. (get plr/materials material-key) (or qty 1)))
-
(defn shapeless [material-map result ingredients qty]
(let [result (ShapelessRecipe. (item-stack result qty))]
(doseq [c ingredients]
- (.addIngredient result (get plr/materials (get material-map c))))
+ (.addIngredient result (items/get-material (get material-map c))))
result))
(defn shaped [material-map result ingredients qty]
@@ -20,7 +15,7 @@
(.shape result (into-array String ingredients))
(doseq [[c mkey] material-map]
(try
- (.setIngredient result (char c) (get plr/materials mkey))
+ (.setIngredient result (char c) (items/get-material mkey))
(catch Exception e
;; Wow, dumb idea to throw an exception if the ingredient
;; doesn't appear in a shape.
@@ -37,7 +32,9 @@
(doseq [recipe recipes]
(.addRecipe (bk/server) recipe)))
-(register-recipes (recipe material-map :monster_egg [" D " "DWD" " D "]))
+#_(def material-map
+ {\W :wood \D :dirt \S :stone \O :compass})
+
#_(register-recipes
(recipe material-map :ink_sack "WW" 2)
(recipe material-map :monster_egg [" D " "DWD" " D "]))
View
19 src/cljminecraft/util.clj
@@ -37,6 +37,11 @@
[str]
(.replaceAll (capitalize-all str) "-" ""))
+(defn uncamelcase
+ "Add dashes and lowercase everything"
+ [str]
+ (.substring (.toLowerCase (.replaceAll str "([A-Z])" "-$1")) 1))
+
(defn glue [sep & strs]
(apply str (interpose sep (filter #(and (not (nil? %)) (> (.length (.trim (str %))) 0)) strs))))
@@ -54,6 +59,12 @@
package (apply glue "." base-package (pop (vec split)))]
(glue "." package classname)))
+(defn class-named [class]
+ (let [split (seq (.split (.getName class) "[.]"))
+ classname (uncamelcase (last split))
+ package (apply glue "." (pop (vec split)))]
+ (glue "." package classname)))
+
(defn port-in-use? [port bind]
(let [bind-addr (if (InetSocketAddress. bind port) (InetSocketAddress. port))]
@@ -63,3 +74,11 @@
(defn throw-runtime [fmt & args]
(throw (java.lang.RuntimeException. (apply format fmt args))))
+
+(defn find-subclasses [package-name class]
+ (filter #(not (nil? %))
+ (seq (.getSubTypesOf (org.reflections.Reflections.
+ package-name
+ (into-array org.reflections.scanners.Scanner []))
+ class))))
+
View
3  src/cljminecraft/world.clj
@@ -9,4 +9,5 @@
(let [data (.. block getState getData)]
(if (instance? Directional data)
(.getFace block (.getFacing data))
- block)))
+ block)))
+
View
10 src/plugin.yml
@@ -17,5 +17,11 @@ commands:
description: Just a test of the tabcompletion
permission: clojure.tabtest
usage: /<command> player material (start|stop) tab-reverse-of-player-name
-
-
+ clj.addevent:
+ description: Get notified on event
+ permission: clojure.event
+ usage: /<command> eventname message
+ clj.spawnentity:
+ description: Spawn a given entity in front of you
+ permission: clojure.spawn
+ usage: /<command> entityname
Please sign in to comment.
Something went wrong with that request. Please try again.