Skip to content

ADR-0016 — Sourcing item art from facepunch.sboxassets

Status: Accepted Date: 2026-07-31 Supersedes:Superseded by:


Context

Applejack ships no art of any kind. Every Model string in the 131 .item GameResource files under Assets/Items/ is a literal copy of the corresponding legacy Garry's Mod (Source 1) .mdl path — e.g. Assets/Items/Food/orange.item's models/props/cs_italy/orange.mdl — carried over verbatim during the Milestone 8 content migration. legacy/content/ (the only place those paths ever resolved) is gitignored, non-shippable, copyrighted Source 1 content per Legacy/00_OVERVIEW.md §3, so none of these paths have ever pointed at anything real. Assets/Items/README.md's FORMAT UNCERTAINTY section already flags vehicle_key.item's Model as "an invented placeholder path... needs real art before this asset is usable in-engine" — that disclaimer in fact applies to all 106 Model-bearing items, not just that one.

Documentation/Architecture/01_PROJECT_LAYOUT.md §3 explicitly puts art sourcing itself out of scope for Documentation/ "until art production starts." sbox.game/facepunch/sboxassets is Facepunch's own official, actively maintained collection of ~1000 free, CC0-licensed, real S&box models and materials, built specifically so any S&box project can use them without needing an artist. Cross-referencing that collection's asset names against Applejack's actual item list (done directly, not sampled — the collection's ~1000 entries were fully enumerated via the Chrome accessibility tree since sbox.game is a Blazor Server SPA with no scrapeable REST listing) turned up direct, unambiguous name matches for a handful of items already marked "Real, working" in Assets/Items/README.md's disposition table (Food, Alcohol, Ammo, Packaging) plus one Drugs item. That is enough concrete signal to justify formally starting art sourcing now, scoped to exactly those matches.

