minecraft

plexus 2021-12-31T14:19:03.040400Z

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

devurandom 2021-12-31T17:44:32.042600Z

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

devurandom 2021-12-31T17:46:40.042800Z

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/9https://github.com/CardboardPowered/cardboard/issues/10 Do you have experience with running Witchcraft on Cardboard?

plexus 2021-12-31T17:52:26.043400Z

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.

plexus 2021-12-31T17:53:06.043600Z

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

🎉 1
devurandom 2022-01-01T20:45:58.043900Z

Hi @plexus! 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?

plexus 2022-01-01T20:54:29.044200Z

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...

🙏 1
plexus 2022-01-01T20:55:57.044400Z

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.

devurandom 2022-01-01T21:05:30.044600Z

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"))))

plexus 2022-01-01T21:36:51.045Z

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.

devurandom 2022-01-01T22:12:09.045200Z

I would add the compiled dispatcher class to a classes directory and add that to the :path in deps.edn, right?

devurandom 2022-01-01T22:13:10.045400Z

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)

devurandom 2022-01-01T22:15:44.045600Z

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. 🙂

plexus 2022-01-02T00:13:25.046200Z

hey, that's great!

plexus 2022-01-02T01:51:25.046400Z

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

plexus 2022-01-02T02:15:19.046600Z

Or maybe not 😄 not that easy to work with it seems

plexus 2022-01-02T04:08:57.046800Z

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)

plexus 2022-01-02T04:09:11.047Z

no need for the whole class registry shenanigans

🎉 1
devurandom 2022-01-02T08:54:21.047200Z

Oof, thanks for finding this. I was looking for such method, but must somehow have missed it.

devurandom 2022-01-02T09:00:22.047500Z

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.

plexus 2022-01-02T13:10:20.047700Z

Yes, I think so. But witchcraft.events might need a small update to detect the new event types

plexus 2021-12-31T14:27:28.041300Z

I just merged a bunch of improvements and additions I did over christmas, including an addition to the gallery of a classic mob spawner

2
🎉 2
plexus 2021-12-31T14:28:42.041700Z

released in witchcraft 0.8.201

plexus 2021-12-31T14:28:55.042100Z

if anyone else got creative over the holidays please share screenshots!