This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
- # adventofcode (4)
- # ai (1)
- # announcements (13)
- # babashka (1)
- # beginners (42)
- # calva (15)
- # chlorine-clover (28)
- # cider (7)
- # cljsrn (1)
- # clojure (3)
- # clojure-china (1)
- # clojure-dev (4)
- # clojure-europe (7)
- # clojure-losangeles (1)
- # clojure-nl (3)
- # clojure-nlp (2)
- # clojure-sweden (12)
- # clojure-uk (2)
- # clojurescript (30)
- # code-reviews (36)
- # core-async (5)
- # cursive (10)
- # data-science (1)
- # datalevin (1)
- # fulcro (11)
- # introduce-yourself (1)
- # lsp (10)
- # malli (7)
- # minecraft (24)
- # missionary (10)
- # monads (6)
- # nrepl (4)
- # off-topic (11)
- # portal (4)
- # rdf (1)
- # reagent (3)
- # releases (4)
- # shadow-cljs (4)
- # spacemacs (4)
Hey @devurandom, I looked at that a tiny bit, main conclusion is that there is nothing in Bukkit for this, so we can't really offer a cross-server solution in Witchcraft. I've been meaning to have a look at the Citizens API but haven't really had the chance. https://wiki.citizensnpcs.co/Citizens_Wiki
There are some tutorials how to do it with Fabric, which supposedly has a more stable API than NMS, but Fabric does not seem to be supported on a Paper server: https://fabricmc.net/wiki/faq:user#can_fabric_run_together_with_bukkit_spigot_paper
There is am implementation of the Paper API on top of Fabric (https://cardboardpowered.org/), but it seems to still be missing some features: • https://github.com/CardboardPowered/cardboard/issues/9 • https://github.com/CardboardPowered/cardboard/issues/10 Do you have experience with running Witchcraft on Cardboard?
Interesting, I did not know about Cardboard... this is all such a big space to explore. It's funny that Fabric was created to get rid of Bukkit/Forge style APIs, instead using NMS directly, but there's such a big legacy of Bukkit plugins that it makes sense for people to do this.
Targeting Cardboard with Witchcraft should be very doable, we're all set up as well to deal with whatever little incompatibliities there are in a transparent way
I took a look at Citizens, but for that I appear to have to create classes with Java annotations (cf. https://wiki.citizensnpcs.co/API#Creating_a_Trait -- the event handler methods on the trait have an
EventHandler annotation). To get those I appear to need
gen-class (`proxy` does not appear to support Java annotations), which means I need to compile the code and have the resulting class on the classpath. However
compile refuses to obey, because it needs the code to be in a file.
I have very little experience with Java->Clojure interop so far and feel like I am going down a (the wrong) rabbit hole here. Do you have an advice?
I see, that sounds very annoying. At this point I'd probably start looking at the Java code to see how they do things to see what the options are...
I've done a lot of Clojure/Java interop but I don't have a good answer off the top of my head, I'd have to look at it closer and try things out. This kind of stuff is where they need concrete classes is where the interop gets super annoying, so maybe citizens isn't for us. Sometimes you can peel back a layer and use some of the functionality in a more direct way, thus bypassing all this java wiring stuff, but hard to say right now.
I also briefly tried to use
proxy to see how far I get with that, but Citizens complains that
Trait class must have a no-arguments constructor, which apparently cannot be satisfied by
(defonce MyTrait (proxy [Trait] ["mytrait"])) (.. CitizensAPI getTraitFactory (registerTrait (.. TraitInfo (create (class MyTrait)) (withName "mytrait"))))
I'll see if I still have time to poke at this a bit. I had a bit of look at their code and as usual everything is locked down so a workaround to avoid making concrete classes would be quite involved. I think gen-class might be the best option in the end... generate a thin wrapper that dispatches to plain functions so you can redefine them afterwards. That will include a compilation step, and figuring out where the compiled output should go so it gets picked up.
I would add the compiled dispatcher class to a
classes directory and add that to the
The code that I could not get to work because of the constructor issue:
(def trait-name "mytrait") (defonce myTrait (proxy [Trait] [trait-name] (load [^DataKey key]) (save [^DataKey key]) (run ) (onAttach  (println trait-name "attached to" (.getNPC this))) (onDespawn ) (onSpawn ) (onRemove ))) (defn register-trait [^Class trait-class ^String trait-name] (.. CitizensAPI getTraitFactory (registerTrait (.. TraitInfo (create trait-class) (withName trait-name))))) (register-trait (class myTrait) trait-name)
Meanwhile I went from traits to playing with behaviours and that works reasonably well:
This is extremely barebones, but it seems I am on the right path. 🙂
(defonce thief (.. CitizensAPI getNPCRegistry (createNPC EntityType/PLAYER "fullwall"))) (.spawn thief (-> (wc/player) (wc/location))) (.. thief getNavigator (setTarget (-> (wc/player) (wc/location)))) (comment (.. thief getDefaultGoalController (addGoal (WanderGoal/createWithNPC thief) 1))) ; A barebones version of TargetNearbyEntityGoal: (def meanBehaviour (proxy [Behavior]  (reset ) (run  BehaviorStatus/RUNNING) (shouldExecute  (if (.. thief isSpawned) (do (.. thief getNavigator (setTarget (wc/player) true)) true) false)))) (.. thief getDefaultGoalController clear) (.. thief getDefaultGoalController (addBehavior meanBehaviour 1))
I think ByteBuddy may hold the answer to using Traits
(.. (net.bytebuddy.ByteBuddy.) (subclass Trait) (make) (load (lambdaisland.classpath/context-classloader)) (getLoaded)) ;;=> net.citizensnpcs.api.trait.Trait$ByteBuddy$dImbs8fA
ok had a better look at AbstractNPC and I think this might actually be enough
(defonce thief (.. CitizensAPI getNPCRegistry (createNPC EntityType/PLAYER "fullwall"))) (def t (proxy [Trait] ["my-trait"] (click [e] (println "Trait got click!")) (onAttach  (println "Trait got attached!")))) (.addTrait thief t)
Oof, thanks for finding this. I was looking for such method, but must somehow have missed it.
To handle events I would hook into the global event handlers that I already know from Witchcraft's block manipulation, right? Then I just need a map to find the NPC object for an entity, but can avoid the
@EventHandler Java annotation and thus
Yes, I think so. But witchcraft.events might need a small update to detect the new event types
I just merged a bunch of improvements and additions I did over christmas, including an addition to the gallery of a classic mob spawner