-
Notifications
You must be signed in to change notification settings - Fork 0
Bootstrap a Plugin
Nate Reprogle edited this page Jan 20, 2026
·
3 revisions
To get started with ByteLib, you must do the following steps:
- Create a
paper-plugin.ymland list ByteLib's boostraper and loader classes - Extend
ByteLibPluginon your main class (It can remain empty, and is recommended) - Create a Google Guice module to register your plugin's dependencies and hook up DI
- Create one or more Lifecycle classes to do work during the
onLoad,onEnable, andonDisablePaper lifecycles - Create a wiring class to hook it all together
- Create a
paper-plugin.ymlin yoursrc/main/resourcesfolder (Example from DimensionPause below)
name: DimensionPause
version: "2.0.0"
main: org.reprogle.dimensionpause.DimensionPausePlugin
api-version: "1.21"
bootstrapper: org.reprogle.bytelib.BytePluginBootstrap
loader: org.reprogle.bytelib.BytePluginLoader- Extend
ByteLibPluginin your main class
public final class DimensionPausePlugin extends ByteLibPlugin {
@Inject
public DimensionPausePlugin(Injector injector, PluginMeta meta, Path dataDir, ComponentLogger logger) {
super(injector, meta, dataDir, logger);
}
}- Create a Module which is used to configure Guice bindings (Example from DimensionPause)
public final class DimensionPauseModule extends AbstractModule {
@Override
protected void configure() {
// Core services
bind(CommandFeedback.class).in(Singleton.class);
// Subcommands
Multibinder<SubCommand> subcommandBinder = Multibinder.newSetBinder(binder(), SubCommand.class);
subcommandBinder.addBinding().to(Reload.class);
subcommandBinder.addBinding().to(State.class);
subcommandBinder.addBinding().to(Toggle.class);
// Lifecycle hooks
Multibinder<PluginLifecycle> lifecycles = Multibinder.newSetBinder(binder(), PluginLifecycle.class);
lifecycles.addBinding().to(DimensionPauseLifecycle.class);
// Commands
Multibinder.newSetBinder(binder(), CommandRegistration.class)
.addBinding()
.to(DPCommandRegistration.class);
}
}- Create at least one Lifecycle class, which contains the standard
onEnable,onLoad, andonDisablemethods. Ensure this implementsPluginLifecycle
public class DimensionPauseLifecycle implements PluginLifecycle {
private final ComponentLogger logger;
@Inject
public DimensionPauseLifecycle(ComponentLogger logger) {
this.logger = logger;
}
@Override
public void onEnable() {
logger.info("Dimension Pause has been loaded");
}
@Override
public void onDisable() {
logger.info("Dimension Pause is shutting down");
}
}- Wire up Guice using one of three conventions:
// Create a class named `[MainClass]Wiring.class` and implement PluginWiring
// Ensure it returns your Module you made in step 3
public static class DimensionPauseWiring implements PluginWiring {
@Override
public List<Module> modules(PluginMeta meta, Path dataDir, ComponentLogger logger) {
return List.of(
new DimensionPauseModule()
);
}
}
//
// OR
//
// Nest a `Wiring` class in your plugin's main class
public final class DimensionPausePlugin extends ByteLibPlugin {
@Inject
public DimensionPausePlugin(Injector injector, PluginMeta meta, Path dataDir, ComponentLogger logger) {
super(injector, meta, dataDir, logger);
}
@SuppressWarnings("unused")
public static class Wiring implements PluginWiring {
@Override
public List<Module> modules(PluginMeta meta, Path dataDir, ComponentLogger logger) {
return List.of(
new DimensionPauseModule()
);
}
}
}
//
// OR
//
// Create a class that implements PluginWiring, and allow the `ServiceLoader` to attempt to locate it (Not recommended)
public static class PluginWiringSetup extends PluginWiring {
@Override
public List<Module> modules(PluginMeta meta, Path dataDir, ComponentLogger logger) {
return List.of(
new DimensionPauseModule()
);
}
}If this seems complex, it's because it is. If DI wasn't involved, this would be much easier, but DI provides the immediate benefit of decoupling your code, making it easier to develop.