# PlayerData (/docs/es_extended/playerdata)

PlayerData is the information ESX keeps about the loaded character: identity, accounts, inventory, loadout, job, group and metadata. On the server it lives on the [xPlayer](/docs/es_extended/server/xplayer) object. On the client a copy is stored in `ESX.PlayerData` and kept up to date by ESX events. Read it on the client for UI and checks, but make every decision that matters (money, items, permissions) on the server.

## Overview [#overview]

| Fact | Value |
| --- | --- |
| Server | `ESX.GetPlayerFromId(source)` returns the [xPlayer](/docs/es_extended/server/xplayer) |
| Client | `ESX.PlayerData` (needs `@es_extended/imports.lua`) or `ESX.GetPlayerData()` |
| Ready flag | `ESX.PlayerLoaded` (client) |
| Sent to client | In `esx:playerLoaded`, then updated by events like `esx:setJob` and `esx:setAccountMoney` |
| Saved columns | `accounts`, `job`, `job_grade`, `group`, `position`, `inventory`, `loadout`, `metadata` in `users` |

## Getting PlayerData [#getting-playerdata]

<Tabs items={['Client', 'Server']}>
<Tab value="Client">

```lua title="fxmanifest.lua"
shared_script "@es_extended/imports.lua"
```

```lua title="client/main.lua"
CreateThread(function()
    while not ESX.PlayerLoaded do
        Wait(250)
    end

    print("Job: " .. ESX.PlayerData.job.label)
end)

RegisterNetEvent("esx:setJob", function(job, lastJob)
    print(("Job changed from %s to %s"):format(lastJob.name, job.name))
end)
```

With the imports file, `ESX.PlayerData` is refreshed automatically in your resource. Without it, call `ESX.GetPlayerData()` each time you need fresh data, because the `ESX` object you got from the export is a copy.

</Tab>
<Tab value="Server">

```lua title="server/main.lua"
local xPlayer = ESX.GetPlayerFromId(source)

if xPlayer then
    print(xPlayer.getName(), xPlayer.getJob().name, xPlayer.getAccount("bank").money)
end
```

Use the getter methods on the server. They return the same tables as the raw fields but stay correct when ESX changes internals.

</Tab>
</Tabs>

## Field reference [#field-reference]

"Client" means `ESX.PlayerData.<field>`, "Server" means the xPlayer object.

