Asset manifests, Bedrock resource-pack validation and packaging helpers for PocketMine-MP plugins and EasyLibrary packages.
LibAssets is the official EasyLibrary asset helper package. It is designed
for plugins that ship or prepare resource packs, generated files and other
versioned assets that need predictable hashes and validation before a server
uses them.
The first 3.0-dev version focuses on practical local workflows:
- scan a directory and create a SHA-256 asset manifest;
- compare an existing manifest against files on disk;
- parse and validate Bedrock resource-pack
manifest.jsonfiles; - validate resource-pack directories and ZIP files;
- build resource-pack ZIPs with byte size and SHA-256 metadata;
- register resource-pack definitions and inspect whether they are active, prepared, pending, disabled or invalid;
- prepare packs into a controlled plugin workspace instead of mixing source, build output and cache files;
- expose a small downloader adapter contract so
LibHttpcan provide remote bytes whileLibAssetsstill owns local validation; - keep the API offline-first so it can be used safely during startup and tests.
- PHP
8.2+ - PocketMine-MP API
5.0.0+ LibCommons 1.0.0-dev+
LibCommons provides the shared pure helpers used internally for SHA-256,
JSON, path and filesystem operations. It does not make LibAssets depend on
network, UI, command or world-editing code.
Standalone plugin:
- Download
LibCommons-1.0.0-dev.pharandLibAssets-1.0.0-dev.phar. - Put both files in
plugins/. - Restart the server.
EasyLibrary package:
/easylibrary packages install libcommons development confirm
/easylibrary packages install libassets development confirm
When dependency resolution is available, installing libassets should stage
libcommons as its required package dependency. Installing both explicitly is
still safe and easier to reason about during development smoke tests.
Then restart and confirm:
/easylibrary packages doctor
use imperazim\assets\AssetManifest;
$manifest = AssetManifest::fromDirectory($plugin->getDataFolder() . 'assets');
foreach ($manifest->getFiles() as $file) {
$plugin->getLogger()->info($file->getPath() . ' ' . $file->getSha256());
}
$report = $manifest->validateAgainstDirectory($plugin->getDataFolder() . 'assets');
if (!$report->isValid()) {
foreach ($report->getProblems() as $problem) {
$plugin->getLogger()->warning($problem);
}
}The manifest stores relative paths, file sizes and SHA-256 hashes. It rejects
path traversal and keeps paths normalized with /.
use imperazim\assets\resource\ResourcePackValidator;
$report = ResourcePackValidator::validateDirectory($plugin->getDataFolder() . 'pack');
if (!$report->isValid()) {
foreach ($report->getErrors() as $error) {
$plugin->getLogger()->error($error);
}
}LibAssets validates the real Bedrock resource-pack contract it can safely
check from PHP: manifest.json, format_version, header UUID/version/name and
resource modules.
use imperazim\assets\resource\ResourcePackBuilder;
$result = ResourcePackBuilder::buildZip(
$plugin->getDataFolder() . 'pack',
$plugin->getDataFolder() . 'dist/server-pack.zip'
);
$plugin->getLogger()->info('Pack SHA-256: ' . $result->getSha256());The builder validates the source directory first and writes a deterministic ZIP with normalized paths. The returned result includes path, size, hash and parsed manifest metadata.
Use ResourcePackWorkflow when a plugin owns one or more resource-pack sources
and wants a predictable staging area under its data folder.
use imperazim\assets\resource\ResourcePackDefinition;
use imperazim\assets\resource\ResourcePackWorkflow;
use imperazim\assets\resource\ResourcePackWorkflowStatus;
$workflow = new ResourcePackWorkflow($plugin->getDataFolder() . 'resource-packs');
$workflow->register(new ResourcePackDefinition(
id: 'lobby',
sourceDirectory: $plugin->getFile() . 'resources/lobby-pack',
targetArchiveName: 'lobby-pack.zip',
enabled: true
));
$report = $workflow->inspect('lobby');
if ($report->getStatus() === ResourcePackWorkflowStatus::PENDING) {
$result = $workflow->prepare('lobby');
$plugin->getLogger()->info('Prepared pack: ' . $result->getBuildResult()?->getPath());
}The workflow creates these directories inside the workspace:
prepared/<id>/contains the copied, validated pack source;dist/<archive>contains the generated ZIP/pack archive;manifests/<id>.jsonstores the generated metadata and source fingerprint;cache/<archive>is reserved for optional remote downloads.
Status meanings:
active: enabled, prepared and current;prepared: prepared and current, but disabled in this workflow definition;pending: source is valid, but the prepared output is missing or stale;disabled: valid source, disabled and not prepared yet;invalid: source, manifest, metadata or generated archive has a clear error.
This workflow does not force PocketMine-MP to register packs at runtime. It keeps the risky part explicit: your plugin decides when and how to pass the prepared archive to the server or a deployment/CDN layer.
LibAssets does not perform HTTP requests itself. Instead, it exposes
AssetDownloader, a tiny adapter contract that LibHttp or another transport
can implement.
use imperazim\assets\remote\AssetDownloader;
use imperazim\assets\remote\AssetDownloadResult;
use imperazim\assets\resource\ResourcePackDefinition;
use imperazim\assets\resource\ResourcePackWorkflow;
$workflow = new ResourcePackWorkflow($plugin->getDataFolder() . 'resource-packs');
$workflow->register(ResourcePackDefinition::fromCdn(
id: 'seasonal',
sourceDirectory: $plugin->getDataFolder() . 'seasonal-pack-source',
baseUrl: 'https://cdn.example.com/packs',
targetArchiveName: 'seasonal-pack.zip',
expectedSha256: '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'
));
$download = $workflow->downloadRemoteArchive('seasonal', new class implements AssetDownloader {
public function download(string $url, string $targetPath, ?string $expectedSha256 = null): AssetDownloadResult {
// Call LibHttp here, write the response body to $targetPath,
// then return AssetDownloadResult::fromPath($targetPath).
return AssetDownloadResult::fromPath($targetPath);
}
});downloadRemoteArchive() validates the downloaded ZIP and checks the expected
SHA-256 when one is configured. That gives plugins a clean split:
LibHttp: request, retry, timeout, response handling;LibAssets: cache path, checksum, Bedrock pack validation and local status.
examples/README.mdcollects focused snippets for manifest checks, pack validation, workflows and remote cache adapters.examples/resource_pack_workflow.phpshows a concise local workflow for validating, preparing and describing resource packs.examples/libhttp_downloader_adapter.phpshows how to bridgeLibHttpdownloads into theAssetDownloadercontract without makingLibAssetsown HTTP transport.
The PHP examples are included in the repository lint step so documentation code stays syntactically valid as the API evolves.
LibAssets ships both:
plugin.ymlfor normal PMMP plugin installation;package.ymlfor EasyLibrary package manager installation.
Runtime dependencies:
- standalone PHAR:
depend: [LibCommons]; - EasyLibrary package:
dependencies.required: [libcommons]; - Composer development/autoload:
imperazim/libcommonsfrom thedevelopmentbranch.
Release assets include:
LibAssets-1.0.0-dev.phar;LibAssets-1.0.0-dev.easylib.zip;package.yml;checksums.txt.
- PHP
8.2+ - PocketMine-MP API
5.0.0 - LibCommons
1.0.0-dev+ - EasyLibrary
3.0.0-devpackage manager
This package intentionally does not own network transport. Remote fetching
belongs in LibHttp or a plugin adapter; LibAssets handles local validation,
cache paths and packaging after bytes exist on disk.