I am trying to build a plugin for a Minecraft Spigot server that ultimately I would like to be able to communicate over serial with things connected to my PC (server is running locally on the PC as well).
I have been able to build and run the plugin and manipulate player/blocks in the game so I know the build process for my base plugin is working. My trouble started when I began trying to include an extra dependency: jSerialComm
I added the dependency entry in my pom.xml file:
<dependency>
<groupId>com.fazecast</groupId>
<artifactId>jSerialComm</artifactId>
<version>[2.0.0,3.0.0)</version>
<scope>provided</scope>
</dependency>
I added some basic code to import classes from jSerialComm
and do something basic with them inside of a command in my plugin:
import com.fazecast.jSerialComm.SerialPort;
public class CommandCheck implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (sender instanceof Player){
System.out.println(SerialPort.getCommPorts());
}
return false;
}
}
This builds fine, I'm using InteliJ and it does recognize the SerialPort class (no red underlines).
But when this command runs in the game I get a Class Not Found error:
org.bukkit.command.CommandException: Unhandled exception executing command 'check' in plugin MyFirstPlugin v1.0-SNAPSHOT
at org.bukkit.command.PluginCommand.execute(PluginCommand.java:47) ~[spigot-api-1.18.1-R0.1-SNAPSHOT.jar:?]
at org.bukkit.command.SimpleCommandMap.dispatch(SimpleCommandMap.java:149) ~[spigot-api-1.18.1-R0.1-SNAPSHOT.jar:?]
at org.bukkit.craftbukkit.v1_18_R1.CraftServer.dispatchCommand(CraftServer.java:821) ~[spigot-1.18.1-R0.1-SNAPSHOT.jar:3380-Spigot-8965a50-0ba6b90]
at net.minecraft.server.network.PlayerConnection.a(PlayerConnection.java:1939) ~[spigot-1.18.1-R0.1-SNAPSHOT.jar:3380-Spigot-8965a50-0ba6b90]
at net.minecraft.server.network.PlayerConnection.a(PlayerConnection.java:1778) ~[spigot-1.18.1-R0.1-SNAPSHOT.jar:3380-Spigot-8965a50-0ba6b90]
at net.minecraft.server.network.PlayerConnection.a(PlayerConnection.java:1759) ~[spigot-1.18.1-R0.1-SNAPSHOT.jar:3380-Spigot-8965a50-0ba6b90]
at net.minecraft.network.protocol.game.PacketPlayInChat.a(PacketPlayInChat.java:46) ~[spigot-1.18.1-R0.1-SNAPSHOT.jar:3380-Spigot-8965a50-0ba6b90]
at net.minecraft.network.protocol.game.PacketPlayInChat.a(PacketPlayInChat.java:1) ~[spigot-1.18.1-R0.1-SNAPSHOT.jar:3380-Spigot-8965a50-0ba6b90]
at net.minecraft.network.protocol.PlayerConnectionUtils.lambda$0(PlayerConnectionUtils.java:30) ~[spigot-1.18.1-R0.1-SNAPSHOT.jar:3380-Spigot-8965a50-0ba6b90]
at net.minecraft.server.TickTask.run(SourceFile:18) ~[spigot-1.18.1-R0.1-SNAPSHOT.jar:3380-Spigot-8965a50-0ba6b90]
at net.minecraft.util.thread.IAsyncTaskHandler.c(SourceFile:151) ~[spigot-1.18.1-R0.1-SNAPSHOT.jar:3380-Spigot-8965a50-0ba6b90]
at net.minecraft.util.thread.IAsyncTaskHandlerReentrant.c(SourceFile:23) ~[spigot-1.18.1-R0.1-SNAPSHOT.jar:3380-Spigot-8965a50-0ba6b90]
at net.minecraft.server.MinecraftServer.b(MinecraftServer.java:1158) ~[spigot-1.18.1-R0.1-SNAPSHOT.jar:3380-Spigot-8965a50-0ba6b90]
at net.minecraft.server.MinecraftServer.c(MinecraftServer.java:1) ~[spigot-1.18.1-R0.1-SNAPSHOT.jar:3380-Spigot-8965a50-0ba6b90]
at net.minecraft.util.thread.IAsyncTaskHandler.y(SourceFile:125) ~[spigot-1.18.1-R0.1-SNAPSHOT.jar:3380-Spigot-8965a50-0ba6b90]
at net.minecraft.server.MinecraftServer.bf(MinecraftServer.java:1137) ~[spigot-1.18.1-R0.1-SNAPSHOT.jar:3380-Spigot-8965a50-0ba6b90]
at net.minecraft.server.MinecraftServer.y(MinecraftServer.java:1130) ~[spigot-1.18.1-R0.1-SNAPSHOT.jar:3380-Spigot-8965a50-0ba6b90]
at net.minecraft.util.thread.IAsyncTaskHandler.c(SourceFile:134) ~[spigot-1.18.1-R0.1-SNAPSHOT.jar:3380-Spigot-8965a50-0ba6b90]
at net.minecraft.server.MinecraftServer.x(MinecraftServer.java:1114) ~[spigot-1.18.1-R0.1-SNAPSHOT.jar:3380-Spigot-8965a50-0ba6b90]
at net.minecraft.server.MinecraftServer.w(MinecraftServer.java:1038) ~[spigot-1.18.1-R0.1-SNAPSHOT.jar:3380-Spigot-8965a50-0ba6b90]
at net.minecraft.server.MinecraftServer.lambda$0(MinecraftServer.java:304) ~[spigot-1.18.1-R0.1-SNAPSHOT.jar:3380-Spigot-8965a50-0ba6b90]
at java.lang.Thread.run(Thread.java:833) [?:?]
Caused by: java.lang.NoClassDefFoundError: com/fazecast/jSerialComm/SerialPort
at com.foamyguy.myfirstplugin.CommandCheck.onCommand(CommandCheck.java:21) ~[?:?]
at org.bukkit.command.PluginCommand.execute(PluginCommand.java:45) ~[spigot-api-1.18.1-R0.1-SNAPSHOT.jar:?]
... 21 more
Caused by: java.lang.ClassNotFoundException: com.fazecast.jSerialComm.SerialPort
at org.bukkit.plugin.java.PluginClassLoader.loadClass0(PluginClassLoader.java:147) ~[spigot-api-1.18.1-R0.1-SNAPSHOT.jar:?]
at org.bukkit.plugin.java.PluginClassLoader.loadClass(PluginClassLoader.java:99) ~[spigot-api-1.18.1-R0.1-SNAPSHOT.jar:?]
at java.lang.ClassLoader.loadClass(ClassLoader.java:520) ~[?:?]
at com.foamyguy.myfirstplugin.CommandCheck.onCommand(CommandCheck.java:21) ~[?:?]
at org.bukkit.command.PluginCommand.execute(PluginCommand.java:45) ~[spigot-api-1.18.1-R0.1-SNAPSHOT.jar:?]
... 21 more
I have unzipped and looked inside of the built jar file and the jSerialComm jar file is successfully being included within my plugin jar:
What do I need to do in order to successfully use an external JAR file (jSerialComm specifically) from inside of a Minecraft plugin? Or alternatively, is there some built-in way that I can connect and communicate over serial ports without needing an external JAR and therefore not needing anything "special" in order to work?
Even if the JAR is present in your plugin, the classes of the JAR are not loaded in the classpath and Spigot cannot access the classes.
You can use a plugin, such as the maven-shade-plugin, which copies all classes from your API-JAR to your Plugin-JAR.
First, set the scope from provided
to compile
.
<dependency>
<groupId>com.fazecast</groupId>
<artifactId>jSerialComm</artifactId>
<version>[2.0.0,3.0.0)</version>
<scope>compile</scope> <!-- -->
</dependency>
Then add the maven-shade-plugin
under build > plugins
inside your pom.xml
<build>
<!-- ... -->
<plugins>
<!-- ... -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- ... -->
</plugins>
<!-- ... -->
</build>
If you now build the jar (using e. g. man clean package
), there should be a "fat-" jar file in the target/ folder that contains the classes of the API as well as your classes.
Contents of your plugin before:
├ com
├ foamguy
└ myfirstplugin
└ ...
├ plugin.yml
└ jSerialComm-xxx.jar
Contents of your plugin after:
├ com
├ foamguy
└ myfirstplugin
└ ...
└ fazecast
└ jSerialComm
└ ...
└ plugin.yml