HomeJavaModsScalaLoader
ScalaLoader
So you want to write plugins in Scala? Great! Or not so great? Scala runtime classes are not on the class/module path by default. Server administrators could add them using the -classpath commandline option, but in reality most bukkit servers run in managed environments by minecraft-specialized server hosts. The standard workaround for this problem is to include the classes in your own plugin and relocate them using a shading plugin in your build process. While this does work, it means that Scala library classes are no longer shared between plugins, making them incompatible because the scala classes will be loaded by different classloaders. Since Spigot 1.16.5 there is the option to include the dependency in the plugin.yml, but this approach has the same problems. And there is the problem of unnecessary ram and disc usage too. As of Scala 2.12.6, the standard library has a size of 3.5 MB. The reflection library is another 5 MB. Using both libraries in multiple plugins results unnecessarily large plugins sizes, while really those Scala classes should only be loaded once. Introducing…

ScalaLoader

ScalaLoader uses a custom PluginLoader that loads the Scala runtime classes for you! You can use this Giter8 template to get started quickly!

Pros

Cons

Caveats


I know what I'm doing, just give me what I need!
Spoiler: Dependency information
Maven:
Code (XML):

<repositories>
    <repository>
        <id>jannyboy11-minecraft-repo </id>
        <url>https://repo.repsy.io/mvn/jannyboy11/minecraft/ </url>
    </repository>
</repositories>

<dependencies>
    <dependency>
        <groupId>com.janboerman.scalaloader </groupId>
        <artifactId>ScalaLoader </artifactId>
        <version>0.18.15-SNAPSHOT </version>
        <scope>provided </scope>
    </dependency>
</dependencies>
 
SBT:
Code (Scala):

resolvers + = "jannyboy11-minecraft-repo" at "https://repo.repsy.io/mvn/jannyboy11/minecraft"
libraryDependencies + = "com.janboerman.scalaloader" % "ScalaLoader" % "0.18.15-SNAPSHOT" % "provided"
 

Javadocs
Available on GitHub Pages.

Example plugin
Code (Scala):
package xyz. janboerman. scalaloader. example. scala

import org. bukkit. ChatColor
import org. bukkit. command. {CommandSender, Command }
import org. bukkit. event. {EventHandler, Listener }
import org. bukkit. event. player. PlayerJoinEvent
import org. bukkit. permissions. PermissionDefault
import xyz. janboerman. scalaloader. plugin. ScalaPluginDescription. {Command => SPCommand, Permission => SPPermission }
import xyz. janboerman. scalaloader. plugin. {ScalaPlugin, ScalaPluginDescription }
import xyz. janboerman. scalaloader. plugin. description. {Scala, ScalaVersion, Api, ApiVersion }

@Scala (version = ScalaVersion. v2_13_10 )
@Api (ApiVersion. v1_19 )
object ExamplePlugin
    extends ScalaPlugin ( new ScalaPluginDescription ( "ScalaExample", "0.1-SNAPSHOT" )
        . commands ( new SPCommand ( "foo" )
            . permission ( "scalaexample.foo" ) )
        . permissions ( new SPPermission ( "scalaexample.foo" )
            . permissionDefault (PermissionDefault. TRUE ) ) )
    with Listener {

    getLogger ( ). info ( "ScalaExample - I am constructed!" )

    override def onLoad ( ) : Unit = {
        getLogger. info ( "ScalaExample - I am loaded!" )
    }

    override def onEnable ( ) : Unit = {
        getLogger. info ( "ScalaExample - I am enabled!" )
        getServer. getPluginManager. registerEvents ( this, this )
    }

    override def onDisable ( ) : Unit = {
        getLogger. info ( "ScalaExample - I am disabled!" )
    }

    @EventHandler
    def onJoin (event : PlayerJoinEvent ) : Unit = {
        event. setJoinMessage (s "${ChatColor.GREEN}Howdy ${event.getPlayer.getName}!" )
    }

    override def onCommand (sender : CommandSender, command : Command, label : String, args : Array [String ] ) : Boolean = {
        sender. sendMessage ( "Executed foo command!" )
        true
    }

    def getInt ( ) = 42

}
Depending on a ScalaPlugin from a JavaPlugin
plugin.yml:
Code (YAML):
name : DummyPlugin
version
: 1.0
main
: xyz.janboerman.dummy.dummyplugin.DummyPlugin
depend
: [ScalaLoader ]
softdepend
: [ScalaExample ] #A hard dependency will not work! Your plugin will not load!
Java code:
Code (Java):
package xyz.janboerman.dummy.dummyplugin ;

import org.bukkit.plugin.java.JavaPlugin ;
import xyz.janboerman.scalaloader.compat.IScalaPluginLoader ;
import xyz. janboerman. scalaloader. example. scala. ExamplePlugin$ ;

public final class DummyPlugin extends JavaPlugin {

    @Override
    public void onEnable ( ) {
        //get the plugin instance
        ExamplePlugin$ plugin = (ExamplePlugin$ ) getServer ( ). getPluginManager ( ). getPlugin ( "ScalaExample" ) ;

        //make sure all classes from the scala plugin can be accessed
        IScalaPluginLoader. openUpToJavaPlugin (plugin, this ) ;

        //do whatever you want afterwards!
        getLogger ( ). info ( "We got " + plugin. getInt ( ) + " from Scala!" ) ;
    }

}

Commands & Permissions
Since this plugin is meant for other developers, the commands and permissions are probably not very useful to end-users. But for completeness here they are:
Spoiler: Commands & Permissions
  • /resetScalaUrls <scala version>: Resets the URLs in the config for a version of Scala. Useful if you overwrote some of them. If 'all' is provided as a scala version then all URLs will be reset. Permission: scalaloader.resetscalaurls
  • /listScalaPlugins: Outputs the names of all scala plugins. Binary-compatible plugins are shown on the same line. Permission: scalaloader.listscalaplugins
  • /dumpClass <plugin> <class file> <format>?: Outpus the bytecode of a class to the console in a textual representation. This command is mostly useful for myself, but if you fancy diving into Java bytecode then more power to you! Permission: scalaloader.dumpclass
  • /setDebug <class name>: Marks or unmarks a class as a class that should be dumped to the console by the classloader when it is loaded. A list is saved in the debug.yml file. Permission: scalaloader.setdebug
  • /classMembers <class name>: Dumps fields and methods visible to ScalaLoader to the command sender. Permission: scalaloader.classmembers
All permissions are by default only granted to server operators.

Usage statistics
ScalaLoader collects some anonymous metrics and sends that to bStats.org. In addition to the standard charts, you can see here which Scala versions are most in use. Opting out can be done by editing the <server_root>/plugins/bStats/config.yml file.

Chat & Support

GitHub issues is the place for bug reports and feature requests, but if you just want to chat about the project, or want to know more about how to use it, you can reach me via Discord: https://discord.gg/pb2cBBKdwq

Commands

Plugin details

Read from the plugin's own plugin.yml.

Quick facts

Install steps are the general flow for this file type — How to install Minecraft Java mods & modpacks walks through it step by step.

ScalaLoader is a free Minecraft Java mod. Compatible with Minecraft 1.8, 1.12, 1.15, 1.16 and newer. Downloaded 3,549 times (via Spigot). Download it and open it directly in the game.

Explore more