synvael/docs/packs.md
Serkyo cae434d227
Some checks are pending
CI / Rust Check & Lint (push) Waiting to run
CI / Rust Tests (push) Waiting to run
CI / Lua Lint & Format (push) Waiting to run
CI / LFS Pointer Guard (push) Waiting to run
docs(workspace): rewrite the subsystem notes and ADRs
2026-08-06 22:52:57 +02:00

60 lines
4.2 KiB
Markdown

# Data packs and resource packs
These are two completely separate, orthogonal systems. We deliberately keep them apart rather than collapsing them into a single generic "pack" concept. (The decision to have data packs register through the modding API instead of building a parallel system is documented in [ADR-0007](adr/0007-declarative-content-via-modding-api.md)).
## Resource packs
Resource packs are strictly client-side asset overlays. They contain textures, sounds, models, fonts, and language files, but absolutely zero logic.
A pack is just a directory tree that mirrors the structure of `/assets/` and overrides files based on their path. When the renderer or asset loader looks for an asset ID, it resolves it against a stack of pack roots (starting from the base game, up through installed packs ordered by priority). The topmost hit wins. Ownership of this system sits entirely with the asset pipeline (currently in `client`, but it might move to a sibling `assets` crate if it grows). Pack authors never need to touch Lua.
A client's choice of resource packs is a purely local decision. The server has no say over them, and they are never checked during multiplayer modlist matching. The **one** exception to this is a **server resource pack**. A server can push a single cosmetic overlay (like a themed or total-conversion server) to connecting clients. This is a one-way push from server to client, applied squarely on top of the client's local stack. The server decides if it's optional (the client can decline and keep playing) or required (if the client declines or the fetch fails, the connection is rejected). Crucially, a server resource pack is still strictly `assets/`-only; it cannot contain `data/` or `scripts/`, ensuring it can never accidentally affect authoritative gameplay state.
## Data packs
Data packs handle declarative content definitions using JSON (though we might evaluate TOML or RON later). They define things like blocks, items, recipes, loot tables, biomes, and tags.
We intentionally did not build a parallel registration system for this. The loader simply parses the declarative files and calls the exact same Lua API that the engine and Lua mods use. This gives us one single source of truth:
```
data/blocks/stone.json → loader → blocks.register{ id = "stone", ... }
```
The loader logic belongs in `scripting` (or a sibling crate if it gets too large). Because they interface with the API, every data-pack schema is treated as a stable contract and versioned deliberately.
**Declarative-first approach (ADR-0007):** JSON and Lua are not meant to be interchangeable options. Anything that can be expressed purely as data (like the static fields of a block, item, recipe, loot table, biome, or tag) must be authored as data inside `data/`. We reserve Lua strictly for behavior, meaning logic that runs on an event or a tick. Because of this, a pure-data block needs absolutely zero Lua, and a data pack can register it entirely on its own. Only the behavioral half (if the block actually has any) comes from a Lua mod.
Our first-party engine content follows this exact same rule: we keep all pure-data `core` content in `data/`, and only behavioral systems live in `scripts/`. If a modder wants to avoid hand-authoring a massive amount of near-identical JSON files, they can use **datagen**. Datagen is code that emits `data/` files at build time before the pack ships. The final generated files are the shipped artifact, while the generator code itself never runs at game load time.
## Canonical load order
Later layers always override earlier ones:
```
base game (assets/scripts + assets/data)
→ data packs (adds/overrides declarative content)
→ Lua mods (has full API access)
→ resource packs (client-only asset overlays, loaded last so they dictate the final visuals)
```
## Repo layout
```
/assets/
data/ # base-game declarative content
blocks/ items/ recipes/ ...
scripts/ # base-game Lua (behavior)
textures/ models/ sounds/ icons/ shaders/ # base-game assets
```
## User-data layout
This is resolved at runtime using the `directories` (or `dirs`) crate:
```
<user-data>/
mods/ # Lua mods
datapacks/ # JSON content packs
resourcepacks/ # asset overlays (client only)
```