Skip to content

Getting started

implementation("nl.dylandebeer:resource-pack-identifier:0.1.0")

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.