What the engine actually requires to reference a cloud package is confirmed directly from ~/Projects/sbox-public (per CLAUDE.md's standing instruction to check real engine source before guessing): - Every asset on sbox.game/<org>/<ident> is itself a package. Package.FullIdent formats as "<org>.<ident>" (engine/Sandbox.Engine/Services/Packages/Package.cs:26). - A GameResource's cloud dependencies are declared via a "__references" JSON array of exactly those full idents. CloudAsset.GetAssetReferenceSources (engine/Sandbox.Tools/Utility/CloudAsset.cs:255) reads this array straight back off every asset's JSON to resolve and download what it references. Shipped resources already carry this field in exactly this shape — e.g. game/addons/base/Assets/ammo/9mm.ammo's "__references": []. - What is not confirmed: the exact internal filename of the model file inside a single-asset cloud package (what facepunch.orange's actual .vmdl is called). sbox.game's Blazor frontend doesn't expose this over any inspectable channel, and there is no S&box editor on this development machine (CLAUDE.md — the real editor runs on a separate Windows box). No shipped sample in sbox-public demonstrates a cross-package Model reference to check against.

Decision

We will replace the Model field on a small, name-matched set of .item files with real facepunch.* cloud-package references, using the confirmed __references declaration mechanism, and flag the model path itself as unverified pending real editor access.

Concretely, for each matched item:

"Model": "models/<ident>.vmdl",
"__references": [ "facepunch.<ident>" ]
<ident> is the package's ident on sbox.game/facepunch/<ident> (e.g. orange, beer_keg_a1). The models/<ident>.vmdl filename is a best guess — it follows the one naming pattern visible anywhere in sbox-public for a single-model package (box_test.vmdl, game/addons/menu/Assets/scenes/reward-unbox.scene:1050) and how Facepunch's own single-asset cloud packages are conventionally named, but is unverified in exactly the same sense as every other "FORMAT UNCERTAINTY" note already in this repo. The __references entry is not a guess — it is required by, and read directly by, the engine code cited above.

Scope: this decision covers .item Model fields only. Code/Vehicles/, Code/World/, Code/Vendors/, Code/Housing/, and Assets/Prefabs/PlayerPawn.prefab have no model-path convention in their C# at all yet (no Model-shaped property exists on Vehicle, DoorComponent, VendorComponent, or HousingListingComponent) — wiring cloud assets into those is separate design work, not decided here. Candidate assets for that future work are listed under Notes, so this collection doesn't need re-scanning later.

Assets adopted this pass (see Assets/Items/README.md for the up-to-date list; each replaces a fabricated legacy .mdl placeholder with the same disposition otherwise unchanged): facepunch.orange, facepunch.beer_keg_a1, facepunch.ammobox45acpclosed, facepunch.plastic_crate_01, facepunch.plastic_crate_02, facepunch.plastic_crate_03, facepunch.plastic_crate_04, facepunch.health_pickup.

The full ~1000-entry collection catalog (ident + display name) is checked in at Assets/facepunch_sboxassets_catalog.tsv so no future session needs to re-drive a browser through the whole unpaginated collection page to find further matches.

Alternatives considered

Keep the legacy .mdl placeholders as-is

The status quo. Cheapest in the short term, but every placeholder is permanently non-functional — there is no legacy content to migrate (legacy/content/ cannot ship), so leaving them means these items can never actually render in-engine. This is the exact problem this ADR exists to start fixing.

Wait for/commission original art

The "real" long-term answer for a shipping game, but there is no art pipeline, no artist, and no budget signal for one yet. Blocking all placeholder items on that indefinitely means shipping nothing renderable for an unbounded time. Free, CC0, officially-sanctioned assets that already match several items by name are a strictly better interim step, and do not preclude replacing them with bespoke art later.

Favourite the collection in the Cloud Browser without touching any .item file

Zero code changes, purely an editor-side bookmark. Rejected because it does not actually wire anything into the project — no .item file would reference real content, so every item would still be exactly as broken as before. Favouriting is still worth doing for browsing convenience, but it is not a substitute for actually setting Model/__references.

Consequences

Positive

Eight items move from a fabricated, permanently-broken placeholder path to a real, free-to-use, CC0-licensed asset reference. Establishes the __references + cloud-ident convention (confirmed correct against engine source) that Vehicles/Housing/World can reuse once they grow their own model-path fields, rather than each module inventing its own.

Negative

The Model value itself (the internal filename inside each package) is still an unverified guess — if wrong, every one of these eight items needs the same mechanical find-and-replace correction once a real editor confirms the actual filename, per the project's existing "fix it, it's a mechanical correction, not a design problem" posture for unverified formats. This ADR intentionally does not attempt to wire Vehicles/Housing/World, so most of the project's asset debt remains exactly as undocumented-as-design-problem as before.

Neutral

Establishes that "art production has started," which changes the framing of Documentation/Architecture/01_PROJECT_LAYOUT.md §3's "out of scope until art production starts" caveat for any future ADR in this area — not a rule change, just a fact this decision now makes true.

Compliance

No automated check enforces this — it's a content/documentation convention. Reviewers adding new items with a cloud-sourced model should include a matching __references entry (mirroring the shape already used here) and should not assume unnamed guessed paths are correct; the project's own build/test gate does not (and cannot, from this machine) load .item assets into a real S&box editor, so nothing currently catches a wrong Model value automatically.

Notes

Open question: whether models/<ident>.vmdl is actually the right internal path for each package. Resolve by opening this project in a real S&box editor, dragging each referenced facepunch.* package from the Cloud Browser onto one of these eight items' Model field, and diffing the resulting JSON against what's checked in here. If the shape differs, correct all eight in the same mechanical pass — the idents (which package to use) are still right even if the path syntax isn't.

Backlog — further candidates found in the same collection scan, not wired in this pass (each needs its consuming system to grow a model-reference convention first): - Vendors (Code/Vendors/VendorComponent.cs): facepunch.cash_register. - Housing furniture (Code/Housing/, once a housing-interior/prefab system exists): facepunch.couch, facepunch.tv, facepunch.office_desk, facepunch.filing_cabinet_01, facepunch.fridge, facepunch.bathtub, facepunch.toilet_a, facepunch.bed_frame_01, facepunch.dentist_chair, facepunch.office_chair, facepunch.locker. - World dressing (Code/World/DoorComponent.cs and prefab-only placed geometry): facepunch.door_handle_a / _b / _c, facepunch.doorframe_metal_a, facepunch.door_lock_a, facepunch.bike_rack, facepunch.metal_wheely_bin, facepunch.park_bin. - Explicitly not suitable for Code/Vehicles/: facepunch.vista_car, facepunch.vista_truck, facepunch.vista_van, facepunch.vista_pickup — these are tagged and named as background/skybox-quality "Vista" scenery props in the collection, not driveable-vehicle-grade models. Do not reach for them when Vehicles finally gets a model field; they'd need to be re-evaluated against a real vehicle-appropriate asset instead.

Second-pass finding (2026-07-31) — this collection is not a deep well for the remaining 98 items. A full name/token match of every remaining .item against all 1000 catalog entries turned up only 7 further usable matches (wired the same pass — see Assets/Items/README.md's FORMAT UNCERTAINTY section for the exact list) before running out of anything defensible. The collection is street/industrial/construction scenery; it has no fruit, fish, baked goods, bottles/jugs beyond two beer containers, and — confirmed, not just unmatched — zero firearms and only one melee-adjacent asset (chefknife2). This means: - The 14 Weapons/PoliceWeapons/IllegalWeapons items and Explosives cannot be resolved by this collection at all, ever — that gap needs either a different free-asset source or actual custom art, not more searching here. - 91 of 106 Model-bearing items remain on dead placeholders after both passes. Free-CC0 sourcing was never going to close this project's whole asset gap by itself (see this ADR's own "Wait for/commission original art" alternative) — this finding is the concrete evidence for that, not a surprise reversal of the decision above, which remains the right interim step for the ~15 items it does genuinely cover.