One Studios
Guides

Clothing as Items

Dynamic, metadata-based clothing that auto-detects any appearance script and turns worn outfits into real inventory items.

One Inventory turns clothing into real items. They equip, unequip, drop, trade, give and sell like anything else, and the matching item auto-creates whenever the worn clothing changes. Every appearance / clothing script works with the inventory; some need a small one-time edit.

Appearance script support

Every appearance / clothing script works with the inventory. Most need no setup at all; a few need one small edit (see Required edits). The matching item auto-creates whenever the worn clothing changes, whatever script you run.

Pause the thread during a custom clothing-shop or character-creator preview so intermediate try-on looks aren't captured:
exports.one_inventory:SetClothingPollingEnabled(false) -- pause
exports.one_inventory:SetClothingPollingEnabled(true)  -- resume
This pauses ONLY the polling thread. Scripts with a save event keep syncing.

Required edits

Most appearance scripts need no changes. The ones below need one small, one-time edit.

We read the current outfit from qb-clothing, so it needs one export. Paste this at the bottom of qb-clothing/client.lua (some forks use qb-clothing/client/main.lua):

exports('GetSkinData', function() return skinData end)

How it works

Two modes

One DB row per piece, with a fixed drawable / texture. Good for unique pieces. Given by name:

exports.one_inventory:AddItem(src, 'onestudios_jacket', 1)

Static pieces use their own definition name and carry no metadata.

When a script sets a new outfit

When an appearance change comes from a script (a clothing store, a wardrobe, the character creator), the affected slot is reconciled. What happens to that slot's item depends on what was already in it:

Slot stateResult
Wearing a dynamic itemIts ids morph in place, no clutter, unless Keep Replaced Clothing is enabled in the config (then the replaced piece returns to the inventory and a new one is minted).
Wearing a static pieceIt always swaps back, and a new dynamic item is created.
EmptyA new dynamic item is created.
Re-applying an owned outfit can duplicate (accepted). We can't tell a fresh buy from re-applying a saved outfit you already own. So: unequip a piece (item to inventory), then re-apply that outfit and confirm, and you get a SECOND item. This is unavoidable with escrowed scripts. Avoid it by unequipping from the inventory, or by disabling the script's saved-outfit / wardrobe feature.

Config (admin panel)

Dynamic Synchronization
boolean
Toggles auto-create / update from appearance changes. OFF disables ALL appearance-driven sync (both save events and the polling thread). Existing dynamic items still equip / unequip.
Keep Replaced Clothing
boolean
Decides what happens when a worn dynamic item changes. OFF morphs it in place (default, no clutter). ON returns the replaced piece to the inventory and mints a new one (a wardrobe of every look).
Clothing Sync Interval (ms)
number
How often the polling thread checks worn clothing, for scripts without a save event.
Naked / Underwear Values
table
The drawable + texture each component is set to when unequipped (legs go to underwear). Set per sex (Male / Female tab; we pick by the player's ped model), and applied on every unequip whether or not an appearance script is installed.

Taking clothing images

Dynamic clothing icons are generated in-game with an admin green-screen capture. The capture assets ship disabled by default, because, while they're loaded, a green-screen box is streamed into the world. Only enable them to generate icons, and do it on a dev / staging server or during downtime.

Enable the capture stream

Two switches must BOTH be on (screenshot-basic must also be started before one_inventory):

  1. Rename one_inventory/stream_disabled to stream (FiveM only auto-loads a folder named exactly stream).
  2. Uncomment this_is_a_map 'yes' in one_inventory/fxmanifest.lua (this places the green-screen box in the world).
  3. restart one_inventory (or restart the server).

Take the pictures

Open the admin panel, go to Clothing -> Take pictures, then pick a component (or all), a drawable (or all), the gender, and optionally override the camera. X / Escape cancels.

Find the icons

Captured icons are written to:

one_inventory/web/images/clothing/<component>/<model>-<drawable>.webp

Restart the resource (or let the file watcher pick them up) to see new icons in-game.

Disable the capture stream

Back to normal play, reverse both switches:

  1. Rename stream back to stream_disabled.
  2. Comment out this_is_a_map 'yes' in one_inventory/fxmanifest.lua again.
  3. restart one_inventory.

Already-captured icons are served independently of this stream, so disabling it does not affect icons you've already made.

Adding a new appearance script (devs)

Drop client/integrations/clothing/<resource>.lua, plus a server file only if the script has a real save event.

Guard and name

if GetResourceState('<resource>') == 'missing' then return end
Clothing = Clothing or {}
Clothing.Name = '<resource>'

Provide Clothing.ApplyEquipped

function Clothing.ApplyEquipped(equippedMap)
  -- equippedMap = { [component] = { drawable, texture } }, equipped slots only; absent = bare
  -- Read the script's full skin, overlay our 14, apply clothing-only, return true.
  -- return false to fall back to native apply.
end

Pick the outbound helper that matches the script's skin format:

keyed
{ [key] = { item, texture } }
Build it yourself. See qb.lua.
unified
{ components = [{ component_id, drawable, texture }], props = [...] }
ClothingOverlayAppearance(appearance, equippedMap, slotMap). See illenium.lua / fivem.lua.
ESX-flat
{ tshirt_1 = drawable, tshirt_2 = texture, ... }
ClothingOverlayFlatSkin(skin, equippedMap, flatMap). See esxskin.lua.

Inbound auto-create

Pick the path that matches the script:

Set Clothing.ConcileByEvent = true in the client file so the polling thread skips this script and the save event drives sync instead. If the script persists and re-applies our look on login, also mark our own saves (with MarkInternalClothingSave()) before triggering its save, so it restores exactly what we equipped:

-- client file
Clothing.ConcileByEvent = true

-- when we apply clothing, persist it through the script
MarkInternalClothingSave()
-- then trigger the script's save

Add server/integrations/clothing/<resource>.lua that hooks the save event, skips the saves we triggered, and reconciles the rest:

-- server file
RegisterNetEvent('<resource>:onSave', function(src, skin)
    if ConsumeInternalClothingSave(src) then return end
    ReconcileClothingSkin(src, <toPieces>(skin, MAP, GetClothingSex(src)))
end)

See also

The client-side clothing exports (GetEquippedClothing, EquipClothing, UnequipClothing) drive equip / unequip from your own scripts.