Skip to content

Commit 332c126

Browse files
committed
feat(runtime): gate legacy embedded fallbacks
1 parent 51c0451 commit 332c126

10 files changed

Lines changed: 181 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ elprobe run <package>
5959
do not break before packages, fallback commands and migration docs are ready.
6060
- Defined the 3.x target model: EasyLibrary core plus official installable
6161
packages under `plugin_data/EasyLibrary/packages/`.
62+
- Added `legacy-embedded.enabled=false` as the package-first default gate for
63+
old bundled runtime fallbacks. EasyLibrary no longer auto-starts embedded
64+
LibCommand, LibPlaceholder, LibTrigger, LibWorld or LibHud fallback pieces
65+
unless the operator explicitly enables that legacy escape hatch.
66+
- Changed command backend selection so embedded LibCommand classes on disk do
67+
not automatically unlock the rich command tree. Package-backed or standalone
68+
LibCommand still provides the rich backend; otherwise EasyLibrary uses the
69+
native recovery command.
6270

6371
### Package manager
6472

README.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,20 @@ AgentFlagsAPI::set(AgentNetworkFlags::DOUBLE_XP, true, 3600, 'Weekend booster');
177177

178178
EasyLibrary 3.x is moving away from shipping every lib as always-on embedded code. These namespaces still exist during the migration window so existing servers do not break immediately, but the target is to move them into official internal packages and remove the legacy embedded copy only after install + restart is proven safe.
179179

