[{"data":1,"prerenderedAt":492},["ShallowReactive",2],{"docs-\u002Fgetting-started\u002Fruntime":3},{"id":4,"title":5,"body":6,"description":482,"extension":483,"meta":484,"navTitle":485,"navigation":486,"path":487,"rawbody":488,"seo":489,"stem":490,"__hash__":491},"docs\u002Fgetting-started\u002Fruntime.md","Runtime",{"type":7,"value":8,"toc":467},"minimark",[9,13,21,26,29,34,38,45,55,58,87,93,97,100,123,126,132,143,147,150,156,161,166,169,175,193,197,202,208,211,217,227,230,249,253,259,265,268,272,278,284,287,330,333,347,350,362,365,369,374,391,398,401,407,413,417,420,426,429,461],[10,11,5],"h1",{"id":12},"runtime",[14,15,16,20],"p",{},[17,18,19],"code",{},"MagicRuntime"," is the managed service container behind the bootstrap-first\nsetup. It gives you one place to access core services, register extra\ncomponents, manage closeable resources, and rebuild config-backed clients on\nreload.",[22,23,25],"h2",{"id":24},"why-a-runtime-container","Why a runtime container",[14,27,28],{},"Without a container, a plugin ends up with a handful of static singletons or\nfields that each service reaches into, and shutdown becomes a manual list of\n\"close this, then that, in the right order\" that is easy to get wrong on reload\nor when a startup step fails halfway.",[14,30,31,33],{},[17,32,19],{}," holds the wired services (config, logger, lang, and anything you\nadd), hands them out by type or by name, and closes everything it manages in\nreverse order when the plugin stops. Your shared code depends on one runtime\nhandle instead of on a specific platform. It also rebuilds config-backed clients\nfor you when the config reloads, so services stay in sync without teardown\nboilerplate.",[22,35,37],{"id":36},"getting-a-runtime","Getting A Runtime",[14,39,40,41,44],{},"The recommended path is ",[17,42,43],{},"buildRuntime()",":",[46,47,53],"pre",{"className":48,"code":50,"language":51,"meta":52},[49],"language-java","BukkitBootstrap.RuntimeResult magic = BukkitBootstrap.forPlugin(this)\n        .enableCommands()\n        .buildRuntime();\n\nMagicRuntime runtime = magic.runtime();\n","java","",[17,54,50],{"__ignoreMap":52},[14,56,57],{},"The same pattern exists for every bootstrap helper:",[59,60,61,67,72,77,82],"ul",{},[62,63,64],"li",{},[17,65,66],{},"BukkitBootstrap",[62,68,69],{},[17,70,71],{},"BungeeBootstrap",[62,73,74],{},[17,75,76],{},"VelocityBootstrap",[62,78,79],{},[17,80,81],{},"FabricBootstrap",[62,83,84],{},[17,85,86],{},"NeoForgeBootstrap",[14,88,89,90,92],{},"Custom platforms can also build ",[17,91,19],{}," manually.",[22,94,96],{"id":95},"core-components","Core Components",[14,98,99],{},"Every runtime starts with typed components for the core services:",[59,101,102,107,112,117],{},[62,103,104],{},[17,105,106],{},"Platform",[62,108,109],{},[17,110,111],{},"ConfigManager",[62,113,114],{},[17,115,116],{},"LoggerCore",[62,118,119,122],{},[17,120,121],{},"LanguageManager"," when configured",[14,124,125],{},"Access them via the typed component registry:",[46,127,130],{"className":128,"code":129,"language":51,"meta":52},[49],"Platform platform = runtime.requireComponent(Platform.class);\nConfigManager configManager = runtime.requireComponent(ConfigManager.class);\nLoggerCore logger = runtime.requireComponent(LoggerCore.class);\nCommandRegistry commands = runtime.findComponent(CommandRegistry.class).orElse(null);\n",[17,131,129],{"__ignoreMap":52},[14,133,134,135,138,139,142],{},"Use ",[17,136,137],{},"findComponent(...)"," when the component is optional and\n",[17,140,141],{},"requireComponent(...)"," when its absence is a bug.",[22,144,146],{"id":145},"typed-components","Typed Components",[14,148,149],{},"Register and replace typed components at runtime:",[46,151,154],{"className":152,"code":153,"language":51,"meta":52},[49],"runtime.putComponent(MyService.class, new MyService());\n\nMyService service = runtime.requireComponent(MyService.class);\nOptional\u003CMyService> opt = runtime.findComponent(MyService.class);\n",[17,155,153],{"__ignoreMap":52},[14,157,158,160],{},[17,159,137],{}," also matches assignable types, so requesting an interface\nwill find a registered implementation.",[162,163,165],"h3",{"id":164},"inspecting-and-reacting-to-state","Inspecting And Reacting To State",[14,167,168],{},"Read the current registries or react when they change:",[46,170,173],{"className":171,"code":172,"language":51,"meta":52},[49],"Map\u003CClass\u003C?>, Object> typed = runtime.components();\nMap\u003CString, Object> named = runtime.namedComponents();\n\nruntime.onStateChanged(() -> logger.debug(\"Runtime components changed\"));\n\nif (runtime.isClosed()) {\n    return; \u002F\u002F don't touch a closed runtime\n}\n",[17,174,172],{"__ignoreMap":52},[14,176,177,180,181,184,185,188,189,192],{},[17,178,179],{},"components()"," and ",[17,182,183],{},"namedComponents()"," return snapshots of the typed and named\nregistries. ",[17,186,187],{},"onStateChanged(...)"," fires after any component registry update, and\n",[17,190,191],{},"isClosed()"," guards against using a runtime after shutdown.",[22,194,196],{"id":195},"named-components","Named Components",[14,198,199,201],{},[17,200,19],{}," also exposes a named registry for dynamic resources:",[46,203,206],{"className":204,"code":205,"language":51,"meta":52},[49],"runtime.putNamedComponent(\"service.cache\", cacheClient);\n\nCacheClient cache = runtime.requireNamedComponent(\"service.cache\", CacheClient.class);\nOptional\u003CCacheClient> opt = runtime.findNamedComponent(\"service.cache\", CacheClient.class);\n",[17,207,205],{"__ignoreMap":52},[14,209,210],{},"Remove a named component when it is no longer needed:",[46,212,215],{"className":213,"code":214,"language":51,"meta":52},[49],"runtime.removeNamedComponent(\"service.cache\");\n",[17,216,214],{"__ignoreMap":52},[14,218,219,222,223,226],{},[17,220,221],{},"findNamedComponent(name)"," also has an untyped overload returning\n",[17,224,225],{},"Optional\u003CObject>"," when you do not need a type check.",[14,228,229],{},"Named components are especially useful for:",[59,231,232,235,238],{},[62,233,234],{},"reloadable clients",[62,236,237],{},"plugin-owned service singletons",[62,239,240,241,244,245,248],{},"resources keyed by logical role (",[17,242,243],{},"http.monitoring",", ",[17,246,247],{},"ws.gateway",")",[22,250,252],{"id":251},"managed-resources","Managed Resources",[14,254,134,255,258],{},[17,256,257],{},"resource(...)"," when you want a stable named slot that closes replaced\nresources automatically:",[46,260,263],{"className":261,"code":262,"language":51,"meta":52},[49],"MagicRuntimeResource\u003CMagicHttpClient> monitoring = runtime.resource(\n        \"http.monitoring\",\n        MagicHttpClient.builder(runtime.platform(), runtime.configManager())\n                .baseUrl(\"https:\u002F\u002Fapi.example.com\u002F\")\n                .build()\n);\n\nMagicHttpClient client = monitoring.require();\nmonitoring.set(MagicHttpClient.builder(runtime.platform(), runtime.configManager())\n        .baseUrl(\"https:\u002F\u002Fapi-two.example.com\u002F\")\n        .build());\n",[17,264,262],{"__ignoreMap":52},[14,266,267],{},"The resource is also exposed through the named component registry under the same\nname.",[22,269,271],{"id":270},"config-bindings","Config Bindings",[14,273,134,274,277],{},[17,275,276],{},"bindConfig(...)"," when a closeable resource should rebuild automatically on\nmatching config reloads:",[46,279,282],{"className":280,"code":281,"language":51,"meta":52},[49],"MagicRuntimeConfigBinding\u003CServiceConfig, MagicHttpClient> binding = runtime.bindConfig(\n        \"http.monitoring\",\n        ServiceConfig.class,\n        config -> MagicHttpClient.builder(runtime.platform(), runtime.configManager())\n                .baseUrl(config.monitoring.baseUrl)\n                .build(),\n        \"monitoring\"\n);\n\nMagicHttpClient client = binding.require();\n",[17,283,281],{"__ignoreMap":52},[14,285,286],{},"The binding is a small handle around the managed resource:",[59,288,289,303,313,322],{},[62,290,291,294,295,298,299,302],{},[17,292,293],{},"require()"," \u002F ",[17,296,297],{},"current()"," — the current resource, throwing or as an ",[17,300,301],{},"Optional",".",[62,304,305,308,309,312],{},[17,306,307],{},"configClass()"," — the bound config type; ",[17,310,311],{},"name()"," — the resource name.",[62,314,315,318,319,302],{},[17,316,317],{},"resource()"," — the underlying ",[17,320,321],{},"MagicRuntimeResource",[62,323,324,294,326,329],{},[17,325,191],{},[17,327,328],{},"close()"," — lifecycle state and manual teardown.",[14,331,332],{},"This pattern works well for:",[59,334,335,338,341,344],{},[62,336,337],{},"HTTP clients",[62,339,340],{},"WebSocket clients",[62,342,343],{},"database pools",[62,345,346],{},"SDK clients",[14,348,349],{},"The HTTP client module also provides higher-level wrappers:",[59,351,352,357],{},[62,353,354],{},[17,355,356],{},"MagicHttpClientProfile",[62,358,359],{},[17,360,361],{},"MagicWebSocketClientProfile",[14,363,364],{},"Use those when the resource being managed is specifically an HTTP or WebSocket\nclient.",[22,366,368],{"id":367},"lifecycle","Lifecycle",[14,370,371,44],{},[17,372,373],{},"MagicRuntime.close()",[59,375,376,379,382,385],{},[62,377,378],{},"unregisters its platform shutdown hook when one was installed",[62,380,381],{},"closes managed resources in reverse registration order",[62,383,384],{},"closes runtime resources and config bindings",[62,386,387,388,390],{},"can shut down ",[17,389,111],{}," automatically",[14,392,393,394,397],{},"Bootstrap helpers already configure the runtime so that ",[17,395,396],{},"magic.runtime().close()","\nis the one shutdown call you usually need in your plugin or mod.",[14,399,400],{},"You can register extra shutdown actions on a live runtime, not only through the\nbuilder:",[46,402,405],{"className":403,"code":404,"language":51,"meta":52},[49],"runtime.onClose(\"metrics\", metrics::flush);\n",[17,406,404],{"__ignoreMap":52},[14,408,409,410,412],{},"Actions registered this way run during ",[17,411,328],{}," alongside managed resources, in\nreverse registration order.",[22,414,416],{"id":415},"building-a-runtime-manually","Building A Runtime Manually",[14,418,419],{},"For NeoForge or custom platforms, build it directly:",[46,421,424],{"className":422,"code":423,"language":51,"meta":52},[49],"MagicRuntime runtime = MagicRuntime.builder(platform, configManager, logger)\n        .languageManager(languageManager)\n        .component(MyPlugin.class, this)\n        .manage(\"database\", databaseClient)\n        .onClose(\"metrics\", metrics::flush)\n        .manageConfigManager(true)\n        .autoRegisterShutdown(true)\n        .build();\n",[17,425,423],{"__ignoreMap":52},[14,427,428],{},"Builder controls:",[59,430,431,436,441,446,451,456],{},[62,432,433],{},[17,434,435],{},"languageManager(...)",[62,437,438],{},[17,439,440],{},"component(...)",[62,442,443],{},[17,444,445],{},"manage(...)",[62,447,448],{},[17,449,450],{},"onClose(...)",[62,452,453],{},[17,454,455],{},"manageConfigManager(...)",[62,457,458],{},[17,459,460],{},"autoRegisterShutdown(...)",[14,462,463,464,466],{},"Disable ",[17,465,460],{}," when the platform already has an explicit\nshutdown phase you want to own manually.",{"title":52,"searchDepth":468,"depth":468,"links":469},3,[470,472,473,474,477,478,479,480,481],{"id":24,"depth":471,"text":25},2,{"id":36,"depth":471,"text":37},{"id":95,"depth":471,"text":96},{"id":145,"depth":471,"text":146,"children":475},[476],{"id":164,"depth":468,"text":165},{"id":195,"depth":471,"text":196},{"id":251,"depth":471,"text":252},{"id":270,"depth":471,"text":271},{"id":367,"depth":471,"text":368},{"id":415,"depth":471,"text":416},"MagicRuntime is the managed container at the heart of MagicUtils: typed components, named resources, and ordered shutdown hooks for every platform.","md",{},null,true,"\u002Fgetting-started\u002Fruntime","---\ntitle: Runtime\ndescription: 'MagicRuntime is the managed container at the heart of MagicUtils: typed components, named resources, and ordered shutdown hooks for every platform.'\n---\n\n# Runtime\n\n`MagicRuntime` is the managed service container behind the bootstrap-first\nsetup. It gives you one place to access core services, register extra\ncomponents, manage closeable resources, and rebuild config-backed clients on\nreload.\n\n## Why a runtime container\n\nWithout a container, a plugin ends up with a handful of static singletons or\nfields that each service reaches into, and shutdown becomes a manual list of\n\"close this, then that, in the right order\" that is easy to get wrong on reload\nor when a startup step fails halfway.\n\n`MagicRuntime` holds the wired services (config, logger, lang, and anything you\nadd), hands them out by type or by name, and closes everything it manages in\nreverse order when the plugin stops. Your shared code depends on one runtime\nhandle instead of on a specific platform. It also rebuilds config-backed clients\nfor you when the config reloads, so services stay in sync without teardown\nboilerplate.\n\n## Getting A Runtime\n\nThe recommended path is `buildRuntime()`:\n\n```java\nBukkitBootstrap.RuntimeResult magic = BukkitBootstrap.forPlugin(this)\n        .enableCommands()\n        .buildRuntime();\n\nMagicRuntime runtime = magic.runtime();\n```\n\nThe same pattern exists for every bootstrap helper:\n\n- `BukkitBootstrap`\n- `BungeeBootstrap`\n- `VelocityBootstrap`\n- `FabricBootstrap`\n- `NeoForgeBootstrap`\n\nCustom platforms can also build `MagicRuntime` manually.\n\n## Core Components\n\nEvery runtime starts with typed components for the core services:\n\n- `Platform`\n- `ConfigManager`\n- `LoggerCore`\n- `LanguageManager` when configured\n\nAccess them via the typed component registry:\n\n```java\nPlatform platform = runtime.requireComponent(Platform.class);\nConfigManager configManager = runtime.requireComponent(ConfigManager.class);\nLoggerCore logger = runtime.requireComponent(LoggerCore.class);\nCommandRegistry commands = runtime.findComponent(CommandRegistry.class).orElse(null);\n```\n\nUse `findComponent(...)` when the component is optional and\n`requireComponent(...)` when its absence is a bug.\n\n## Typed Components\n\nRegister and replace typed components at runtime:\n\n```java\nruntime.putComponent(MyService.class, new MyService());\n\nMyService service = runtime.requireComponent(MyService.class);\nOptional\u003CMyService> opt = runtime.findComponent(MyService.class);\n```\n\n`findComponent(...)` also matches assignable types, so requesting an interface\nwill find a registered implementation.\n\n### Inspecting And Reacting To State\n\nRead the current registries or react when they change:\n\n```java\nMap\u003CClass\u003C?>, Object> typed = runtime.components();\nMap\u003CString, Object> named = runtime.namedComponents();\n\nruntime.onStateChanged(() -> logger.debug(\"Runtime components changed\"));\n\nif (runtime.isClosed()) {\n    return; \u002F\u002F don't touch a closed runtime\n}\n```\n\n`components()` and `namedComponents()` return snapshots of the typed and named\nregistries. `onStateChanged(...)` fires after any component registry update, and\n`isClosed()` guards against using a runtime after shutdown.\n\n## Named Components\n\n`MagicRuntime` also exposes a named registry for dynamic resources:\n\n```java\nruntime.putNamedComponent(\"service.cache\", cacheClient);\n\nCacheClient cache = runtime.requireNamedComponent(\"service.cache\", CacheClient.class);\nOptional\u003CCacheClient> opt = runtime.findNamedComponent(\"service.cache\", CacheClient.class);\n```\n\nRemove a named component when it is no longer needed:\n\n```java\nruntime.removeNamedComponent(\"service.cache\");\n```\n\n`findNamedComponent(name)` also has an untyped overload returning\n`Optional\u003CObject>` when you do not need a type check.\n\nNamed components are especially useful for:\n\n- reloadable clients\n- plugin-owned service singletons\n- resources keyed by logical role (`http.monitoring`, `ws.gateway`)\n\n## Managed Resources\n\nUse `resource(...)` when you want a stable named slot that closes replaced\nresources automatically:\n\n```java\nMagicRuntimeResource\u003CMagicHttpClient> monitoring = runtime.resource(\n        \"http.monitoring\",\n        MagicHttpClient.builder(runtime.platform(), runtime.configManager())\n                .baseUrl(\"https:\u002F\u002Fapi.example.com\u002F\")\n                .build()\n);\n\nMagicHttpClient client = monitoring.require();\nmonitoring.set(MagicHttpClient.builder(runtime.platform(), runtime.configManager())\n        .baseUrl(\"https:\u002F\u002Fapi-two.example.com\u002F\")\n        .build());\n```\n\nThe resource is also exposed through the named component registry under the same\nname.\n\n## Config Bindings\n\nUse `bindConfig(...)` when a closeable resource should rebuild automatically on\nmatching config reloads:\n\n```java\nMagicRuntimeConfigBinding\u003CServiceConfig, MagicHttpClient> binding = runtime.bindConfig(\n        \"http.monitoring\",\n        ServiceConfig.class,\n        config -> MagicHttpClient.builder(runtime.platform(), runtime.configManager())\n                .baseUrl(config.monitoring.baseUrl)\n                .build(),\n        \"monitoring\"\n);\n\nMagicHttpClient client = binding.require();\n```\n\nThe binding is a small handle around the managed resource:\n\n- `require()` \u002F `current()` — the current resource, throwing or as an `Optional`.\n- `configClass()` — the bound config type; `name()` — the resource name.\n- `resource()` — the underlying `MagicRuntimeResource`.\n- `isClosed()` \u002F `close()` — lifecycle state and manual teardown.\n\nThis pattern works well for:\n\n- HTTP clients\n- WebSocket clients\n- database pools\n- SDK clients\n\nThe HTTP client module also provides higher-level wrappers:\n\n- `MagicHttpClientProfile`\n- `MagicWebSocketClientProfile`\n\nUse those when the resource being managed is specifically an HTTP or WebSocket\nclient.\n\n## Lifecycle\n\n`MagicRuntime.close()`:\n\n- unregisters its platform shutdown hook when one was installed\n- closes managed resources in reverse registration order\n- closes runtime resources and config bindings\n- can shut down `ConfigManager` automatically\n\nBootstrap helpers already configure the runtime so that `magic.runtime().close()`\nis the one shutdown call you usually need in your plugin or mod.\n\nYou can register extra shutdown actions on a live runtime, not only through the\nbuilder:\n\n```java\nruntime.onClose(\"metrics\", metrics::flush);\n```\n\nActions registered this way run during `close()` alongside managed resources, in\nreverse registration order.\n\n## Building A Runtime Manually\n\nFor NeoForge or custom platforms, build it directly:\n\n```java\nMagicRuntime runtime = MagicRuntime.builder(platform, configManager, logger)\n        .languageManager(languageManager)\n        .component(MyPlugin.class, this)\n        .manage(\"database\", databaseClient)\n        .onClose(\"metrics\", metrics::flush)\n        .manageConfigManager(true)\n        .autoRegisterShutdown(true)\n        .build();\n```\n\nBuilder controls:\n\n- `languageManager(...)`\n- `component(...)`\n- `manage(...)`\n- `onClose(...)`\n- `manageConfigManager(...)`\n- `autoRegisterShutdown(...)`\n\nDisable `autoRegisterShutdown(...)` when the platform already has an explicit\nshutdown phase you want to own manually.\n",{"title":5,"description":482},"getting-started\u002Fruntime","r_FmIgnAt_MhnBOQ7XLMAq3p6gPuaoMaJmz8vs3SxL4",1783944486698]