This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-12-31
Channels
- # 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
Hi @U07FP7QJ0!
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 proxy
.
(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 :path
in deps.edn
, right?
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:
(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))
This is extremely barebones, but it seems I am on the right path. 🙂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 gen-class
.
Yes, I think so. But witchcraft.events might need a small update to detect the new event types