Skip to content

Bootstrap a Plugin

Nate Reprogle edited this page Jan 20, 2026 · 3 revisions

Bootstrap a Plugin

To get started with ByteLib, you must do the following steps:

  1. Create a paper-plugin.yml and list ByteLib's boostraper and loader classes
  2. Extend ByteLibPlugin on your main class (It can remain empty, and is recommended)
  3. Create a Google Guice module to register your plugin's dependencies and hook up DI
  4. Create one or more Lifecycle classes to do work during the onLoad, onEnable, and onDisable Paper lifecycles
  5. Create a wiring class to hook it all together

Bootstrapping in Detail

  1. Create a paper-plugin.yml in your src/main/resources folder (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
  1. Extend ByteLibPlugin in 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);
    }
}
  1. 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);
    }
}
  1. Create at least one Lifecycle class, which contains the standard onEnable, onLoad, and onDisable methods. Ensure this implements PluginLifecycle
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");
    }
}
  1. 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.

Clone this wiki locally