RivalityFX Docsrivalityfx.com ↗

Authoring a streamed outfit pack

We'll ship custom clothing — a kind='outfit' cosmetic pack that dresses the freemode peds with garments your pack streams. This is the guide for the asset and wiring half; the descriptor fields themselves are in the pack descriptor reference.

The reference template is cosmetic_pack_vanguard (MIT): the manifest, the two .meta skeletons, and four placeholder outfits, everything present except the binary assets. Copy it, rename, and follow along.

Why outfits and not ped models? A custom ped model changes the silhouette and the damage capsule — in a PvP arena that is pay-to-win wearing a cosmetic's clothes. Freemode components leave every player on the same rig. Outfits are the appearance kind built for selling looks without touching hitboxes.

Prerequisites

  • A Cfx.re Element Club subscription (Argentum or higher) on the server's license key, for any server above 8 slots. Streaming clothing for the freemode peds is gated by Cfx.re — without the subscription the pack registers, but the collections never mount for connecting players and every equip is refused. Budget it as a running cost of the catalogue, not a one-off.
  • A cloth tool — e.g. Durty Cloth Tool — that compiles your source meshes/textures into the GTA formats. You do not hand-write any of the binary files.

1. What the cloth tool must produce

Three kinds of output, per gender (male and female catalogues are entirely separate assets — one set each):

OutputWhat it isWhere it goes
.ymtThe compiled CPedVariationInfo — the collection: the index of every drawable/texture the pack addsstream/
.ydd + .ytdThe garment meshes and their texture variantsstream/
.metaThe ShopPedApparel catalogue that introduces the DLC pack to the gameresource root, declared in the manifest (next section)

Ship the tool's .meta output verbatim and only check the names against the template's skeletons (mp_m_freemode_01_vanguard.meta / mp_f_freemode_01_vanguard.meta). Three names must agree, and everything hangs off them:

  • dlcName — your pack's short DLC name, all lowercase (the streamed .ymt filename derives from it and must be all-lowercase). This is also the dlcName your pack.lua descriptor declares.
  • pedName — the freemode ped the catalogue dresses (mp_m_freemode_01 or mp_f_freemode_01).
  • fullDlcName — always <pedName>_<dlcName>. This is the collection name the runtime targets, and the prefix of every streamed filename.

2. The manifest wiring — where everyone trips

For a streamed ped pack, dropping models into stream/ is enough — FiveM auto-streams the folder, no declaration needed. That is not true for add-on clothing. Freemode clothing only mounts as a variation collection if the game is told the DLC pack exists, and that introduction is the ShopPedApparel .meta — which, like every data-driven .meta in FiveM, needs two declarations:

-- fxmanifest.lua
files {
    'mp_m_freemode_01_vanguard.meta',   -- 1. ship the file to the client
    'mp_f_freemode_01_vanguard.meta',   --    (it is NOT in stream/, so it is not auto-transferred)
}
data_file 'SHOP_PED_APPAREL_META_FILE' 'mp_m_freemode_01_vanguard.meta'   -- 2. parse it
data_file 'SHOP_PED_APPAREL_META_FILE' 'mp_f_freemode_01_vanguard.meta'

Forget files{} and the data_file points at nothing; forget the data_file and the file downloads but the collection never mounts. One data_file line per meta — the male and female catalogues are separate metas (separate pedName/fullDlcName → separate collections) even when they ship in one resource. The .ydd/.ytd/.ymt binaries themselves DO go in stream/ and are auto-streamed — only the .meta needs declaring.

The rest of the manifest is the standard pack shape — dependencies { 'sdk', 'customization' } and the shared registration scripts (see the template).

3. Filename conventions

Every streamed file is prefixed by the fullDlcName. From the template's stream/README.md:

stream/
  mp_m_freemode_01_vanguard.ymt                          # the collection (CPedVariationInfo)
 
  # components: <fullDlcName>^<slot>_<NNN>_<u|r>.ydd
  #           + <fullDlcName>^<slot>_diff_<NNN>_<a-z>_<uni|whi>.ytd
  mp_m_freemode_01_vanguard^jbib_000_u.ydd               # top, drawable 000
  mp_m_freemode_01_vanguard^jbib_diff_000_a_uni.ytd      #   its texture variant a (= texture 0)
  mp_m_freemode_01_vanguard^uppr_000_u.ydd               #   matching arms — ALWAYS pair with the top
  ...
 
  # props: note the `_p_` infix and the `p_` slot names; no _u/_r suffix
  # <pedName>_p_<dlcName>^<p_slot>_<NNN>.ydd + <pedName>_p_<dlcName>^<p_slot>_diff_<NNN>_<a-z>.ytd
  mp_m_freemode_01_p_vanguard^p_head_000.ydd             # helmet
  mp_m_freemode_01_p_vanguard^p_head_diff_000_a.ytd
  • <slot> is the RAGE short name (head berd hair uppr lowr hand feet teef accs task decl jbib, props p_head p_eyes p_ears p_lwrist p_rwrist) — the same vocabulary as the descriptor's slot table.
  • <NNN> is the local drawable index — the number your descriptor's pieces declare. It is local to this collection; the runtime applies it with the collection natives, so it never collides with other packs and never shifts on a gamebuild bump. This is the addressing rule — if you take one thing from this guide, take that.
  • Model suffix _u = universal texture set, _r = race-variant; the texture suffix (_uni/_whi) must match the model's choice.
  • Texture letter az maps to texture = 025 in the descriptor.
  • Keep the whole tree lowercase — the .ymt name must be, and then you can't get it wrong.

