Migration Guide¶
This page shows how to move from the older manual wiring style to the current bootstrap-first setup.
1. Manual Bootstrap -> buildRuntime()¶
Bukkit/Paper¶
Before:
Platform platform = new BukkitPlatformProvider(this);
ConfigManager configManager = new ConfigManager(platform);
Logger logger = new Logger(platform, this, configManager);
LanguageManager languageManager = new LanguageManager(this, configManager);
languageManager.init("en");
languageManager.addMagicUtilsMessages();
logger.setLanguageManager(languageManager);
Messages.register(getName(), languageManager);
After:
What you gain:
- one managed shutdown handle
- consistent logger/lang/messages wiring
- optional command registry integration
- easy access through
MagicRuntime
Fabric¶
Before:
Platform platform = new FabricPlatformProvider(server);
ConfigManager configManager = new ConfigManager(platform);
Logger logger = new Logger(platform, configManager, "MyMod");
After:
FabricBootstrap.RuntimeResult magic = FabricBootstrap.forMod("mymod", () -> server)
.buildRuntime();
Important: Fabric command registration still happens inside
CommandRegistrationCallback.EVENT.
Velocity¶
Before:
Platform platform = new VelocityPlatformProvider(proxy, slf4j, dataDirectory, this);
ConfigManager configManager = new ConfigManager(platform);
LoggerCore logger = new LoggerCore(platform, configManager, this, "MyPlugin");
After:
VelocityBootstrap.RuntimeResult magic = VelocityBootstrap.forPlugin(proxy, this, "MyPlugin", dataDirectory)
.slf4j(slf4j)
.buildRuntime();
2. initialize(...) -> Explicit Registry Or Bootstrap¶
Older code often used the static default registry:
CommandRegistry.initialize(plugin, "myplugin", logger);
CommandRegistry.register(plugin, new DonateCommand());
Preferred now:
CommandRegistry registry = CommandRegistry.create(plugin, "myplugin", logger);
registry.registerCommand(new DonateCommand());
Or through bootstrap:
BukkitBootstrap.RuntimeResult magic = BukkitBootstrap.forPlugin(plugin)
.permissionPrefix("myplugin")
.enableCommands()
.configureCommands(registry -> registry.registerCommand(new DonateCommand()))
.buildRuntime();
Why prefer this:
- easier testing
- fewer hidden globals
- clearer ownership in multi-plugin or multi-mod setups
3. CommandSpec.builder(...) -> MagicCommand.builder(...)¶
Older builder-based command code often produced a detached spec:
CommandSpec<CommandSender> spec = CommandSpec.<CommandSender>builder("donate")
.execute(ctx -> CommandResult.success("ok"))
.build();
registry.registerSpec(spec);
Preferred now:
MagicCommand command = MagicCommand.<CommandSender>builder("donate")
.execute(ctx -> CommandResult.success("ok"))
.build();
registry.registerCommand(command);
This matters because builder-authored commands are now real MagicCommand
instances. They can use the same runtime adaptation API as annotation-based
commands:
withName(...)addAlias(...)addSubCommand(...)setExecute(...)mount(existingCommand)
registerSpec(...) still works for migration, but it is now the compatibility
path rather than the primary one.
4. Ad-Hoc Reload Logic -> MagicRuntime Bindings¶
Older reload code often looked like this:
if (client != null) {
client.close();
}
client = MagicHttpClient.builder(platform, configManager)
.baseUrl(config.monitoring.baseUrl)
.build();
Preferred now:
MagicRuntimeConfigBinding<ServiceConfig, MagicHttpClient> binding = runtime.bindConfig(
"http.monitoring",
ServiceConfig.class,
config -> MagicHttpClient.builder(runtime.platform(), runtime.configManager())
.baseUrl(config.monitoring.baseUrl)
.build(),
"monitoring"
);
Or use the higher-level profile wrapper:
MagicHttpClientProfile<ServiceConfig> monitoring = MagicHttpClientProfile
.builder(runtime, "http.monitoring", ServiceConfig.class)
.sections("monitoring")
.baseUrl(config -> config.monitoring.baseUrl)
.build();
4. Manual Shutdown -> One Runtime Close¶
Before:
After:
That only works when the services were created through bootstrap or registered inside the runtime.
5. When Not To Migrate¶
Keep the manual style when:
- you are integrating MagicUtils into an unusual platform
- you need partial module wiring without the bootstrap defaults
- you intentionally manage service lifecycles yourself
Even in those cases, MagicRuntime is still useful as a local lifecycle
container.