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
Modelfield on a small, name-matched set of.itemfiles with realfacepunch.*cloud-package references, using the confirmed__referencesdeclaration 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.