Asset Distribution

The key words MUST, MUST NOT, and MAY below are used as in RFC 2119.

PSZ is a fan game, and that — not file size — is what decides where an asset lives. SEGA's Phantasy Star assets MUST NOT be redistributed in this public git repo, so they're kept out of source and shipped through a separately-published pack. Everything the project owns (original art, all code) or is permissively licensed to redistribute (Creative Commons, vendored permissive packs) is committed to source. Absent licensing, we'd simply commit and distribute everything; the split exists purely to keep redistributable and non-redistributable content apart. This page is the canonical definition of what lives where and the invariants CI enforces.

The three channels

The split rule

The dividing line is licensing / provenance, not size or file type. Ask: may we legally redistribute this file in a public repo?

Size is incidental — the SEGA media happens to be the bulk, and keeping it out of the binary also keeps the download small, but that's a side effect, not the reason. The authoritative, machine-readable encoding of the split is the export presets (export_presets.cfg): the platform presets (Android / Linux / Windows / macOS) exclude_filter the SEGA-derived dirs (so they stay out of the binary), and the "Asset Pack" preset builds dist/assets.pck from them. This page is the intent; the presets are the mechanism. When they disagree, the presets win and this page is the bug.

Source-of-truth files (committed)

Invariants (CI MUST enforce)

Code in the pack is harmless (and accepted)

Godot's --export-pack (all_resources) bundles scripts/, scenes/, and .godot/global_script_class_cache.cfg into the pack, and the glob exclude_filter can't strip resources. This is fine. The pack is mounted with load_resource_pack(path, false)replace_files=false, so in-tree (build) resources win — and the global class cache is read at engine init, before the pack mounts. So the build's code is always authoritative; the pack's code copy never runs. The only way to produce a truly code-free pack is a custom PCKPacker, which was attempted and abandoned as not worth it — so this is accepted, not gated (the publish logs pack composition for visibility but never fails on it; see issue #284).

A direct consequence: a code-only change never needs a republish. Editing scripts/ or scenes/ ships entirely via the next build; the pack is irrelevant to it. Only adding or changing files under assets/ (the SEGA media) requires a republish.

Backup of the dev asset tree

Much of the working media under assets/ is not committed to git (the heavy art is gitignored — it lives in the pack, not the repo). The safety net for it is R2: npm run sync-tree mirrors the entire assets/ tree to R2 (incremental md5 diff), so the R2 mirror doubles as the dev-asset backup, not just the web-tools CDN. Run it whenever you add or change media; a lost dev box can then be restored with scripts/tools/fetch_assets_dev.sh. R2 credentials live in .env (R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, …).

Two things are not on R2 and rely on other backups:

Caveat: sync-tree --delete removes R2 objects missing locally, so run a plain sync-tree (never --delete) from an incomplete local tree, or it will prune the backup.

Publishing (when assets change)

  1. cd scripts/publish && npm run sync-tree — upload added/modified files to the R2 mirror (no commit).
  2. npm run upload-pack — rebuild dist/assets.pck, upload to Arweave, rewrite assets_manifest.json + asset_tree.txt.
  3. Bump VERSION + project.godot, commit the manifest + tree, open a PR. verify-assets + r2-mirror-check validate both channels.