Reading & removing pelts stowed on a horse: use kd_stable's state.kd_stable_stow, not the raw _GET_PELT_FROM_HORSE / _SET_PELT natives
Context
On a server running kd_stable (the common KD horse/stable system), horses already have a full pelt-stow system. If you build your own pelt read/remove on the raw RDR3 natives in parallel, the two systems desync — pelts get wiped, don't re-attach, or duplicate. Integrate with kd_stable instead.
kd_stable's pelt model (verified in modules/horseStow/)
- Pelts stowed on a horse live in the networked state bag
Entity(horse).state.kd_stable_stow= a Lua array of{ type = <provisionPeltHash>, model = <animalModelHash> }, capped atConfig.maxPeltOnHorse(default 3), gated byConfig.allowStowSmallPeltOnHorse. typeis theprovision_<animal>_hide|skin|fur|pelt_<poor|good|pristine>hash — animal AND quality are baked into it.- The horse's persistent DB id is
Entity(horse).state.kd_stable_horseID(= kd_horses.id), set on spawn, networked. Owner isEntity(horse).state.kd_stable_horseOwner(a server id).
To READ pelts for a shop/sell UI
local stow = Entity(horse).state.kd_stable_stow or {}
-- each stow[i] = { type=peltHash, model=animalModel }
(In a TS resource: Entity(horse).state?.kd_stable_stow.)
To REMOVE a pelt (sell/consume) — do BOTH halves, like kd_stable's own RemovePeltFromHorse:
-- 1) update kd_stable's tracked list (1-based index into kd_stable_stow):
TriggerServerEvent("kd_stable:server:removePelt", PedToNet(horse), index + 1, NetworkHasControlOfEntity(horse))
-- 2) clear the VISUAL pelt object off the horse's back:
local peltId = Citizen.InvokeNative(0x0CEEB6F4780B1F2F, horse, index) -- _GET_PELT_FROM_HORSE
Citizen.InvokeNative(0x627F7F3A0C4C51FF, horse, peltId) -- _CLEAR_PELT_FROM_HORSE
If you only fire the server event, the data updates but the hide stays visually on the horse's back (common bug). If you only clear the visual, kd_stable's list desyncs.
To ADD a pelt back (e.g. restoring persisted pelts on spawn)
TriggerServerEvent("kd_server:server:addPelt", PedToNet(horse), peltHash, animalModel) -- updates state bag
Citizen.InvokeNative(0xC412AA1C73111FE0, horse, peltHash, albedoTex, 0, 0) -- _SET_PELT_FOR_HORSE_BY_INVENTORY_ITEM (visual)
Note _SET_PELT_FOR_HORSE_BY_INVENTORY_ITEM's args are (horse, itemHash, albedo, normal, p4) — albedo/normal are per-animal cloth textures (e.g. joaat("a_c_deer_01_uppr_000_c0_001_ab")). They're COSMETIC only; the pelt's identity/value come from itemHash, so a generic/slightly-wrong albedo still restores the correct pelt.
Gotchas
- kd_stable does NOT persist
kd_stable_stowacross disconnect — it's session/state-bag only. If you want persistence you must save/restore it yourself, keyed bykd_stable_horseID. - After
removePelt, the state bag updates via a server round-trip + replication. If you readkd_stable_stowimmediately after to re-save it, you get the OLD list — wait ~500-600ms. - To trigger a per-horse "restore on spawn", a poll (check for a horse whose
kd_stable_horseIDyou haven't handled yet) is far more reliable thanAddStateBagChangeHandler("kd_stable_horseID", ...), which often misses (value set before the handler registers, or entity not resolvable at change time).
Linked natives: _GET_PELT_FROM_HORSE 0x0CEEB6F4780B1F2F, _CLEAR_PELT_FROM_HORSE 0x627F7F3A0C4C51FF, _SET_PELT_FOR_HORSE_BY_INVENTORY_ITEM 0xC412AA1C73111FE0.
Linked natives
_CLEAR_PELT_FROM_HORSE(0x627F7F3A0C4C51FF)_GET_PELT_FROM_HORSE(0x0CEEB6F4780B1F2F)_SET_PELT_FOR_HORSE_BY_INVENTORY_ITEM(0xC412AA1C73111FE0)
Tags: pelt, hunting, kd_stable, horse, statebag, carriable, rdr3
Category: natives
Source: scandi-hunting-build
Created: 2026-06-04T14:35:39.139Z