Paper Plugins
Paper plugins allow developers to take advantage of more modern concepts introduced by Mojang, such as datapacks, to expand the field of what the Paper API is able to introduce.
This is experimental and may be subject to change.
How do I use them?
Similarly to Bukkit plugins, you have to introduce a paper-plugin.yml
file into your jar resources folder.
This can act as a drop in replacement for plugin.yml
, allowing you to specifically target the Paper platform.
It should be noted you still have the ability to include both paper-plugin.yml
and plugin.yml
in the same jar.
Here is an example configuration.
name: Paper-Test-Plugin
version: '1.0'
main: io.papermc.testplugin.TestPlugin
description: Paper Test Plugin
api-version: '1.19'
bootstrapper: io.papermc.testplugin.TestPluginBootstrap
loader: io.papermc.testplugin.TestPluginLoader
Dependency Declaration
Dependency declaration is specified a bit differently than Bukkit plugins, as you are able to define if a dependency is required during bootstrapping. This means that any bootstrapping logic for your dependencies will be run before yours is run.
dependencies:
- name: DependencyName
required: true
bootstrap: true
If a dependency is required
it must be installed on the server or your plugin will fail to load. If marked otherwise, it will
ignore if the dependency isn't found.
Otherwise, your plugin will be loaded fine, in the same fashion as a plugin that is a soft-depend
in a Bukkit plugin.yml
If a dependency is marked as bootstrap
, this indicates that this dependency is required during bootstrapping, which currently does not serve a purpose.
Note that unlike Bukkit plugins, dependencies are independent of the plugin load order. This means that although declaring a dependency will give you access to its classes, if you require it to be initialised before your plugin is loaded, see how to declare load order.
Load Order Declaration
To declare the order in which plugins are loaded before/after your plugin, you are able to define two sections in your paper-plugin.yml
.
It should be noted that the load order for bootstrapping is independent, meaning that if you want to define the load order for bootstrappers
you must set the bootstrap
field to true
.
Note that in certain cases plugins may be able to introduce cyclic loading loops, which will prevent the server from starting. Please read the cyclic loading guide for more information.
load-before
You are able to define a list of plugins that your plugin should load before.
load-before:
- name: PluginName
bootstrap: false
This means that your plugin will load before PluginName
is loaded.
load-after
You are then able to define a list of plugins that your plugin should load after.
load-after:
- name: PluginName
bootstrap: false
This means that your plugin will load after PluginName
is loaded.
What is it used for?
Paper plugins lay down the framework for some future API. Our goals are to open more modern API that better aligns with Vanilla. Paper plugins allow us to do just that by making a new way to load plugin resources before the server has started by using Bootstrappers.
Bootstrapper
Paper plugins are able to identify their own bootstrapper by implementing io.papermc.paper.plugin.bootstrap.PluginBootstrap
and adding
the class of your implementation to the bootstrapper field in the paper-plugin.yml
.
public class TestPluginBootstrap implements PluginBootstrap {
@Override
public void bootstrap(@NotNull PluginProviderContext context) {
}
@Override
public @NotNull JavaPlugin createPlugin(@NotNull PluginProviderContext context) {
return new TestPlugin("My custom parameter");
}
}
A Bootstrapper allows you to also override how your plugin is initiated, allowing you to pass values into your plugin constructor. Currently, bootstrappers do not offer much new API, and are highly experimental. This may be subject to change once more API is introduced.
Loaders
Paper plugins are able to identify their own plugin loader by implementing io.papermc.paper.plugin.loader.PluginLoader
and adding
the class of your implementation to the loader field in the paper-plugin.yml
.
The goal of the plugin loader is the creation of an expected/dynamic environment for the plugin to load into. This, as of right now, only applies to creating the expected classpath for the plugin, e.g. supplying external libraries to the plugin.
public class TestPluginLoader implements PluginLoader {
@Override
public void classloader(@NotNull PluginClasspathBuilder classpathBuilder) {
classpathBuilder.addLibrary(new JarLibrary(Path.of("dependency.jar")));
MavenLibraryResolver resolver = new MavenLibraryResolver();
resolver.addDependency(new Dependency(new DefaultArtifact("com.example:example:version"), null));
resolver.addRepository(new RemoteRepository.Builder("paper", "default", "https://repo.papermc.io/repository/maven-public/").build());
classpathBuilder.addLibrary(resolver);
}
}
Currently, you are able to add two different library types, JarLibrary
, and MavenLibraryResolver
.
Differences
Bukkit Serialization System
Paper plugins do not support the serialization system (org.bukkit.configuration.serialization
) that Bukkit uses. Your classes will not be able to be
serialized, vise versa. It is highly recommended to not use this system with Paper plugins.
Classloading Isolation
Paper plugins have isolated classloaders, meaning that relocating dependencies will not be necessary. Paper plugins are not able to access each other unless given explicit access by depending on another plugin, etc. This prevents plugins from accidentally accessing your dependencies, and in general helps ensure that plugins are only able to access your plugin if they explicitly say that they're depending on it.
Paper plugins have the ability to bypass this, being able to access OTHER plugins classloaders by adding
has-open-classloader: true
to your paper-plugin.yml
. Note, other plugins will still be unable to access your classloader.
Load Order Logic Split
In order to better take advantage of classloading isolation, Paper plugins do not use the dependencies
field to determine load order.
This was done for a variety of reasons, mostly to allow better control and allow plugins to properly share classloaders.
See information on declaring load order for more information on how to declare the load order of your plugin.