| Field | Type | Client | Server | Description |
| --- | --- | --- | --- | --- |
| `identifier` | `string` | Yes | `getIdentifier()` | Character identifier, `char1:...` with multicharacter. |
| `ssn` | `string` | Yes | `getSSN()` | Social security number `XXX-XX-XXXX`. |
| `name` | `string` | Yes | `getName()` | Character name, or the FiveM name without an identity. |
| `firstName` | `string` | Yes | `get("firstName")` | Default `"John"` until an identity exists. |
| `lastName` | `string` | Yes | `get("lastName")` | Default `"Doe"`. |
| `dateofbirth` | `string` | Yes | `get("dateofbirth")` | As stored by esx_identity, default `"01/01/2000"`. |
| `sex` | `string` | Yes | `get("sex")` | `"m"` or `"f"`. Only set when the character has an identity. |
| `height` | `number` | Yes | `get("height")` | Height in cm, default `120`. |
| `group` | `string` | Yes | `getGroup()` | Permission group. |
| `job` | `table` | Yes | `getJob()` | See [job](#job). |
| `accounts` | `table[]` | Yes | `getAccounts()` | See [accounts](#accounts). |
| `money` | `number` | Yes (snapshot) | `getMoney()` | Cash at load time. Not updated on the client afterwards. |
| `inventory` | `table[]` | Yes | `getInventory()` | See [inventory](#inventory). |
| `weight` | `number` | Yes (load time) | `getWeight()` | Carried weight. |
| `maxWeight` | `number` | Yes | `getMaxWeight()` | Weight limit. |
| `loadout` | `table[]` | Yes | `getLoadout()` | See [loadout](#loadout). |
| `metadata` | `table` | Yes | `getMeta()` | Persistent custom data. |
| `variables` | `table` | Yes | `get(key)` | Non-persistent custom data set with `xPlayer.set`. |
| `coords` | `vector3` | Yes (live) | `getCoords()` | On the client this reads the ped's current position. |
| `skin` | `table` | Yes | no | Saved skinchanger data. |
| `dead` | `boolean` | Yes | no | Updated on death and respawn. |
| `ped` | `number` | Yes | no | Current ped handle. |
| `weapon` | `number` | Yes | no | Hash of the weapon in hand. |
| `vehicle` | `number` | Yes | no | Current vehicle handle, if any. |
| `seat` | `number` | Yes | no | Current seat index, if in a vehicle. |
| `source` / `playerId` | `number` | no | Yes | Server ID. Use `GetPlayerServerId(PlayerId())` on the client. |
| `license` | `string` | no | Yes | Identifier with type prefix. |
| `admin` | `boolean` | no | Yes | Admin state at load. |

## Details [#details]

### job [#job]

```lua
{
    name = "police",
    label = "LSPD",
    type = "leo",
    onDuty = true,
    grade = 2,
    grade_name = "sergeant",
    grade_label = "Sergeant",
    grade_salary = 900,
    skin_male = {},
    skin_female = {},
}
```

`grade` and `grade_salary` are numbers. `onDuty` comes from `metadata.jobDuty`. The field list is described in [xPlayer.getJob](/docs/es_extended/server/xplayer#getjob).

### accounts [#accounts]

An array, not a map:

```lua
{
    { name = "bank", money = 50000, label = "Bank", round = true, index = 1 },
    { name = "money", money = 500, label = "Cash", round = true, index = 2 },
    { name = "black_money", money = 0, label = "Dirty Money", round = true, index = 3 },
}
```

The order follows `Config.Accounts` and is not guaranteed. Look accounts up by name:

<Tabs items={['Client', 'Server']}>
<Tab value="Client">

```lua
local bank = ESX.GetAccount("bank")
print("Bank: $" .. (bank and bank.money or 0))
```

</Tab>
<Tab value="Server">

```lua
local bank = xPlayer.getAccount("bank")
print("Bank: $" .. bank.money)
```

</Tab>
</Tabs>

### inventory [#inventory]

With the default inventory: an array of the items the player owns (count above `0`), sorted by label.

```lua
{
    { name = "bread", label = "Bread", count = 3, weight = 1, usable = true, rare = false, canRemove = true },
    { name = "water", label = "Water", count = 1, weight = 1, usable = true, rare = false, canRemove = true },
}
```

```lua title="client/main.lua"
local breadCount = ESX.SearchInventory("bread", true) -- 0 when the player has none
```

With ox_inventory, use ox_inventory's own exports to read items.

### loadout [#loadout]

```lua
{
    { name = "WEAPON_PISTOL", label = "Pistol", ammo = 48, components = { "clip_default", "flashlight" }, tintIndex = 0 },
}
```

Empty with ox_inventory.

### metadata [#metadata]

A free-form table that is saved to the database. ESX itself uses `health`, `armor`, `lastPlaytime` and `jobDuty`. Write it on the server with [`xPlayer.setMeta`](/docs/es_extended/server/xplayer#setmeta); the client copy updates automatically.

```lua
-- client
local stress = ESX.PlayerData.metadata.stress or 0
```

## Reacting to changes on the client [#reacting-to-changes-on-the-client]

| Event | Updates |
| --- | --- |
| `esx:playerLoaded` | Whole `ESX.PlayerData`. |
| `esx:setJob` | `job` |
| `esx:setAccountMoney` | One entry in `accounts` |
| `esx:addInventoryItem`, `esx:removeInventoryItem`, `esx:setInventory` | `inventory` |
| `esx:setMaxWeight` | `maxWeight` |
| `esx:setGroup` | `group` |
| `esx:updatePlayerData` | `metadata` or `variables` |
| `esx:onPlayerLogout` | Clears `ESX.PlayerData` and sets `ESX.PlayerLoaded = false` |

With `@es_extended/imports.lua` you can also define a global `OnPlayerData` function. It is called with `key, value, lastValue` whenever es_extended changes a field:

```lua title="client/main.lua"
function OnPlayerData(key, value)
    if key == "job" then
        updateJobBlips(value.name)
    end
end
```

See [client events](/docs/es_extended/events/client) for the full list.

## Common issues [#common-issues]

- **`attempt to index a nil value (field 'job')`**: you read `ESX.PlayerData` before `esx:playerLoaded`. Wait for `ESX.PlayerLoaded`.
- **Stale data in a resource without imports**: the exported `ESX` object is a snapshot. Use `ESX.GetPlayerData()` or switch to `@es_extended/imports.lua`.
- **`xPlayer.firstName` is nil**: on the server, identity fields are variables. Use `xPlayer.get("firstName")`.
- **Inventory indexed by name returns nil**: `inventory` is an array. Use `ESX.SearchInventory` or `xPlayer.getInventoryItem`.

## Related [#related]

- [xPlayer](/docs/es_extended/server/xplayer)
- [Client functions](/docs/es_extended/client/functions)
- [Client events](/docs/es_extended/events/client)
- [Shared playerdata helpers](/docs/es_extended/shared/playerdata)