4. The descriptor

With the assets named, the descriptor is small. Declare the dlcName once at pack level — every item derives its collection from it as <base>_<dlcName>, so the one name that must match the shipped assets is written once instead of repeated per item (repetition is how it drifts):

local PACK = definePack{
    id = 'mypack', apiVersion = 2, kind = 'outfit',
    dlcName = 'mypack',                      -- must equal the .meta dlcName
    status = 'locked', sku = '<tebex package id>',
    present = { title = 'My Pack', sub = 'Premium', rgb = '132 168 96' },
    skins = {
        { id = 'mypack_strike_m', label = 'Strike', base = 'mp_m_freemode_01',
          pieces = {
              { slot = 11, drawable = 0 },              -- jbib: the top…
              { slot = 3,  drawable = 0 },              -- uppr: …ALWAYS with its arms
              { slot = 4,  drawable = 0 },              -- lowr
              { slot = 6,  drawable = 0 },              -- feet
              { slot = 0,  drawable = 0, prop = true }, -- p_head: helmet
          } },
        -- one item per gender: the female twin targets base = 'mp_f_freemode_01'
        -- and lives in the mp_f_ collection. Keep local indices in lockstep across
        -- the two collections — it makes the asset checklist trivial.
    },
}

Two conventions worth keeping:

  • A top (11/jbib) always ships with its matching arms (3/uppr). The jbib mesh has no arms of its own; without your uppr the player keeps the previous outfit's sleeves — floating hands, arms clipping through the jacket. The validator warns when the pair is broken.
  • One item per gender. base fixes the rig and the gender (the locker's cat chip is derived from it — don't set it), so every look is two items, one per freemode ped.

5. Verify with /collections — no guessing

Your descriptor's collection name must be whatever the game actually mounted — and that is not always what you expect. Start the resource with assets in place, spawn in, and run the client command:

/collections

It dumps every variation collection mounted on your ped, with per-slot drawable counts — so both the right name and whether your drawables actually landed are one command away. Your fullDlcNames must appear there.

Add-on clothing collections only mount on the ped models they target. Run /collections while wearing a freemode ped (equip an outfit, or the base ped) — on any other model the list will not show your clothing collections, and you would wrongly conclude the pack never streamed. The command prints a note when that is the case.

6. How failures present

Deliberate design: an authoring mistake costs one console read, never a silent wrong-garment.

FailureWhat happens
Collection not mounted (resource not started, stream/ not downloaded, Element Club lapsed, name typo)The equip is refused — the player keeps their previous appearance — and the log names the collection that was wanted and every collection that IS mounted (the /collections list).
A piece's drawable/texture doesn't exist in the collectionThe piece is skipped with a warning naming slot/drawable/collection; the rest of the outfit applies and the skipped slot shows the default garment.
Top without armsValidator warning at registration (see above).

7. Limits (real ones, they bite)

  • 128 drawables per component slot per collection is the safe ceiling — at 255 the client crashes. The SDK validator warns at local index ≥ 128: split the catalogue across a second pack/collection instead.
  • 26 texture variants per drawable (the az letter).
  • Everything in stream/ is a first-connect download for every player. Pack weight is a product decision, not a technical afterthought — keep textures sane and ship looks in separate packs when the season grows.

8. Ship it

  • Git LFS for the binaries — never commit them raw: git lfs track "resources/[packs]/*/stream/**".
  • Thumbnails: an outfit id is not a ped, so there is no stock render — upload a studio shot to your assets CDN at <rfx_assets_url>/outfits/<id>.webp, or set the item's image. See thumbnails.
  • Selling it: status='locked' + sku puts it on the shelf exactly like any other pack — see Selling VIP & packs. Note the Element Club prerequisite above: for a paid outfit pack it is a hard dependency, not a convenience.

Where to go next