0

I'm new to clojure..

Suppose that a server loads plugins by loading jar file on runtime.

And the server itself has many classes that must be initialized after server startup. (these classes will check if server if running while constructing...)

Can clojure get involved in such a scene?


Here is the detailed

I want to create a Slimefun addon. And I just want to use clojure to setup items

But as we all know, in minecraft dev almost every thing should only be initialized after server start, and the classes will also check if the server is running when constructing (such as ItemStack)

And during clojure compilation, codes seem to be executed. (I don't know if this is right...). That meens if my code involves classes such as ItemStack, then the compilation will fail.

So, if I want to dev spigot plugin involving Clojure, what should I do?


compilation log:

freeze-dolphin@stardust:~/Documents/workspace/idea/Dumortierite/clj-module$ lein jar
Compiling io.sn.dumortierite.clj-module.items
Reflection warning, io/sn/dumortierite/clj_module/items.clj:15:1 - call to io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack ctor can't be resolved.
Syntax error macroexpanding def at (items.clj:15:1).
Execution error (NullPointerException) at org.bukkit.Bukkit/getItemFactory (Bukkit.java:1831).
Cannot invoke "org.bukkit.Server.getItemFactory()" because "org.bukkit.Bukkit.server" is null

Full report at:
/tmp/clojure-2943387361270522517.edn
Compilation failed: Subprocess failed (exit code: 1)

Here is the full report: https://pastebin.com/xzmxHp5q


Also if intrested in how to combine leiningen and gradle you can check out the repo used above: https://github.com/freeze-dolphin/Dumortierite/tree/master/clj-module

amalloy
  • 89,153
  • 8
  • 140
  • 205
  • 1
    When you see things like this while _compiling_ it's very likely you are doing something at the top level (e.g. a `def`) you only ever want to do when _running_ - you need to "hide" this from the compiler -- but when looking at the linked repo I don't find the word "server", which I would have expected somewhere. So generic hint: compiling clojure is always to some degree running clojure -- which often comes down to "don't def heavy things". – cfrick Jun 14 '23 at 06:26
  • I've reverted your edit. On Stack Overflow, we don't edit solutions into questions. You've accepted my answer, which is a clear enough signal that the problem is solved. If there are important details you think my answer doesn't cover, you're free to comment on it, or add an additional Answer with the details. You can retrieve your edit from the [revision history](https://stackoverflow.com/revisions/76469477/2) if it's helpful. – amalloy Jun 15 '23 at 07:15

2 Answers2

2

Thanks for including the full stacktrace - a lot of newcomers don't, and it's very frustrating because it invariably contains useful information. In this case, it shows that, unsurprisingly, cfrick's guess is right. Specifically, at line 15 of items.clj, you have written

(def something (whatever (org.bukkit.Bukkit/getItemFactory)))

This means that, during compilation, you are calling getItemFactory. Generally speaking you should depend on very little during compilation. Top level defs should just use function, numbers, strings, sequences, lightweight stuff like that. Anything that you don't need until runtime should be deferred, usually by putting it inside a function defined by defn:

(defn something [args] (whatever (org.bukkit.Bukkit/getItemFactory)))

which you only call at runtime, once you're ready to run your initialization code.

If you need something variable-like at the top level despite its expensive dependencies, of course you don't want to initialize it every time you call the function it contains. The easiest solution is to use delay:

(def something (delay (whatever ...)))

Then you dereference it with @something, which calls whatever only the first time you dereference it.

amalloy
  • 89,153
  • 8
  • 140
  • 205
0

I'm not familiar with Minecraft, etc, but anything you can do with Java can also be done with Clojure. If you know how to write a Java "plugin", you should be able to "easily" convert that to Clojure by using a thin Java "bridge".

All the details are described in this question.

See also this question.

Alan Thompson
  • 29,276
  • 6
  • 41
  • 48
  • So I should write a plugin api wrapper first and then call the wrapper from clojure? – Freeze Dolphin Jun 14 '23 at 01:48
  • It's the reverse - Java will call Clojure. Although if it's an option, I would try a Clojure-only solution. Clojure can generate Java classes with the desired API with some caveats, and it's well documented. – Eugene Pakhomov Jun 14 '23 at 07:41