Skip to content

Commit

Permalink
Merge pull request #20 from yoshiquest/dev
Browse files Browse the repository at this point in the history
Version 0.5.2
  • Loading branch information
ryanhaney97 committed Jan 25, 2016
2 parents 0675289 + 9aa4c23 commit eb43a35
Show file tree
Hide file tree
Showing 12 changed files with 681 additions and 92 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ gradlew
gradlew.bat
logs/
update-test
tutorial.md
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ Oh yeah, when getting ready to deploy your mod, be sure to use gradle build (not

##Downloads

- Version 0.5.2: [forge-clj](http://bit.ly/1PeHOgA "forge-clj Version 0.5.2") [forge-clj devkit](http://bit.ly/1Qn55fh "forge-clj devkit Version 0.5.2")

- Version 0.5.1: [forge-clj](http://bit.ly/1PAHtpQ "forge-clj Version 0.5.1") [forge-clj devkit](http://bit.ly/1OJpv19 "forge-clj devkit Version 0.5.1")

- Version 0.5.0: [forge-clj](http://bit.ly/1GREeYa "forge-clj Version 0.5.0") [forge-clj devkit](http://bit.ly/1GREcQ4 "forge-clj devkit Version 0.5.0")
Expand All @@ -38,6 +40,8 @@ Oh yeah, when getting ready to deploy your mod, be sure to use gradle build (not

##Changelog

- Version 0.5.2: Added the ability to make biomes, and extended upon defobj so that you can now optionally make it generate a class underneath for the purpose of accessing protected fields. Also added some support for custom chunk providers, though unfortunately I was unable to replicate Minecraft's terrain generation function, due to some slowness in the language as well as general complexity issues. If you wish to make a chunk provider for a forge-clj mod, I would recommend just copying the chunk provider from Java, and then reference it via inter-op. My apologies for not being able to figure this out, and even greater apologies for spending a month TRYING to figure it out and failing.

- Version 0.5.1: Added docstrings to the code. Also added 2 new useful macros: defclass and with-prefix. Also rearranged the namespaces a bit. The deftab macro is now in forge-clj.items, and many of the more utility-based functions in the core (gen-classname, get-fullname, etc.) are now in forge-clj.util. I also added a new namespace called forge-clj.registry, that contains all of the register functions and such (except for the network stuff). Also made forge-clj.client.registry, which is the same, but is for the client side.

- Version 0.5.0: Added REPL support. To use it, first add the :repl keyword to the defmod macro, and set it to your port number or set it to true to use the default port of 7888. Then just connect to it with another tool (if using leiningen do "lein repl :connect \<portnumber\>"). Also finished adding support for GUIs (with inventory).
Expand Down
6 changes: 3 additions & 3 deletions src/main/clojure/forge_clj/client/ui.clj
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
[net.minecraft.client.gui.inventory GuiContainer]))

(defmacro defguicontainer
"DEFCLASS: Given a name space, class name, and classdata, creates a class extending GuiContainer.
"DEFCLASS: Given a class name and classdata, creates a class extending GuiContainer.
Remember to create implementations of drawGuiContainerBackgroundLayer and
drawGuiContainerForegroundLayer or this will break!"
[name-ns class-name & args]
[class-name & args]
(let [classdata (apply hash-map args)]
`(defclass GuiContainer ~name-ns ~class-name ~classdata)))
`(defclass GuiContainer ~class-name ~classdata)))
177 changes: 114 additions & 63 deletions src/main/clojure/forge_clj/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
(:require
[clojure.string :as string]
[clojure.set :as cset]
[forge-clj.util :refer [gen-method gen-setter gen-classname get-fullname with-prefix]]
[forge-clj.util :refer [gen-method gen-setter gen-classname get-fullname with-prefix deep-merge construct update-map-keys]]
[clojure.tools.nrepl.server :refer [start-server]])
(:import
[java.lang.reflect Field]
[cpw.mods.fml.common Mod Mod$EventHandler FMLCommonHandler]
[cpw.mods.fml.common.event FMLPreInitializationEvent FMLInitializationEvent FMLPostInitializationEvent]))

Expand All @@ -20,11 +21,12 @@
This will allow forge-clj to include the client namespaces only if run on an integrated client.
The :repl keyword can also be used to start a nrepl. If :repl is 'true', it'll use the default value
of 7888 as the port. Otherwise, if :repl is a number, it'll use that number as the port instead."
[name-ns mod-name version & proxies]
(let [proxies (apply hash-map proxies)
commonproxy (get proxies :common {})
clientproxy (get proxies :client {})
repl (get proxies :repl)
[mod-name version & options]
(let [options (apply hash-map options)
name-ns (get options :ns *ns*)
commonproxy (get options :common {})
clientproxy (get options :client {})
repl (get options :repl)
repl (if (true? repl)
7888
repl)
Expand All @@ -35,7 +37,7 @@
(get commonproxy :pre-init (get commonproxy :init (get commonproxy :post-init nil))))
common-ns (if common-ns (first (string/split (str common-ns) #"/")) nil)
prefix (str mod-name "-")
fullname (symbol (str (string/replace name-ns #"-" "_") "." (gen-classname mod-name)))]
fullname (get-fullname name-ns mod-name)]
`(do
(gen-class
:name ~(with-meta fullname `{Mod {:name ~(str (gen-classname mod-name)) :modid ~(str mod-name) :version ~(str version)}})
Expand Down Expand Up @@ -72,52 +74,8 @@
~(when (:post-init clientproxy)
`(~(:post-init clientproxy) ~'this ~'event))))))))

(defmacro genobj
"MACRO: General purpose macro used to extend objects. Takes the superclass, constructor arguments (as a vector), the name,
and the data (as a map), to create an instance of an anonymous class that extends the provided superclass.
The data given will be converted to a java setter that will be called on the resulting object
(for example, :block-name \"name\" becomes .setBlockName(\"name\")).
Multiple arguments can be specified using a vector.
This occurs unless one of the following special keywords is used:
:override - should contain a map of method-names (as keywords, such as :set-block, which results in .setBlock) and functions to override them.
:interfaces - makes the object implement the provided interfaces, contained in a vector. Interface methods must be overriden via :override.
:calls - map of calls on the final object. Differs from normal since you can specify methods without the word set in front of them."
[superclass constructor-args objdata]
(let [overrides (:override objdata)
override-methods (when overrides (map gen-method (keys overrides)))
override-calls (if overrides (map #(list apply %1 'args) (vals overrides)))
override-calls (if overrides (map #(list %1 ['& 'args] %2) override-methods override-calls))
interfaces (:interfaces objdata)
method-calls (:calls objdata)
objdata (dissoc objdata :override :interfaces :calls)
setters (map gen-setter (keys objdata))
calls (map #(if (vector? %2)
(apply list %1 %2)
(list %1 %2)) setters (vals objdata))
method-calls (map #(if (vector? (val %1))
(apply list (symbol (str "." (gen-method (key %1)))) (val %1))
(list (symbol (str "." (gen-method (key %1)))) (val %1))) method-calls)
super-vector (if interfaces (concat [superclass] interfaces) [superclass])]
(if overrides
`(doto (proxy ~super-vector ~constructor-args
~@override-calls)
~@calls
~@method-calls)
`(doto (proxy ~super-vector ~constructor-args)
~@calls
~@method-calls))))

(defmacro defobj
"MACRO: same as genobj, but takes a name (as the third argument), and stores the resulting instance in a def with that name.
Other macros using this will have DEFOBJ: in their docs, instead of MACRO:.
Realize that other things using defobj in forge-clj are macros as well unless specified otherwise."
[superclass constructor-args obj-name objdata]
`(def ~obj-name (genobj ~superclass ~constructor-args ~objdata)))

(defmacro defclass
"MACRO: Given a superclass (optional), a namespace name, a class name,
"MACRO: Given a superclass (optional), a class name,
and a map of classdata, creates a Java class using Clojure's gen-class function.
This new class will have a name as specified by gen-classname, and a full package name as specified by get-fullname.
The full package name will be stored in a def with a name equal to the class name provided.
Expand All @@ -139,8 +97,9 @@
:factory - see Clojure's documentation for gen-class.
:impl-ns - see Clojure's documentation for gen-class.
:load-impl-ns - see Clojure's documentation for gen-class."
([superclass name-ns class-name classdata]
(let [classdata (cset/rename-keys classdata {:expose-fields :exposes
([superclass class-name classdata]
(let [name-ns (get classdata :ns *ns*)
classdata (cset/rename-keys classdata {:expose-fields :exposes
:expose :exposes-methods
:expose-methods :exposes-methods
:interfaces :implements})
Expand All @@ -156,26 +115,118 @@
~@classdata)
(def ~class-name ~fullname)
(import ~fullname))))
([name-ns class-name classdata]
`(defclass nil ~name-ns ~class-name ~classdata)))
([class-name classdata]
`(defclass nil ~class-name ~classdata)))

(defmacro genobjclass
[obj-name superclass constructor-args objdata]
(let [class-name (symbol (str obj-name "-class"))
name-ns (get-in objdata [:class :ns] *ns*)
fullname (get-fullname name-ns class-name)
current-methods (concat (mapv #(gen-method (str "set-" (name %1))) (keys (dissoc objdata :override :interfaces :calls :fields :class))) (mapv gen-method (keys (get objdata :calls {}))))
new-methods (merge (update-map-keys #(keyword (str "set-" (name %1))) (dissoc objdata :override :interfaces :calls :fields :class)) (get objdata :calls {}))
new-methods (update-map-keys #(gen-method (str "super-" obj-name "-" (name %1))) new-methods)
exposes-methods (zipmap current-methods (keys new-methods))
method-calls (map #(if (vector? (val %1))
`((fn [~'obj]
(~(symbol (str "." (key %1))) ~(with-meta 'obj `{:tag ~fullname}) ~@(val %1))))
`((fn [~'obj]
(~(symbol (str "." (key %1))) ~(with-meta 'obj `{:tag ~fullname}) ~(val %1))))) new-methods)
current-fields (update-map-keys gen-method (:fields objdata))
new-fields (update-map-keys #(gen-method (str "set-" obj-name "-" (name %1))) (:fields objdata))
exposes (zipmap (keys current-fields) (map #(hash-map :set %1) (keys new-fields)))
field-calls (map (fn [field]
`((fn [~'obj]
(~(symbol (str "." (gen-method (key field)))) ~(with-meta 'obj `{:tag ~fullname}) ~(val field))))) new-fields)
overrides (update-map-keys gen-method (:override objdata))
overrides (map (fn [override]
`(def ~(key override) ~(val override))) overrides)
classdata (deep-merge
{:exposes-methods exposes-methods
:exposes exposes}
(if (map? (:class objdata))
(cset/rename-keys (:class objdata) {:expose-fields :exposes
:expose :exposes-methods
:expose-methods :exposes-methods
:interfaces :implements})
{}))]
`(do
(defclass ~superclass ~class-name ~classdata)
~(if overrides
`(with-prefix ~(str class-name "-")
~@overrides))
(doto (apply construct ~class-name ~constructor-args)
~@method-calls
~@field-calls))))

(defmacro genobj
"MACRO: General purpose macro used to extend objects. Takes the superclass, constructor arguments (as a vector), the name,
and the data (as a map), to create an instance of an anonymous class that extends the provided superclass.
The data given will be converted to a java setter that will be called on the resulting object
(for example, :block-name \"name\" becomes .setBlockName(\"name\")).
Multiple arguments can be specified using a vector.
This occurs unless one of the following special keywords is used:
:override - should contain a map of method-names (as keywords, such as :set-block, which results in .setBlock) and functions to override them.
:interfaces - makes the object implement the provided interfaces, contained in a vector. Interface methods must be overriden via :override.
:calls - map of calls on the final object. Differs from normal since you can specify methods without the word set in front of them."
[superclass constructor-args objdata]
(let [overrides (:override objdata)
override-methods (when overrides (map gen-method (keys overrides)))
override-calls (if overrides (map #(list apply %1 'args) (vals overrides)))
override-calls (if overrides (map #(list %1 ['& 'args] %2) override-methods override-calls))
interfaces (:interfaces objdata)
method-calls (:calls objdata)
fields (:fields objdata)
objdata (dissoc objdata :override :interfaces :calls :fields :class)
setters (map gen-setter (keys objdata))
calls (map #(if (vector? %2)
(apply list %1 %2)
(list %1 %2)) setters (vals objdata))
method-calls (map #(if (vector? (val %1))
(apply list (symbol (str "." (gen-method (key %1)))) (val %1))
(list (symbol (str "." (gen-method (key %1)))) (val %1))) method-calls)
field-calls (map (fn [field]
`(fn [~'obj]
(set! (~(symbol (str ".-" (gen-method (key field)))) ~(with-meta 'obj `{:tag ~superclass})) ~(val field)))) fields)
super-vector (if interfaces (concat [superclass] interfaces) [superclass])
obj-creator (if overrides
`(proxy ~super-vector ~constructor-args
~@override-calls)
`(proxy ~super-vector ~constructor-args))]
`(doto ~obj-creator
~@calls
~@method-calls
~@field-calls)))

(defmacro defobj
"MACRO: same as genobj, but takes a name (as the third argument), and stores the resulting instance in a def with that name.
Other macros using this will have DEFOBJ: in their docs, instead of MACRO:.
Realize that other things using defobj in forge-clj are macros as well unless specified otherwise."
[superclass constructor-args obj-name objdata]
(if (:class objdata)
`(def ~obj-name (genobjclass ~obj-name ~superclass ~constructor-args ~objdata))
`(def ~obj-name (genobj ~superclass ~constructor-args ~objdata))))

(defmacro defassocclass
"DEFCLASS: Creates a class with the specified superclass (optional), namespace name, class name, and classdata.
"DEFCLASS: Creates a class with the specified superclass (optional), class name, and classdata.
This class implements ITransientAssociative, and as such classes created with this can be treated similarly to a hash-map.
For example, data can be obtained via (:keyword class-instance), or (get class-instance :keyword).
Data can also be changed via the assoc! function. Remember to include the exclaimation point!
Other macros using this will have DEFASSOCCLASS: in their docs instead of MACRO:.
This also means that those labeled as using this macro are also macros unless specified otherwise."
([superclass name-ns class-name classdata]
(let [classdata (assoc classdata :interfaces (conj (get classdata :interfaces []) `clojure.lang.ITransientAssociative)
([superclass class-name classdata]
(let [name-ns (get classdata :ns *ns*)
classdata (assoc classdata :interfaces (conj (get classdata :interfaces []) `clojure.lang.ITransientAssociative)
:init 'initialize
:state 'data)
fields (get classdata :fields {})
prefix (str class-name "-")
fullname (get-fullname name-ns class-name)
this-sym (with-meta 'this {:tag fullname})]
`(do
(defclass ~superclass ~name-ns ~class-name ~classdata)
(defclass ~superclass ~class-name ~classdata)
(with-prefix ~prefix
(defn ~'initialize []
[[] (atom ~fields)])
Expand All @@ -192,14 +243,14 @@
(get (deref (~'.-data ~this-sym)) ~'obj))
([~'this ~'obj ~'notfound]
(get (deref (~'.-data ~this-sym)) ~'obj ~'notfound)))))))
([name-ns class-name classdata]
`(defassocclass nil ~name-ns ~class-name ~classdata)))
([class-name classdata]
`(defassocclass nil ~class-name ~classdata)))

;Mod declaration by forge-clj
;----------------------------

(gen-class
:name ^{Mod {:name "ForgeClj" :modid "forge-clj" :version "0.5.1"}} forge_clj.core.ForgeClj
:name ^{Mod {:name "ForgeClj" :modid "forge-clj" :version "0.5.2"}} forge_clj.core.ForgeClj
:prefix "forge-clj-"
:methods [[^{Mod$EventHandler []} preInit [cpw.mods.fml.common.event.FMLPreInitializationEvent] void]
[^{Mod$EventHandler []} init [cpw.mods.fml.common.event.FMLInitializationEvent] void]
Expand Down
5 changes: 3 additions & 2 deletions src/main/clojure/forge_clj/entity.clj
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@
:on-load - called after loading nbt data, with an instance of this passed to it.
:on-save - called before saving nbt data, with an instance of this passed to it."
[name-ns class-name & args]
[class-name & args]
(let [classdata (apply hash-map args)
name-ns (get classdata :ns *ns*)
classdata (assoc classdata :interfaces (conj (get classdata :interfaces []) `IExtendedEntityProperties))
prefix (str class-name "-")
fullname (get-fullname name-ns class-name)
Expand All @@ -26,7 +27,7 @@
on-save (get classdata :on-save `(constantly nil))
classdata (dissoc classdata :on-load :on-save)]
`(do
(defassocclass ~name-ns ~class-name ~classdata)
(defassocclass ~class-name ~classdata)
(with-prefix ~prefix
(defn ~'loadNBTData [~'this ~'compound]
(read-tag-data! (~'.-data ~this-sym) ~'compound)
Expand Down
10 changes: 6 additions & 4 deletions src/main/clojure/forge_clj/event.clj
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,13 @@
`[])}) [~event] ~'void]))

(defmacro gen-events
"MACRO: Creates an event handler given the namespace, handler name, and a series of arguments representing the events
"MACRO: Creates an event handler given the handler name and a series of arguments representing the events
to be handled."
[name-ns handler-name & args]
(let [fullname (get-fullname name-ns handler-name)
events (apply hash-map args)
[handler-name & args]
(let [events (apply hash-map args)
name-ns (get events :ns *ns*)
events (dissoc events :ns)
fullname (get-fullname name-ns handler-name)
signitures (mapv gen-event-signiture events)
prefix (str handler-name "-")]
`(do
Expand Down
Loading

0 comments on commit eb43a35

Please sign in to comment.