# 25 - Pack Management Pack management covers download/install into the packs workspace, validation, unused-resource cleanup and restore, packaging for distribution, and unsafe replacement of a live world’s pack snapshot. Authoring packs live under the platform packs root; production worlds copy that tree into `/iris/pack` (see `05 - Concepts & Pack Layout.md` and `06 - Worlds & Lifecycle.md`). See also: `03 - Configuration.md`, `04 - Commands & Permissions.md`, `10 - Studio & VSCode Schemas.md`, `24 - Pack Mods & Snippets.md`, `27 - Example - Configuring Overworld.md`. ## Pack workspace | Item | Path / rule | |------|-------------| | Packs root | Bukkit: plugin data `packs/`; modded: `config/irisworldgen/packs/` (platform data folder) | | Visible packs | Non-hidden directories listed by `PackDirectoryResolver` | | Presence | Pack exists if it has safe tree + at least one `dimensions/*.json` (parse failures do not trigger redownload) | | Safe key | Download destination keys: `[a-z0-9_-]+` | ## Download ### Commands | Command | Behavior | |---------|----------| | Bukkit: `/iris download [branch=stable] [overwrite=false]`; modded: `/iris download [branch] [force]` | Download into packs root | | Default overworld special case | Pack name `overworld` uses IrisDimensions overworld **beta release zip** (`…/releases/download/beta/overworld.zip`), not an arbitrary branch zip | | Other packs | `IrisDimensions//` GitHub archive search via `StudioSVC.downloadSearch` | | Param | Default | Notes | |-------|---------|-------| | `pack` | required | Folder/key or repo short name | | `branch` | `stable` | GitHub ref when not default overworld | | `overwrite` | `false` | Force replace existing present pack | ### Install pipeline (`PackDownloader`) 1. Per-key/ref download lock (concurrent startup and commands do not double-fetch). 2. If pack present and not force → skip network. 3. Download zip (size/entry limits: archive ≤512MiB, ≤100k entries, total uncompressed budget, per-file cap). 4. Unpack to temp; require single pack home directory. 5. Open as datapack-compiler `IrisData`; require **exactly one** dimension; key = that dimension load key. 6. Run `PackValidator.validate`; blocking errors abort install. 7. Publish into `packs//` with conflict checks (refuses symlink targets; detects dimension-key conflicts with other folders). Default overworld repository constant: `IrisDimensions/overworld`. ## Validate | Command | Behavior | |---------|----------| | Bukkit: `/iris pack validate [pack=]`; modded: `/iris pack validate [pack]` | Validate one pack or all visible packs; publish into `PackValidationRegistry` | | Bukkit: `/iris pack status [pack=]`; modded: `/iris pack status [pack]` | Show cached registry results (run validate first) | ### Checks performed (`PackValidator`) | Check | Blocking vs warning | |-------|---------------------| | Missing pack / missing `dimensions/` / no dimension JSON | Blocking | | Dimension JSON integrity (`PackDimensionValidator`) | Blocking / warnings as emitted | | Loot graph (`PackLootValidator`) | Blocking | | Removed worldgen fields (e.g. `fluidBodies`) | Blocking | | Object surface support | Blocking | | Unsupported structure transforms (`rotation` / `translate` / `scale` on forbidden surfaces) | Blocking | | Structure graph + compiled graph validator | Errors blocking; warnings advisory | | Native structure replacement envelopes | Blocking | | Spawner → entity references | Blocking | | Custom biome spawns category resolution | Blocking | | Content keys / bad block properties (`ContentKeyValidator`) | Blocking when `general.strictContentKeys` or `-Diris.strictContent`; else warnings (palette-sourced stay advisory) | `isLoadable()` is false when any blocking error exists. Status reports blocking count and up to 10 warnings (plus “more” count). ## Cleanup (unused resources) | Command | Mode | Behavior | |---------|------|----------| | Bukkit: `/iris pack cleanup [mode=preview]`; modded: `/iris pack cleanup [apply]` | `preview` (default) | List unused candidates; no writes | | | `apply` | Quarantine candidates under pack `.iris-trash//` | Managed folders scanned for unreferenced JSON resources: `biomes`, `regions`, `entities`, `spawners`, `loot`, `generators`, `expressions`, `markers`, `blocks`, `mods`. Excluded from cleanup corpus: `.iris-trash`, `datapack-imports`, `externaldatapacks`, `internaldatapacks`, `datapacks`, `cache`, `objects`, `.iris`. Cleanup re-scans on apply (not a blind apply of an old preview). Failed apply may leave paths still quarantined and reports them. ## Restore | Command | Mode | Behavior | |---------|------|----------| | Bukkit: `/iris pack restore [mode=preview]`; modded: `/iris pack restore [apply]` | `preview` | List latest quarantine dump files and conflicts | | | `apply` | Move files back from latest dump if destinations free | Restore **refuses** when destination paths already exist (conflict list). Nothing restored when no quarantine dump exists. ## Package (export) | Command | Behavior | |---------|----------| | Bukkit: `/iris studio package [dimension=default] [obfuscate=false] [minify=true]`; modded: `/iris studio package [pack]` | Compile dimension closure to a zip | | Param | Default | Notes | |-------|---------|-------| | `dimension` | contextual / `default` | Dimension in packs | | `obfuscate` | `false` | Obfuscate packaged content when true | | `minify` | `true` | Compact JSON (indent 0) | Pipeline (`IrisPackageCompiler`): 1. Load dimension and walk regions → biomes → generators, loot, entities, spawners, structures/objects closure. 2. Stage under Iris data `exports//`. 3. Write `package.json` with hash, time, version. 4. Zip to `exports/.iris` (compression level 9); delete staging folder. Does not modify the source pack or any world snapshot. ## Developer update-world (unsafe) | Command | Behavior | |---------|----------| | `/iris developer update-world world= pack= confirm=true [fresh-download=false]` | Replace the world’s pack snapshot | | Param | Default | Notes | |-------|---------|-------| | `world` | contextual | Target world folder | | `pack` / dimension | contextual | Source dimension (live packs root) | | `confirm` | `false` | Required true; otherwise prints warning only | | `fresh-download` | `false` | Re-download pack before install | Implementation: 1. Requires `confirm=true`. 2. Optional `StudioSVC.downloadSearch` when `fresh-download`. 3. Acquires `PACK_MUTATION` / `PACK_PUBLISH` lease. 4. `StudioSVC.replaceIntoWorld` → install into `worldFolder/iris/pack` with `replaceExisting=true` (atomic stage/publish). 5. If an engine still holds that pack data, Iris **restarts the server** after commit (`"An active Iris world pack was replaced."`). This is intentionally unsafe for production without backups: existing chunks keep old terrain; only future generation and pack-driven systems see new content. Prefer staging a new world when pack contracts change. ## Related operations | Task | Where | |------|-------| | Create studio project from template | `/iris studio create` (`10 - Studio & VSCode Schemas.md`) | | Open VSCode + schemas | `/iris studio vscode` | | Import vanilla objects/structures into pack | `/iris studio importvanilla` | | Structure import | `/iris structure …` | | Strict content keys | `settings.general.strictContentKeys` (`03 - Configuration.md`) | | Datapack bootstrap / install | Server configurator + `/iris datapack` (see platform docs) | ## Operator checklist 1. Download or place pack under `packs//` with `dimensions/*.json`. 2. On Bukkit, run `/iris pack validate pack=` until loadable. Modded uses `/iris pack validate `. 3. On Bukkit, optionally run `/iris pack cleanup mode=preview`, then `mode=apply` after review; restore if needed. Modded uses the `apply` literal. 4. Create world with `/iris create …` (copies pack) or open studio for live edit. 5. To ship on Bukkit: `/iris studio package dimension=`. 6. To refresh an existing world pack only after backup: `/iris dev update-world world= pack= confirm=true`.