The .unitypackage format
avatar-unitypackage (CLI avatar unitypackage) reads Unity's
.unitypackage distribution format — the file an avatar or world is usually shipped
as — and bridges it to the rest of the toolchain, plus a file-level "test an avatar inside a world"
cross-check. Full text: docs/reference/unitypackage.md.
The format: gzip + tar, GUID directories
avatar unitypackage info avatar.unitypackage
avatar unitypackage list avatar.unitypackage
avatar unitypackage extract avatar.unitypackage -o proj
avatar unitypackage testbed avatar.unitypackage world.unitypackage --strict
A .unitypackage is a gzip-compressed tar. There is no manifest; every asset
is a top-level directory named by its Unity GUID (32 hex chars), containing:
| Member | Contents |
|---|---|
pathname | The project-relative path the asset had on export, e.g. Assets/Avatar/final.fbx. First non-empty line is the path; Unity occasionally appends trailing lines, which are ignored. |
asset | The raw asset bytes. Absent for folder entries (a directory has a pathname and a .meta but no asset). |
asset.meta | The Unity .meta sidecar (YAML) — same guid, plus import settings. |
preview.png | Optional thumbnail. Ignored. |
The reader loads the whole archive into memory and indexes it by GUID — built for one-shot CLI runs,
not streaming. A large world export can be ~1 GB uncompressed; the testbed cross-check
of a ~90 MB avatar against a ~700 MB world peaks around 1.1 GB RSS.
Summarizing — avatar unitypackage info
UnityPackage::summary() reports entry/file/folder counts, total asset bytes, a size breakdown
by extension, and heuristic PackageTraits:
vrc_sdk—Sdk2,Sdk3Avatars,Sdk3Worlds,Unknown, or none. Determined from the runtime plugin DLLs (VRCSDK2.dllvsVRCSDK3*.dll) and VPM package paths (com.vrchat.avatars/com.vrchat.worlds) — not fromVRCSDK/version.txt, which is date-based and whose 2021-era bundle shipped SDK2 and SDK3 editor scripts in one folder, making the source tree alone ambiguous.looks_like_avatar— a VRChat avatar prefab (prefab-id-v1_avtr_*.prefab) is present.looks_like_world— scenes present and no avatar prefab.
Extracting — avatar unitypackage extract
extract reconstructs a normal Unity project tree: each asset at
<dest>/<pathname> with its .meta beside it. Because the lint/stats
and FBX/armature tools only need files on disk plus the .meta GUID index, an extracted
package is immediately consumable:
avatar unitypackage extract avatar.unitypackage -o /tmp/proj
avatar lint /tmp/proj
avatar stats /tmp/proj/Assets/Avatar/.../final.fbx
avatar armature check /tmp/proj/Assets/Avatar/.../final.fbxAssets/, Packages/,
ProjectSettings/, …), but old SDK exports sometimes leak absolute paths to bundled
editor DLLs. The extractor rejects absolute POSIX paths, Windows drive letters (C:/…
— important on non-Windows hosts, where the std path parser treats C: as an ordinary
segment), UNC roots, and .. traversal; these are counted in
ExtractReport::skipped_unsafe and not written.
The avatar-in-world testbed
avatar unitypackage testbed avatar.unitypackage world.unitypackage
Testing an avatar "in a map" offline is, at the file level, the question: if I import both packages
into one Unity project, what breaks? UnityPackage::overlap answers it:
- GUID collisions — the same GUID in both packages. Flagged
identicalwhen the asset bytes match (a harmless duplicate); otherwise Unity keeps whichever was imported last, silently changing one package's asset — e.g. two shader or SDK versions fighting. - Path collisions — different GUIDs claiming the same
Assets/...path. The second import overwrites the first file on disk while keeping a different GUID, so references can dangle.
--strict exits non-zero when any different-bytes GUID collision or path collision exists, for
gating; --json emits both package summaries and the full overlap. A clean result means the
avatar can be dropped into the world's project to preview scale, lighting, and placement without
clobbering the world's assets.
Notes
SDK2 avatars surface their age through the rest of the toolchain naturally: avatar lint
reports VRC001 (no com.vrchat.avatars package) and finds no SDK3 descriptor,
and avatar stats on the project finds no avatar because the prefab carries the SDK2
VRC_AvatarDescriptor. Run avatar stats <fbx> directly for the geometry
rank regardless — or take the SDK2 package through
avatar migrate sdk3.