Getting started¶
Requires Java 25. The library is thread-safe once constructed, so a single ResourcePackIdentifier can be shared.
Identify a pack¶
identify(Path) works out what it was handed — a .zip, a pack directory, or a bare pack.mcmeta.
var identifier = new ResourcePackIdentifier();
IdentificationResult result = identifier.identify(Path.of("MyPack.zip"));
The same call handles the other two shapes:
identifier.identify(Path.of("MyPack")); // a directory containing pack.mcmeta
identifier.identify(Path.of("MyPack/pack.mcmeta")); // the file itself
If you already know what you have, say so and skip the sniffing:
identifier.identify(PackSource.archive(archive));
identifier.identify(PackSource.directory(directory));
identifier.identify(PackSource.metaFile(file));
Handle every outcome¶
IdentificationResult is sealed, so an exhaustive switch cannot forget a case:
String summary = switch (identifier.identify(archive)) {
case IdentificationResult.Identified(var metadata, var declared, var effective, var diagnostics) ->
metadata.pack().plainTextDescription() + " targets " + declared.describe();
case IdentificationResult.NestedPack(String entry) ->
"pack.mcmeta sits at " + entry + ", so Minecraft will reject this archive";
case IdentificationResult.NoMetadata ignored -> "not a resource pack";
case IdentificationResult.Malformed(String reason) -> "unusable: " + reason;
};
| Case | Meaning |
|---|---|
Identified |
Parsed. Carries the metadata, both version resolutions, and any diagnostics |
NestedPack |
A pack.mcmeta exists one directory down, so the game will reject the archive |
NoMetadata |
No pack.mcmeta at all — not a resource pack |
Malformed |
Found, but unusable: broken JSON, no pack section, or no format declaration |
IOException is thrown rather than modelled — a missing file or an unreadable disk is not a fact about the pack.
Nested packs are reported, not accepted¶
Zipping the folder instead of its contents is the most common packaging mistake. Minecraft looks only at the archive root, so such a pack does not load.
Path archive = Zips.write(target, Map.of("MyPack/pack.mcmeta", packMcmeta));
IdentificationResult.NestedPack nested =
assertInstanceOf(IdentificationResult.NestedPack.class, identifier.identify(archive));
assertEquals("MyPack/pack.mcmeta", nested.entryPath());
The library finds it and tells you where it is, but will not parse it as a success — reporting metadata for a pack the game refuses to load would be worse than saying nothing.
Entry names are matched case-sensitively, exactly as the game does. Pack.mcmeta at the root is NoMetadata.
Limits¶
Reads are capped at 1 MB so a hostile archive cannot exhaust memory. Real files are well under 2 KB. Change it by constructing the reader yourself:
ResourcePackIdentifier strict = new ResourcePackIdentifier(
VersionTable.bundled(), new PackSourceReader(64), new McmetaParser());
Anything over the limit comes back as Malformed rather than being buffered.