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
- Git source → build (APK / desktop / web) — everything we may redistribute: all code (
scripts/,scenes/), all data resources (data/*.tresregistries),themes/, our original art, and permissively-licensed assets (Creative Commons, vendoredassets/kenney_*). Committed and compiled into the build. - Arweave
assets.pck— the SEGA / Phantasy Star–derived assets we can't redistribute in the repo (enemy / player / NPC models, weapons, music, SFX, stage meshes & textures, …; ~340 MB). Not committed. The runtimebootstrap.gddownloads it on first launch, verifies its sha256 againstassets_manifest.json, and mounts it. - R2 raw mirror — the same pack assets as individual files, for the Vite web tools (quest editor, storybook, retarget viewers) via
VITE_ASSETS_BASE, and doubling as the dev backup. Not committed. A few bundle-OK-but-not-raw-redistributable assets are excluded (see Backup).
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?
- Yes → commit to source (ships in the build): all code, plus original art we made and permissively-licensed assets (Creative Commons, vendored packs like
kenney_*). - No → pack only (Arweave, not committed): SEGA / Phantasy Star–derived assets. The pack is the fan-game distribution mechanism that keeps them out of the 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)
export_presets.cfg— the per-platform include/exclude that decides build-vs-pack.assets_manifest.json— the pack the game expects:version,pack.sha256,pack.size, gatewayurls, andsidecar.urls.asset_tree.txt— the flat list of every path inside the published pack, committed alongside the manifest socheck-asset-refscan verify source references resolve.
Invariants (CI MUST enforce)
- Every referenced asset is in the pack. Each
res://assets/…string in source MUST appear inasset_tree.txt. (Guard:check-asset-refs.) - Texture URIs inside GLBs resolve. A GLB's
images[].uri(a bare filename Godot resolves as a GLB sibling) MUST exist on disk / in the tree. Source-text greps can't see inside binary GLBs, so this is its own check. (Guard: GLB-URI check — the blind spot behind issue #245.) - The published pack matches the manifest. The pack URL serves a valid
GDPCand the sidecar'sversion/sha256/sizeequal the in-repo manifest. (Guard:verify-assets.) - The R2 mirror has every added/modified asset. (Guard:
r2-mirror-check.)
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:
assets/kenney_*— vendored in git (Creative Commons), so the repo is its backup.assets/npcs/cowgirl/— the Booth.pm license permits bundling (so it ships in the pack) but not re-hosting the raw file on a public CDN, so it's excluded from the R2 mirror. The model actually used is a custom hybrid GLB (cowgirl_hybrid.glb); keep an offline copy of any cowgirl source that isn't committed to git, since R2 (the usual backup) doesn't hold it.
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)
cd scripts/publish && npm run sync-tree— upload added/modified files to the R2 mirror (no commit).npm run upload-pack— rebuilddist/assets.pck, upload to Arweave, rewriteassets_manifest.json+asset_tree.txt.- Bump
VERSION+project.godot, commit the manifest + tree, open a PR.verify-assets+r2-mirror-checkvalidate both channels.