Deeplearning4j for Minecraft Plugins (Doesn't work due to different entry point)

Hi,

Newer versions of deeplearning4j (e.g. 1.0.0-M1) do not work for Minecraft Server plugins (https://www.spigotmc.org/ or https://papermc.io/).

For a minecraft server plugin, I specify an entry point. For example, the server initiates my code by executing “onEnable()” in “joehot200.MyPackage.MyClass”, which I specify in a YML file.

However, this means that deeplearning4j cannot find it’s libraries, being unable to load the logger ("
SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder”) or the Nd4j backend (“Caused by: java.lang.RuntimeException: org.nd4j.linalg.factory.Nd4jBackend$NoAva
ilableBackendException: Please ensure that you have an nd4j backend on your clas
spath.”).

I have tested this extensively. For example, if I make a runnable jar, it works as expected. But run the exact same code or even exact same .jar file through an entry point of a minecraft plugin, and it does not work.

@Joehot200 I’d like to help you get this resolved. Could you give me a github repo? I’m not really familiar with how to test dl4j in a minecraft environment but I would love to learn.

Thanks for your reply @agibsonccc! Here is a GitHub link with my plugin’s code, which is the IrisClassifier but run through Minecraft.

As you have no experience with Minecraft, I have uploaded a complete working example here:
http://endcraft.net/ToDL4jDevs.rar - This will open up to look like this:


To run the server, simply click “run.bat” and the server will start. The server software is free, and anybody is fully licensed to run it.

By running the server, you will run my IrisClassifier in the “plugins” folder. This file is bulky as I have shaded all of the DL4j libraries into it - sorry! The code used in my plugin is exactly the same code as I have posted in github above.

After starting the server, look in the server console and wait for a message like this:

[13:48:31 INFO]: [IrisClassifier_Minecraft] Enabling IrisClassifier_Minecraft v1
.0
[13:48:31 ERROR]: Error occurred while enabling IrisClassifier_Minecraft v1.0 (I
s it up to date?)
java.lang.ExceptionInInitializerError....
.... bla bla etc.

@Joehot200 Could you give me the maven coordinates for the minecraft plugin api you’re using? I’ve tried a few and all of them seem to be invalid. The snapshots are out of date enough that I can’t even download them.

My goal is to build an uber jar with everything needed to replace the plugin in your directory to see the problem. I can currently run your example but I can’t update it. Thanks!

@agibsonccc Maven can be found here:

<repositories>
    <!-- This adds the Spigot Maven repository to the build -->
    <repository>
        <id>spigot-repo</id>
        <url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
    </repository>
</repositories>

<dependencies>
    <!--This adds the Spigot API artifact to the build -->
    <dependency>
           <groupId>org.spigotmc</groupId>
           <artifactId>spigot-api</artifactId>
           <!-- For DL4J testing purposes the version doesn't matter. -->
           <version>1.8.8-R0.1-SNAPSHOT</version> 
           <!-- <version>1.18-R0.1-SNAPSHOT</version> -->
           <scope>provided</scope>
    </dependency>
</dependencies>

I have updated my GitHub example (GitHub - joehot200/AA) to contain the plugin.yml file that you need to put in the root of your JAR file for it the plugin run. Note that if you change the package name (com.joehot200.DL4j) or class name (IrisClassifier) you will have to change the main: com.joehot200.DL4j.IrisClassifier property in the plugin.yml to match it.

Let me know if you need anything else, I sometimes forget how many weird quirks minecraft has compared to ‘normal’ programming.

@Joehot200 this is definitely a classloader issue. It seems to not be able to load the resources we bundle in our jar. Do you know if minecraft requires us to set a custom plugin classloader? Some googling says I’m not the only one who’s ran in to this classloader per plugin issue.

@agibsonccc Classloaders are somewhat outside my area of expertise, sorry. That said, plugins do not commonly require a custom class loader. If it helps, here is a link to Spigot’s PluginClassLoader: Spigot-API/PluginClassLoader.java at master · SpigotMC/Spigot-API · GitHub

This spigot thread may be useful: https://www.spigotmc.org/threads/registering-a-custom-pluginloader.449746/

Just to confuse you some more, I should add the fact that alpha builds of DL4j work. If you change the DL4j version in pom.xml to <dl4j.version>1.0.0-alpha</dl4j.version> and make no other changes the plugin will load successfully.

@Joehot200 got it let me see what I can do. I’ll have to look in to how minecraft’s classloader handles resources. From what I’m seeing it can’t load our bundled native binaries for some reason.

Can you clarify that there is some issue with security/permissions + loading bundled artifacts I should be aware of?

1 Like

@agibsonccc

Can you clarify that there is some issue with security/permissions + loading bundled artifacts I should be aware of?

I’m not entirely sure what you mean, I am unaware of any inherent security flaws or problems with loading bundled resources, and indeed bundling resources into a Spigot plugin is often recommended (e.g. guava, apache libraries etc) by the developers of Spigot itself.

DL4j is vastly bigger than the usual libraries that are bundled however the principle should be the same.

@Joehot200 Something like java’s SecurityManager is known to break resource loading. I wasn’t sure if Minecraft had an equivalent of that. I’ll look more in to spiggot to see if there’s anything like resource size limitations among other things. Thanks!

Ah, I see. I haven’t heard of any sort of SecurityManager for Spigot.

Do I understand this correctly, that starting with beta it started being a problem? We introduced a change in Classloader handling going from beta6 to beta7:

@treo

Do I understand this correctly, that starting with beta it started being a problem?

That’s correct, however the issue may have come before beta7, as in my testing using version 1.0.0-alpha works, however version 1.0.0-beta2 does not. Is it possible that there were some changes during this time as well?

(Sorry to create so much trouble for you guys, really appreciate the help)

@Joehot200 add this to your plugin and it will work:

  ND4JClassLoading.setNd4jClassloader(this.getClassLoader());

Full class:

public class DL4jMinecraft extends JavaPlugin  {
    @Override
    public InputStream getResource(String filename) {
        return super.getResource(filename);
    }

    @Override
    public void onEnable() {
        super.onEnable();
        System.setProperty("org.bytedeco.javacpp.logger.debug","true");
        ND4JClassLoading.setNd4jClassloader(this.getClassLoader());
        INDArray arr = Nd4j.ones(1);
        System.out.println("Created ndarray");
    }
}

Note the: this.getClassLoader() call. Similar to the thread you link I think we do use the context classloader by default which caused this issue.

Note we also have a:
Dl4jClassLoading as well.

That will be:

import org.deeplearning4j.common.config.DL4JClassLoading; 

DL4JClassLoading.setDl4jClassloader(this.getClassLoader());

1 Like

Looks like it works!! The code is definitely executing, creating a model and then telling me I’m a fool for passing the wrong array of inputs to the output (which you probably noticed and were like “haha what a fool”) just as expected!

image

I will let you know if I encounter any more problems related to this, but as far as I can tell for all intents and purposes I believe this is now SOLVED. Thank you so much for your help.

See you around!

Cheers
Joe