- Rust 79.8%
- Nix 20.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .forgejo/workflows | ||
| actions/build | ||
| example | ||
| example-mrpack | ||
| lib | ||
| nix-modules | ||
| src | ||
| templates/pack | ||
| .envrc | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| flake.lock | ||
| flake.nix | ||
| README.md | ||
mcpb
A Cargo-inspired Minecraft modpack builder: an mcpb.toml manifest,
an mcpb.lock pin file, features, side-aware targets, and builds for
client and server — as a CLI, as a nix lib, and as a CI action.
mcpb init my-pack --loader fabric
cd my-pack
mcpb add mod modrinth:sodium
mcpb build client
mcpb build server
mcpb build client --feature performance
Install
nix profile install git+https://git.jingus.dev/jingus/mcpb#mcpb
# or ad hoc:
nix run git+https://git.jingus.dev/jingus/mcpb#mcpb -- --help
Other flakes can pull the binary:
inputs.mcpb.url = "git+https://git.jingus.dev/jingus/mcpb";
# packages.${system}.mcpb
CLI
| command | what it does |
|---|---|
mcpb init [PATH] |
scaffold mcpb.toml and a default icon.png |
mcpb add mod modrinth:sodium |
resolve + pin a mod from Modrinth |
mcpb add mod url:https://…/x.jar |
pin a mod from a direct URL (hashed on add) |
mcpb add mod files/x.jar |
reference a local jar (any path inside the pack) |
mcpb add resource-pack modrinth:… / shader-pack / asset |
same sources, other kinds |
mcpb add … --feature perf [--side client] |
optional entry, enabled by a feature |
mcpb add asset files/icon.png --path server-icon.png |
place a file anywhere in the pack |
mcpb remove mod sodium |
remove from manifest + lock (prunes feature lists) |
mcpb update [NAME…] |
re-resolve pins to latest |
mcpb list |
show features and pinned entries |
mcpb build client / server |
build (see below) |
Build flags: --features a,b (or repeated --feature), --all-features,
--no-default-features, --format folder|zip|mrpack, --output DIR
(output root, defaults to <pack>/.mcpb/out), --pack DIR (global;
defaults to the nearest ancestor with mcpb.toml).
Manifest (mcpb.toml)
[pack]
name = "my-pack"
version = "0.1.0"
minecraft = "1.21.1"
loader = "fabric" # fabric | quilt | neoforge | forge | paper; omit for none
# loader-version = "0.16.9" # pin a loader version
icon = "icon.png" # copied into every build
[features]
default = ["sodium"]
performance = ["lithium"]
[dependencies]
sodium = { modrinth = "sodium", side = "client" }
lithium = { modrinth = "lithium", optional = true }
[resource-packs]
fresh = { modrinth = "fresh-animations" }
[shader-packs]
complementary = { modrinth = "complementary-reimagined" }
[assets]
icon = { file = "files/server-icon.png", path = "server-icon.png" }
- Entry sources:
modrinth = "<slug|id>",url = "…", orfile = "…"(relative to the pack root — any tracked path works;files/is a convention, not a requirement, and is not created for you). optional = trueentries install only when an enabled feature references them (mcpb add --featuredoes this for you).side = "client"|"server"|"both"overrides automatic side detection. Without it, Modrinth'sclient_side/server_sidedecide: client builds skip entries unsupported on the client; server builds include an entry only when the server side isrequired(anoptionalserver side, like Sodium's, is skipped — force it withside = "both").iconpoints at any file in the pack; it is copied to the root of every build (and into the mrpack overrides).mcpb initwrites a default pixel-art crate icon you can replace.- Paper packs install plugins into
plugins/instead ofmods/.
Overrides: config files, worlds, anything not an entry
Builds assemble entries into mods/ (or plugins/), resourcepacks/,
shaderpacks/, and placed assets — then copy plain files over the
result. Those files live in up to three optional directories at
the pack root. None exist by default; create them when needed:
| directory | copied into | typical use |
|---|---|---|
overrides/ |
every build | shared config/, options.txt, worlds |
client-overrides/ |
client builds only | keybindings, client-only config |
server-overrides/ |
server builds only | server.properties, whitelist |
Contents map to the build root: overrides/config/foo.toml becomes
config/foo.toml in the output. Target-specific directories are
applied after overrides/, so they can replace shared files. In
--format mrpack, the common + client overrides are packaged as the
archive's overrides/ directory; server overrides are not included.
Lock (mcpb.lock)
mcpb add/mcpb update/mcpb build keep mcpb.lock pinned:
Modrinth project + version ids, file URLs, sha1/sha512, sizes,
environment sides, and the server launcher. Builds verify hashes on
download. Commit it — it is the reproducibility boundary for the nix
lib.
Targets, formats, and .mcpb/
Targets are installation mechanisms, not architectures. Everything
mcpb generates lives under .mcpb/ (gitignored):
.mcpb/
deps/ # content-addressed download cache,
# reused across builds
out/
client/folder/ # ready-to-use tree
client/zip/my-pack-client-0.1.0.zip # zip of the same tree (folder kept)
client/mrpack/my-pack-0.1.0.mrpack # Modrinth index format (client only)
server/folder/ # launcher/installer + run.sh/run.bat
server/zip/my-pack-server-0.1.0.zip
folder(default): the build tree, including the pack icon.zip: additionally writes the archive next to the kept folder.mrpack(client only):modrinth.index.jsonwith URLs + hashes + env, the pinned loader version, and overrides; local-file entries are folded intooverrides/.
Server builds also fetch and pin the loader: the fabric/quilt server
launcher jar, the neoforge/forge installer (with run.sh/run.bat
that install on first start), or the paper server.jar.
MCPB_JAVA_ARGS overrides the default -Xms512M -Xmx4G heap flags
at runtime.
Nix lib
The flake exposes lib.buildModpack, lib.buildClient, and
lib.buildServer — pure, hermetic builds from mcpb.toml +
mcpb.lock (modrinth/url entries via fetchurl, so remote entries
require the lock; loader pins come from the lock's [loader]):
# in a pack repo
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
mcpb.url = "git+https://git.jingus.dev/jingus/mcpb";
};
outputs = { self, nixpkgs, mcpb }:
let pkgs = nixpkgs.legacyPackages.x86_64-linux; in
{
packages.x86_64-linux = rec {
client = mcpb.lib.buildClient { inherit pkgs; pack = self; };
server = mcpb.lib.buildServer { inherit pkgs; pack = self; };
default = client;
};
};
}
Options: pack, target (client/server), format
(folder/zip/mrpack; mrpack requires a pinned loader-version),
features, allFeatures, noDefaultFeatures, name. Feature,
side, overrides, and icon semantics mirror the CLI. There is a starter
template with a devshell that provides the CLI:
nix flake init -t git+https://git.jingus.dev/jingus/mcpb#pack
This repo also carries example/ and example-mrpack/ — offline
packs exercising the lib in nix flake check.
CI
Composite action (actions/build/action.yml):
- uses: https://git.jingus.dev/jingus/mcpb/actions/build@main
with:
target: client # or server
format: zip # folder | zip | mrpack
features: performance # optional, comma separated
pack-dir: . # directory containing mcpb.toml
It runs nix run <mcpb flake>#mcpb -- build …; override the flake ref
with the mcpb: input. Reusable workflow
(.forgejo/workflows/build-pack.yml):
jobs:
build:
uses: https://git.jingus.dev/jingus/mcpb/.forgejo/workflows/build-pack.yml@main
with:
target: client
format: zip
It builds and uploads .mcpb/out/ as an artifact. This repo dogfoods
the action in .forgejo/workflows/example-pack.yml.
Development
nix develop
cargo run -- --help
cargo test
nix build .#mcpb
nix flake check
nix run .#validate
Unit tests cover feature resolution, side filtering, manifest editing
(toml_edit roundtrips), lock roundtrips, Modrinth payload parsing,
loader versioning/script generation, hashing, and zip writing — all
offline. nix flake check additionally builds the example packs with
the nix lib and smoke-tests the CLI in the sandbox.
Layout
flake.nix # inputs only; modules under nix-modules/
lib/default.nix # nix lib: buildModpack / buildClient / buildServer
nix-modules/
packages/mcpb.nix # crane build + clippy/fmt/test checks
checks.nix # CLI smoke, example packs, template check
apps.nix # `nix run .#mcpb`, `nix run .#validate`
lib.nix, templates.nix, parts.nix, shell.nix
src/
main.rs, manifest.rs, lock.rs, modrinth.rs, loader.rs, build.rs,
util.rs, cmd/
src/assets/icon.png # default pack icon written by `mcpb init`
templates/pack/ # `nix flake init -t` starter (devshell + builds)
example/, example-mrpack/ # offline example packs (checked in CI)
actions/build/action.yml # composite CI action
.forgejo/workflows/ # ci.yml, build-pack.yml, example-pack.yml