# Building from source ## The important part **The pack is generated at build time from the upstream mod, not hand-maintained.** You do not edit `pack/configuration/*.yml` — those are build output and committing changes to them means the next build discards them. Two places that are hand-maintained: | file | what it is | |---|---| | `tools/build-config.release.yml` | which pieces are generated, and how | | `src/main/resources/config.yml` | the plugin's own runtime settings | ## Requirements - Java 25 (to build; the jar it produces runs on Java 21+) - Python 3 with `pyyaml` and `pillow` - Network access on the first build, to fetch the upstream mod ## Build ```sh ./gradlew build ``` That fetches upstream, runs the generator, and assembles the jar with the pack inside. `./gradlew regeneratePack` re-runs just the generator. ## Diagnostics Seven commands exist behind a JVM argument and a permission. They are not features and are deliberately absent from the player-facing [Commands](Commands.md) page — but they are how the design decisions below were *measured* rather than guessed, so they are worth knowing about if you are reading this page. Enable with a system property, because it is read during `onLoad()` before the plugin data folder exists: ```sh java -Dcmb.debug=true -jar paper.jar ``` The argument registers the commands; each also requires the `cmb.debug` permission to run. | command | what it measures | |---|---| | `/cmbvshape` | vanilla collision shapes. **This is what justified the wall family being furniture**: nothing vanilla collides like a standing half-block, and a bell — the only exact match — covers four states that would have to be taken from real bells | | `/cmbcut` | what a stonecutter offers for a given item, asked of a real one. **This is what showed a CMB stonecutter recipe was also accepting vanilla blocks**, which is why it now takes only its own ingredient | | `/cmbvslab` | vertical slab placement, including the double-to-block merge | | `/cmbstairs` | the stairs state matrix. Also runs automatically on `CraftEngineReloadEvent` | | `/cmb1b` | **does the live server match the generated pack?** Expectations come from the pack's own `intermediate/content.json` and `allocation.json` — which carrier each block was assigned, which states it declares. Scoped to cubes and pillars, since a cube failure is systemic rather than per-block. Deliberately not a second source of truth | | `/cmbpieces` | dumps the served pieces | | `/cmbcraft` | crafting behaviour | Three non-command diagnostics come on with the same property: - **`OwnershipVerifier`** — logs `ownership verifier active (-Dcmb.debug=true)` and writes `plugins/CMB/block-overrides.json`. Dumps the actual blockstate bindings after CraftEngine has parsed its packs, and compares them against what the pre-parse scan found. Catches the silent case: a binding from another pack that CMB never discovered looks identical to a clean allocation until the server logs a bind failure. - **`RegistryTimingProbe`** — how long CraftEngine's registry work takes. - **`StairsMatrixCommand`** — the automatic run on CraftEngine reload. If a design decision on this wiki looks arbitrary, one of these is usually why. Running `/cmbvshape` and `/cmbcut` before proposing a change tends to save the argument. ## Upstream pin `upstream/upstream.properties`: ```properties repo=cinchtail/Cinchs_Missing_Blocks branch=Multi-Loader-1.21.1 version=4.4.0 commit=f7035f29f082616ed44dc7e3b355265aa203c34f content_sha256=47166b3117ecc3df4645063b6f679b2a682ae5d9fd0c7871743f3f09851c67aa ``` Never edit that by hand — move it with: ```sh python3 tools/upstream.py bump ``` The `upstream-watch` workflow does this and opens a PR when the upstream branch moves. The `content_sha256` covers the files the generator reads, so the GitHub copy and the bundled fallback are provably the same content. ## Turning pieces on and off `tools/build-config.release.yml`: ```yaml features: vertical-slabs: cmb: true vanilla: true disabled: [] stonecutting: true doubles: both horizontal-stairs: cmb: true vanilla: true disabled: [] stonecutting: true furniture-fallback: true ``` Adding a `stonecutting: true` emits one stonecutter recipe per piece, one output each, alongside the existing shaped recipe. Chiseled sources are skipped — vanilla does not cut its own chiseled stone bricks either. ## Gates The build fails rather than shipping a pack that is subtly wrong. The checks cover: | gate | what it proves | |---|---| | `carrier_eligibility` | no borrowed state is multipart or collision-unsafe | | `carrier_invariant` | claimed states are released, lendable and distinct | | `block_schema` | every declared variant resolves | | `model_references` | every model named by a blockstate exists | | `l10n_references` | every translation key referenced is declared | | `native_functionality` | drops, recipes and mining tags survive the port | | `deferred_references` | nothing emitted names a deferred block | | `yaml_sizes` | no generated file is over CraftEngine's limit | ## Count check 996 pieces / 347 blocks / 649 furniture / 1,822 items / 1,383 recipes is **derived from the pack**, not typed into the docs. The check counts both trees and subtracts the 268 doubled ids that appear in both, and fails on drift. This matters because 268 doubled ids exist in the block tree *and* the furniture tree. Counting both without subtracting gives 1,264, which was a real bug here for three rounds of correction before the overlap was found rather than the arithmetic. ## Adding a piece 1. Add or adjust config in `tools/build-config.release.yml`. 2. `./gradlew regeneratePack` 3. Confirm the summary's counts and that no gate failed. 4. Update the docs only if a count actually changed — the check will tell you. Do not hand-edit anything under `pack/`.