Runtime
MagicRuntime is the managed service container behind the bootstrap-first
setup. It gives you one place to access core services, register extra
components, manage closeable resources, and rebuild config-backed clients on
reload.
Why a runtime container
Without a container, a plugin ends up with a handful of static singletons or fields that each service reaches into, and shutdown becomes a manual list of "close this, then that, in the right order" that is easy to get wrong on reload or when a startup step fails halfway.
MagicRuntime holds the wired services (config, logger, lang, and anything you
add), hands them out by type or by name, and closes everything it manages in
reverse order when the plugin stops. Your shared code depends on one runtime
handle instead of on a specific platform. It also rebuilds config-backed clients
for you when the config reloads, so services stay in sync without teardown
boilerplate.
Getting A Runtime
The recommended path is buildRuntime():
The same pattern exists for every bootstrap helper:
BukkitBootstrapBungeeBootstrapVelocityBootstrapFabricBootstrapNeoForgeBootstrap
Custom platforms can also build MagicRuntime manually.
Core Components
Every runtime starts with typed components for the core services:
PlatformConfigManagerLoggerCoreLanguageManagerwhen configured
Access them via the typed component registry:
Use findComponent(...) when the component is optional and
requireComponent(...) when its absence is a bug.
Typed Components
Register and replace typed components at runtime:
findComponent(...) also matches assignable types, so requesting an interface
will find a registered implementation.
Inspecting And Reacting To State
Read the current registries or react when they change:
components() and namedComponents() return snapshots of the typed and named
registries. onStateChanged(...) fires after any component registry update, and
isClosed() guards against using a runtime after shutdown.
Named Components
MagicRuntime also exposes a named registry for dynamic resources:
Remove a named component when it is no longer needed:
findNamedComponent(name) also has an untyped overload returning
Optional<Object> when you do not need a type check.
Named components are especially useful for:
- reloadable clients
- plugin-owned service singletons
- resources keyed by logical role (
http.monitoring,ws.gateway)
Managed Resources
Use resource(...) when you want a stable named slot that closes replaced
resources automatically:
The resource is also exposed through the named component registry under the same name.
Config Bindings
Use bindConfig(...) when a closeable resource should rebuild automatically on
matching config reloads:
The binding is a small handle around the managed resource:
require()/current()— the current resource, throwing or as anOptional.configClass()— the bound config type;name()— the resource name.resource()— the underlyingMagicRuntimeResource.isClosed()/close()— lifecycle state and manual teardown.
This pattern works well for:
- HTTP clients
- WebSocket clients
- database pools
- SDK clients
The HTTP client module also provides higher-level wrappers:
MagicHttpClientProfileMagicWebSocketClientProfile
Use those when the resource being managed is specifically an HTTP or WebSocket client.
Lifecycle
MagicRuntime.close():
- unregisters its platform shutdown hook when one was installed
- closes managed resources in reverse registration order
- closes runtime resources and config bindings
- can shut down
ConfigManagerautomatically
Bootstrap helpers already configure the runtime so that magic.runtime().close()
is the one shutdown call you usually need in your plugin or mod.
You can register extra shutdown actions on a live runtime, not only through the builder:
Actions registered this way run during close() alongside managed resources, in
reverse registration order.
Building A Runtime Manually
For NeoForge or custom platforms, build it directly:
Builder controls:
languageManager(...)component(...)manage(...)onClose(...)manageConfigManager(...)autoRegisterShutdown(...)
Disable autoRegisterShutdown(...) when the platform already has an explicit
shutdown phase you want to own manually.