180+
In the package-first 3.0-dev model, legacy embedded fallback is disabled by
181+
default:
182+
183+
```yaml
184+
legacy-embedded:
185+
enabled: false
186+
```
187+
188+
With this setting, EasyLibrary does not automatically start bundled
189+
LibCommand, LibPlaceholder, LibTrigger, LibWorld or LibHud fallback runtime
190+
pieces. Install the official EasyLibrary package or standalone plugin instead.
191+
Only enable `legacy-embedded.enabled=true` as a temporary compatibility escape
192+
hatch while migrating an older 2.x-style server.
193+
180194
| Library | Namespace | Main purpose |
181195
|---|---|---|
182196
| [LibPlaceholder](https://github.com/ImperaZim/LibPlaceholder) | `imperazim\placeholder` | Placeholder parsing and expansion registry |
@@ -209,7 +223,9 @@ Standalone installed:
209223
an installed internal package reports installed-shadowed-by-standalone instead of active
210224
211225
Legacy embedded fallback:
212-
kept during early 3.0-dev so existing servers keep working
226+
source kept during 3.0-dev migration
227+
disabled by default through legacy-embedded.enabled=false
228+
can be enabled temporarily for older 2.x-style servers
213229
removed only after the internal package replacement is tested
214230
```
215231

@@ -403,6 +419,12 @@ or reloads managers that EasyLibrary itself owns in the current server session.
403419
After a restart, normal `plugin.yml`, config and standalone plugin priority
404420
rules still apply.
405421

422+
The legacy embedded runtime targets are gated by
423+
`legacy-embedded.enabled=true`. With the default package-first config, use
424+
package-backed or standalone libs instead. The fallback native
425+
`/easylibrary` recovery command still remains available when LibCommand is not
426+
loaded.
427+
406428
The standalone installer remains available for compatibility. The preferred 3.x path is `/easylibrary packages install`, but `/easylibrary libs install` can still download official standalone PHARs after `confirm`, checksum validation and `library-installer.enabled=true`. It still does not hot-load the PHAR; restart is required.
407429

408430
```txt

changelogs/3.0.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,48 @@ does not mean embedded full-bundle behavior is the final target. It exists so
8484
servers can migrate gradually while the package manager, fallback command and
8585
Wiki/migration docs mature.
8686

87+
### Legacy embedded fallback is now gated
88+
89+
The package-first 3.0-dev default now disables automatic embedded fallback
90+
runtime startup:
91+
92+
```yaml
93+
legacy-embedded:
94+
enabled: false
95+
```
96+
97+
This changed the migration behavior intentionally:
98+
99+
- EasyLibrary no longer auto-starts bundled LibCommand, LibPlaceholder,
100+
LibTrigger, LibWorld or LibHud fallback runtime pieces just because their
101+
source directories still exist inside the EasyLibrary tree.
102+
- Package-backed or standalone LibCommand still unlocks the rich command tree.
103+
- If no LibCommand provider is active and the only available classes are the
104+
EasyLibrary embedded copy, EasyLibrary registers the native recovery command
105+
instead of silently using the legacy rich command backend.
106+
- `/easylibrary doctor` and library status now report the legacy embedded
107+
fallback as disabled by config instead of implying that it is the preferred
108+
active provider.
109+
110+
Why this exists:
111+
112+
- The 3.x target is package-backed/standalone ownership, not hidden embedded
113+
ownership.
114+
- Operators should see a missing package/provider and install the official
115+
package instead of accidentally relying on old bundled source.
116+
- The embedded source can still stay in the repository while the migration
117+
audit continues, but it no longer defines the normal boot model.
118+
119+
Temporary escape hatch:
120+
121+
```yaml
122+
legacy-embedded:
123+
enabled: true
124+
```
125+
126+
Use that only while migrating an older 2.x-style server that still depends on
127+
the full-bundle behavior. It should not be the default for new 3.x setups.
128+
87129
### Public API compatibility so far
88130

89131
The 3.0-dev work described here is an architectural and operational migration,

docs/standalone-vs-embedded.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,18 @@ break immediately. It is not the long-term target. The target is:
3434
EasyLibrary core + official internal packages
3535
```
3636

37+
The current 3.0-dev package-first default keeps that fallback disabled:
38+
39+
```yaml
40+
legacy-embedded:
41+
enabled: false
42+
```
43+
44+
When disabled, EasyLibrary does not auto-start bundled LibCommand,
45+
LibPlaceholder, LibTrigger, LibWorld or LibHud fallback runtime pieces. Use
46+
package-backed or standalone providers. Turn it on only as a temporary escape
47+
hatch for an older server while migrating to packages.
48+
3749
## Mixed mode rule
3850
3951
Mixed mode is allowed for migration only:

docs/usage.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,17 @@ EasyLibrary 3.x has three possible providers for official libs:
1616
Only one provider should own a runtime at a time. If a standalone plugin exists,
1717
it wins over the matching internal package for that server session.
1818

19+
The package-first 3.0-dev default keeps legacy embedded fallback disabled:
20+
21+
```yaml
22+
legacy-embedded:
23+
enabled: false
24+
```
25+
26+
With this default, install official packages or standalone plugins. If
27+
LibCommand is unavailable, EasyLibrary registers only the small native recovery
28+
command so package repair/install commands remain reachable.
29+
1930
## First boot
2031
2132
After installing EasyLibrary, run:

resources/config.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,14 @@ library-installer:
8888
# standalone plugin files.
8989
restart-required: true
9090

91+
# Temporary EasyLibrary 2.x embedded fallback.
92+
# Keep disabled for the 3.x package-first model. When disabled, EasyLibrary does
93+
# not automatically start bundled LibCommand, LibPlaceholder, LibTrigger,
94+
# LibWorld or LibHud fallback runtime pieces. Use official packages or
95+
# standalone plugins instead.
96+
legacy-embedded:
97+
enabled: false
98+
9199
# Embedded LibPlaceholder options.
92100
libplaceholder:
93101
# Optional LibPacket bridge for outgoing text packets.

src/LibraryModules.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ final class LibraryModules {
2929
* Load all modules.
3030
*/
3131
public static function loadModules(PluginBase $plugin): void {
32+
if (!self::isLegacyEmbeddedEnabled($plugin)) {
33+
$plugin->getLogger()->info('[LegacyEmbedded] Disabled by config; official libs must be loaded as packages or standalone plugins.');
34+
return;
35+
}
36+
3237
$pluginManager = $plugin->getServer()->getPluginManager();
3338
$pluginManager->registerEvents(
3439
new EmbeddedLibraryLifecycleListener($plugin),
@@ -107,6 +112,7 @@ public static function unloadModules(PluginBase $plugin): void {
107112
}
108113

109114
public static function activateEmbeddedCommandRuntime(PluginBase $plugin): bool {
115+
self::requireLegacyEmbeddedEnabled($plugin, 'LibCommand');
110116
if (!CommandClassAvailability::hookerAvailable()) {
111117
throw new \RuntimeException('Embedded LibCommand classes are not available.');
112118
}
@@ -142,6 +148,7 @@ public static function deactivateEmbeddedCommandRuntime(PluginBase $plugin): boo
142148
}
143149

144150
public static function activateEmbeddedPlaceholderRuntime(PluginBase $plugin): bool {
151+
self::requireLegacyEmbeddedEnabled($plugin, 'LibPlaceholder');
145152
if (!self::isPlaceholderRuntimeAvailable()) {
146153
throw new \RuntimeException('LibPlaceholder classes are not available.');
147154
}
@@ -182,6 +189,7 @@ public static function deactivateEmbeddedPlaceholderRuntime(PluginBase $plugin):
182189
}
183190

184191
public static function activateEmbeddedTriggerRuntime(PluginBase $plugin): bool {
192+
self::requireLegacyEmbeddedEnabled($plugin, 'LibTrigger');
185193
if (!self::isTriggerRuntimeAvailable()) {
186194
throw new \RuntimeException('LibTrigger classes are not available.');
187195
}
@@ -218,6 +226,7 @@ public static function deactivateEmbeddedTriggerRuntime(PluginBase $plugin): boo
218226
}
219227

220228
public static function activateEmbeddedWorldRuntime(PluginBase $plugin): bool {
229+
self::requireLegacyEmbeddedEnabled($plugin, 'LibWorld');
221230
if (!self::isWorldRuntimeAvailable()) {
222231
throw new \RuntimeException('LibWorld classes are not available.');
223232
}
@@ -262,6 +271,31 @@ private static function isStandaloneEnabled(PluginBase $plugin, string $pluginNa
262271
return $standalone !== null && $standalone->isEnabled();
263272
}
264273

274+
public static function isLegacyEmbeddedEnabled(PluginBase $plugin): bool {
275+
$value = $plugin->getConfig()->getNested('legacy-embedded.enabled', false);
276+
if (is_bool($value)) {
277+
return $value;
278+
}
279+
if (is_int($value) || is_float($value)) {
280+
return ((int) $value) !== 0;
281+
}
282+
if (is_string($value)) {
283+
$normalized = strtolower(trim($value));
284+
if (is_numeric($normalized)) {
285+
return ((int) $normalized) !== 0;
286+
}
287+
return in_array($normalized, ['1', 'true', 'yes', 'on', 'enabled'], true);
288+
}
289+
290+
return false;
291+
}
292+
293+
private static function requireLegacyEmbeddedEnabled(PluginBase $plugin, string $pluginName): void {
294+
if (!self::isLegacyEmbeddedEnabled($plugin)) {
295+
throw new \RuntimeException("Embedded {$pluginName} runtime is disabled by config (legacy-embedded.enabled=false). Install the official package or standalone plugin instead.");
296+
}
297+
}
298+
265299
private static function isPlaceholderRuntimeAvailable(): bool {
266300
return class_exists(\imperazim\placeholder\PlaceholderManager::class);
267301
}
@@ -339,6 +373,9 @@ public static function onStandalonePlaceholderDisabled(
339373
PluginBase $plugin,
340374
Plugin $standalone
341375
): void {
376+
if (!self::isLegacyEmbeddedEnabled($plugin)) {
377+
return;
378+
}
342379
if (!self::isPlaceholderRuntimeAvailable()) {
343380
return;
344381
}
@@ -377,6 +414,9 @@ public static function onStandaloneWorldDisabled(
377414
PluginBase $plugin,
378415
Plugin $standalone
379416
): void {
417+
if (!self::isLegacyEmbeddedEnabled($plugin)) {
418+
return;
419+
}
380420
if (!self::isWorldRuntimeAvailable()) {
381421
return;
382422
}

src/imperazim/library/LibraryStatusService.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ private function inspect(OfficialLibrarySource $source): LibraryStatus {
125125
);
126126
}
127127

128+
if ($source->getPluginName() !== 'EasyLibraryAgentBridge' && !\LibraryModules::isLegacyEmbeddedEnabled($this->host)) {
129+
return $this->legacyEmbeddedDisabledStatus($source);
130+
}
131+
128132
return match ($source->getPluginName()) {
129133
'LibCommand' => $this->embeddedCommandStatus($source),
130134
'LibPlaceholder' => $this->embeddedPlaceholderStatus($source),
@@ -141,6 +145,19 @@ private function inspect(OfficialLibrarySource $source): LibraryStatus {
141145
};
142146
}
143147

148+
private function legacyEmbeddedDisabledStatus(OfficialLibrarySource $source): LibraryStatus {
149+
return new LibraryStatus(
150+
$source,
151+
LibraryRuntimeState::EMBEDDED_DISABLED,
152+
$this->host->getName(),
153+
$source->getExpectedVersion(),
154+
[
155+
'Legacy embedded fallback is disabled by config.',
156+
'Install the official package or standalone plugin to provide this library.',
157+
]
158+
);
159+
}
160+
144161
/**
145162
* @param string[] $files
146163
*/
@@ -417,6 +434,9 @@ private function embeddedRuntimeHostName(OfficialLibrarySource $source): ?string
417434
}
418435

419436
private function embeddedFallbackProviderName(OfficialLibrarySource $source): ?string {
437+
if ($source->getPluginName() !== 'EasyLibraryAgentBridge' && !\LibraryModules::isLegacyEmbeddedEnabled($this->host)) {
438+
return null;
439+
}
420440
if ($source->getEmbeddedKind() === OfficialLibrarySource::EMBEDDED_CLASSPATH) {
421441
return $this->host->getName();
422442
}

src/imperazim/library/command/backend/CommandBackendResolver.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
use imperazim\agent\AgentManager;
88
use imperazim\components\plugin\PluginToolkit;
99
use imperazim\library\command\provider\CommandClassAvailability;
10+
use imperazim\library\command\provider\CommandProviderResolver;
11+
use imperazim\library\command\provider\CommandProviderStatus;
1012

1113
final class CommandBackendResolver {
1214

@@ -16,7 +18,14 @@ public function __construct(
1618
) {}
1719

1820
public function resolve(): CommandBackend {
19-
if (CommandClassAvailability::richCommandsAvailable()) {
21+
$provider = (new CommandProviderResolver($this->host))->resolve();
22+
if (
23+
CommandClassAvailability::richCommandsAvailable()
24+
&& (
25+
$provider->getProvider() !== CommandProviderStatus::EMBEDDED_LEGACY
26+
|| \LibraryModules::isLegacyEmbeddedEnabled($this->host)
27+
)
28+
) {
2029
return new LibCommandBackend($this->host, $this->agentManager);
2130
}
2231

src/imperazim/library/command/provider/CommandProviderResolver.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,13 @@ public function resolve(): CommandProviderStatus {
4040
$notes = [];
4141

4242
if ($provider === CommandProviderStatus::EMBEDDED_LEGACY) {
43-
$warnings[] = 'EasyLibrary internal commands are currently using the embedded legacy LibCommand classes.';
44-
$notes[] = 'Do not remove src/imperazim/command until package-backed or standalone LibCommand is guaranteed for command registration.';
43+
if (\LibraryModules::isLegacyEmbeddedEnabled($this->host)) {
44+
$warnings[] = 'EasyLibrary internal commands are currently using the embedded legacy LibCommand classes.';
45+
$notes[] = 'Do not remove src/imperazim/command until package-backed or standalone LibCommand is guaranteed for command registration.';
46+
} else {
47+
$notes[] = 'Embedded legacy LibCommand classes exist on disk but are disabled by legacy-embedded.enabled=false.';
48+
$notes[] = 'EasyLibrary will use the native recovery command unless package-backed or standalone LibCommand is active.';
49+
}
4550
}
4651
if ($provider === CommandProviderStatus::MISSING) {
4752
$warnings[] = 'imperazim\\command\\Command is not loaded; EasyLibrary command registration would fail.';

0 commit comments

Comments
 (0)