Showing weapons on a player's back in FiveM
Get the weapon's world model with GetWeapontypeModel(hash), create it with CreateObject and attach it to the spine bone (SKEL_Spine3, bone id 24818, resolved with GetPedBoneIndex) using AttachEntityToEntity. Hide the prop while that weapon is in the player's hands. On ox_inventory the ped only holds the equipped weapon, so ask the inventory (GetItemCount) what the player carries. The cleanest sync is to publish the list in a player state bag and let every client draw local props for nearby players.
A rifle that simply vanishes when you holster it is one of the small things that makes GTA feel like GTA rather than a roleplay world. Slinging long guns on the player's back tells everyone nearby what a person is carrying before a word is said, and it is only a prop, a bone and an offset. The hard parts are knowing which weapons the player really owns on an inventory server, and making sure other players see the same thing you do.

#How a back-weapon script works
A back weapon is not a weapon at all. It is an ordinary object using the same model the game draws when a gun lies on the ground, glued to a bone on the ped's skeleton. The game has no idea it represents a rifle, which is exactly what you want: it cannot be fired, dropped or picked up, and it costs roughly what any small prop costs.
Every script that does this, from the old weapons-on-back releases to the ones bundled with modern frameworks, does the same four things:
- Work out which long weapons the player is carrying.
- For each one that is not currently in their hands, load the weapon's world model and create a prop.
- Attach the prop to a spine bone with a position and rotation offset so it sits across the back.
- Delete the prop the moment the weapon is drawn, and recreate it when it is put away.
| Job | Native |
|---|---|
| Model for a weapon | GetWeapontypeModel(weaponHash) |
| Load the model | RequestModel / HasModelLoaded |
| Create the prop | CreateObject(model, x, y, z, isNetwork, netMissionEntity, doorFlag) |
| Find the bone | GetPedBoneIndex(ped, 24818) |
| Attach it | AttachEntityToEntity(...) |
| Weapon in hand | GetSelectedPedWeapon(ped) |
The natives involved
#Choosing which weapons go on the back
Only long guns belong on the back. Pistols and tasers sit in a holster, which is a different job handled by a draw animation (see the holster guide). Melee weapons are a judgement call: a bat or a katana on the back reads well, a knife does not.
Keep the list in a config array rather than a keyed table. An array keeps its order, and that order becomes your priority when a player carries more long guns than you are willing to render.
Config = {}
Config.Max = 2 -- props per player
Config.Range = 60.0 -- draw distance for other players
Config.Bone = 24818 -- SKEL_Spine3
Config.Weapons = {
{ name = 'WEAPON_CARBINERIFLE', pos = vec3(0.075, -0.15, -0.02), rot = vec3(0.0, 165.0, 0.0) },
{ name = 'WEAPON_ASSAULTRIFLE', pos = vec3(0.075, -0.15, -0.02), rot = vec3(0.0, 165.0, 0.0) },
{ name = 'WEAPON_SPECIALCARBINE', pos = vec3(0.075, -0.15, -0.02), rot = vec3(0.0, 165.0, 0.0) },
{ name = 'WEAPON_PUMPSHOTGUN', pos = vec3(0.075, -0.15, 0.10), rot = vec3(0.0, 195.0, 0.0) },
{ name = 'WEAPON_SMG', pos = vec3(0.075, -0.15, 0.10), rot = vec3(0.0, 195.0, 0.0) },
{ name = 'WEAPON_BAT', pos = vec3(0.10, -0.16, 0.00), rot = vec3(0.0, 225.0, 0.0) },
}The numbers are a starting point, not gospel. Every model has its origin in a different place, so a rifle whose pivot sits at the grip needs a different offset from one whose pivot sits at the receiver. Tuning is covered further down.
#Creating the prop from the weapon's model
GetWeapontypeModel returns the model hash for any weapon the game knows about, including add-on weapons, because it reads the Model field from weapons.meta. That is why this approach works for a custom rifle without extra configuration, as long as the add-on's model is streamed and registered in weaponarchetypes.meta. The vanilla model names are listed in the weapon model names guide.
local function loadModel(model)
if model == 0 or not IsModelInCdimage(model) then return false end
RequestModel(model)
local deadline = GetGameTimer() + 5000
while not HasModelLoaded(model) do
if GetGameTimer() > deadline then return false end
Wait(0)
end
return true
end
function CreateBackProp(ped, entry)
local model = GetWeapontypeModel(joaat(entry.name))
if not loadModel(model) then return nil end
local c = GetEntityCoords(ped)
local obj = CreateObject(model, c.x, c.y, c.z - 10.0, false, false, false)
SetModelAsNoLongerNeeded(model)
SetEntityCollision(obj, false, false)
AttachEntityToEntity(obj, ped, GetPedBoneIndex(ped, Config.Bone),
entry.pos.x, entry.pos.y, entry.pos.z,
entry.rot.x, entry.rot.y, entry.rot.z,
true, true, false, true, 2, true)
return obj
endThe prop is created below the ped and attached immediately, so nobody sees it pop into existence at the player's feet. Collision is switched off because a rifle with collision catches doorframes and shoves the player sideways in tight corridors.
#AttachEntityToEntity, parameter by parameter
The attach native has fifteen parameters and most copy-pasted scripts get at least one of them wrong. In order:
| # | Parameter | Value used here | Why |
|---|---|---|---|
| 1 | entity1 | the prop | The thing being attached |
| 2 | entity2 | the ped | What it is attached to |
| 3 | boneIndex | GetPedBoneIndex(ped, 24818) | Bone index, not bone id |
| 4-6 | xPos, yPos, zPos | config offset | Position relative to the bone |
| 7-9 | xRot, yRot, zRot | config rotation | Rotation relative to the bone |
| 10 | p9 | true | Commonly passed as true |
| 11 | useSoftPinning | true | Lets the attachment detach cleanly under force |
| 12 | collision | false | No collision between prop and world |
| 13 | isPed | true | The prop is being attached to a ped |
| 14 | rotationOrder | 2 | The order rotations are applied in |
| 15 | syncRot | true | Keep the rotation locked to the bone |
AttachEntityToEntity parameters
The classic mistake is passing 24818 straight in as parameter three. That number is the bone id (SKEL_Spine3, 0x60F2); the native wants the index of that bone inside this ped's skeleton, which is what GetPedBoneIndex returns. Pass the raw id and the rifle ends up floating somewhere near the pelvis or the feet, and it will be in a different wrong place on a different ped model.
#Knowing what the player actually carries
This is where most back-weapon scripts break on modern servers. HasPedGotWeapon asks the ped, and on an inventory server the ped does not hold your whole arsenal.
- ox_inventory gives the ped only the weapon that is currently equipped. Everything else is an item in a slot, so
HasPedGotWeaponreturns false for the rifle in your bag. Ask the inventory with the client exportexports.ox_inventory:GetItemCount('WEAPON_CARBINERIFLE'). - qb-inventory works the same way: weapons are items and the ped receives one when it is used. Read
QBCore.Functions.GetPlayerData().itemson the client and look for weapon items. - ESX with the built-in loadout really does give the ped every weapon, so
HasPedGotWeapon(ped, hash, false)is correct there.
local useOx = GetResourceState('ox_inventory') == 'started'
local function carries(ped, name)
if useOx then
return exports.ox_inventory:GetItemCount(name) > 0
end
return HasPedGotWeapon(ped, joaat(name), false)
end
function GetBackList(ped)
local held = GetSelectedPedWeapon(ped)
local list = {}
for _, entry in ipairs(Config.Weapons) do
local hash = joaat(entry.name)
if hash ~= held and carries(ped, entry.name) then
list[#list + 1] = entry.name
if #list >= Config.Max then break end
end
end
return list
endExcluding the weapon in hand is what makes the rifle "move" from the back to the hands when it is drawn. Nothing moves, of course: one prop is deleted and the real weapon appears.
#Making other players see it
There are two ways to make back weapons visible to everyone, and the difference matters more than any offset.
Networked props. Each client creates its own props with isNetwork = true, so the objects exist on the server and replicate. It works, but every player now owns several networked entities, they are left behind if a client crashes, and with sv_entityLockdown set to strict the client is not allowed to create them at all.
State bag plus local props. Each client publishes the list of weapons it carries on its own player state bag. Every client reads that list for players within range and creates non-networked props locally. Nothing is created on the server, nothing leaks when someone drops, and the whole thing costs one small replicated value per player.
local last = ''
CreateThread(function()
while true do
local list = GetBackList(PlayerPedId())
local key = table.concat(list, ',')
if key ~= last then
last = key
LocalPlayer.state:set('backWeapons', key, true)
end
Wait(750)
end
end)local rendered = {} -- [serverId] = { key, ped, objs }
local function clear(id)
local r = rendered[id]
if not r then return end
for _, obj in ipairs(r.objs) do
if DoesEntityExist(obj) then DeleteEntity(obj) end
end
rendered[id] = nil
end
local function entryFor(name)
for _, entry in ipairs(Config.Weapons) do
if entry.name == name then return entry end
end
end
CreateThread(function()
while true do
local myPos = GetEntityCoords(PlayerPedId())
local seen = {}
for _, player in ipairs(GetActivePlayers()) do
local ped = GetPlayerPed(player)
local id = GetPlayerServerId(player)
local key = Player(id).state.backWeapons or ''
if IsPedInAnyVehicle(ped, false) or IsEntityDead(ped)
or #(GetEntityCoords(ped) - myPos) > Config.Range then
key = ''
end
seen[id] = true
local r = rendered[id]
if not r or r.key ~= key or r.ped ~= ped then
clear(id)
local objs = {}
for name in key:gmatch('[^,]+') do
local entry = entryFor(name)
local obj = entry and CreateBackProp(ped, entry)
if obj then objs[#objs + 1] = obj end
end
rendered[id] = { key = key, ped = ped, objs = objs }
end
end
for id in pairs(rendered) do
if not seen[id] then clear(id) end
end
Wait(500)
end
end)
AddEventHandler('onResourceStop', function(res)
if res ~= GetCurrentResourceName() then return end
for id in pairs(rendered) do clear(id) end
end)GetActivePlayers includes the local player, so the same loop draws your own back weapons. Comparing the stored ped handle catches respawns and model changes, which create a new ped and would otherwise leave the old props attached to nothing.
#Performance and edge cases
- Cap the count. Two props per player is plenty. Twenty players with four rifles each is eighty attached objects for no gameplay gain.
- Hide in vehicles. Seats clip through rifles on almost every car. The render loop above clears the props whenever the ped is in a vehicle.
- Bags and vests. A large backpack (component 5) or a bulky vest (component 9) pushes the model out of alignment. Some servers keep a second offset per weapon for players wearing a bag; check
GetPedDrawableVariation(ped, 5)and swap offsets. - Female peds.
mp_f_freemode_01has a slightly different spine. If rifles hover off female characters, add a small per-model correction. - Add-on weapons. A heavy weapon pack means heavy props. Oversized textures on weapons show up here too, because every back weapon streams the full model. Weapon pack performance covers budgets.
#Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Rifle floats at the hips or feet | Raw bone id passed as the index | Wrap it in GetPedBoneIndex(ped, 24818) |
| Prop visible only to you | Local prop and no sync | Publish a state bag and render for every player |
| Nothing appears on ox_inventory | HasPedGotWeapon used | Use exports.ox_inventory:GetItemCount |
| Add-on weapon never shows | Model not streamed or not in archetypes | Check IsModelInCdimage(model); see the streaming guides |
| Rifle stays on the back after drawing | List not refreshed | Exclude GetSelectedPedWeapon and poll |
| Props left in the world after a crash | Networked props owned by the client | Switch to the state bag approach |
Common problems
If an add-on weapon shows in the hand but not on the back, the model hash is probably fine and the offset is the problem: a model whose origin sits far from the grip can end up a metre behind the player. Set the offset to zero and work outwards.
Questions
How do I put weapons on a player's back in FiveM?
CreateObject(GetWeapontypeModel(hash), ...) and attach it with AttachEntityToEntity to the bone index returned by GetPedBoneIndex(ped, 24818), then delete it whenever that weapon is in the player's hands.Why do weapons on back not show with ox_inventory?
HasPedGotWeapon is false for everything else. Check the inventory with exports.ox_inventory:GetItemCount(name) instead.Which bone should a back weapon attach to?
SKEL_Spine3 (bone id 24818, 0x60F2) is the usual choice. Always convert it with GetPedBoneIndex before passing it to the attach native.Do other players see my back weapons?
Does this work for add-on weapons?
GetWeapontypeModel reads the model from the add-on's weapons.meta, so any add-on that streams correctly can be shown on the back with its own offset.Ready to pick a map?
Twelve themes on three base map styles, $8 each, instant download.
Relevant to what you just read
The tool for this
Twenty theme pairs for ox_lib and ox_inventory From $15, built in the browser, exported as a drop-in resource.
Look it up while you work
The reference tables this guide leans on, free and without an account.
- All 21,631 object model namesEvery model CreateObject accepts, with its hash and a flag for the LOD and proxy names that spawn invisible.
- All 921 vehicle spawn namesEvery vanilla model with a picture, its display name, its class and the hash CreateVehicle takes.
- All 860 ped model namesEvery model GetHashKey resolves, with its hash and how many clothing components and props it carries.