# xPlayer (/docs/es_extended/server/xplayer)

`xPlayer` is the server-side object that represents one loaded ESX character. You get it with `ESX.GetPlayerFromId(source)` and use its methods to change money, items, job, group, weapons and metadata. Every change goes through these methods so that es_extended can sync the client, fire events and save the data to the database.

## Overview [#overview]

| Fact | Value |
| --- | --- |
| Side | Server only |
| Defined in | `es_extended/server/classes/player.lua` and `server/classes/player/*.lua` |
| Created by | es_extended when a character loads (`CreateExtendedPlayer`) |
| Stored in | `ESX.Players[source]`, also indexed by identifier |
| Saved | Every 10 minutes, on `/save` and `/saveall`, and when the player drops |
| Version | ESX Legacy 1.16.4 |

<Callout type="info" title="ox_inventory changes some methods">
When the `ox_inventory` resource exists, es_extended sets `Config.CustomInventory = "ox"` and replaces the inventory, weapon and some account methods with versions that forward to ox_inventory. Each affected method below has an "ox_inventory" note. See the [ox_inventory guide](/docs/guides/ox-inventory).
</Callout>

## Getting an xPlayer [#getting-an-xplayer]

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

if not xPlayer then
    return -- player is not loaded (still connecting, in character selection, or gone)
end

print(xPlayer.getName(), xPlayer.getJob().name)
```

Other ways to get a player object:

| Function | Returns |
| --- | --- |
| [`ESX.GetPlayerFromId(source)`](/docs/es_extended/server/functions#getplayerfromid) | `xPlayer` or `nil` |
| [`ESX.GetPlayerFromIdentifier(identifier)`](/docs/es_extended/server/functions#getplayerfromidentifier) | `xPlayer` or `nil` |
| [`ESX.GetExtendedPlayers(key, value)`](/docs/es_extended/server/functions#getextendedplayers) | array of `xPlayer`, optionally filtered |
| [`ESX.Player(source)`](/docs/es_extended/server/functions#player) | a static proxy (see below) |

### Static players (`ESX.Player`) [#static-players-esxplayer]

`ESX.Player(src)` and `ESX.ExtendedPlayers()` exist only in resources that load `@es_extended/imports.lua`. They return a lightweight proxy instead of the real object. Every key you index on the proxy becomes a function that calls the method through the `RunStaticPlayerMethod` export:

```lua
local player = ESX.Player(source)

if player then
    player.addAccountMoney("bank", 500, "Quest reward")
    local job = player.getJob() -- returns a copy, not a live reference
end
```

<Callout type="warn" title="Only methods work on static players">
`player.job`, `player.name` and other fields do not exist on the proxy. Indexing any key returns a function. Use the getters (`getJob()`, `getName()`) instead. Tables returned through the export are copies, so changing them has no effect on the player.
</Callout>

## Properties [#properties]

The object also exposes raw fields. Read them if you like, but change state only through methods, otherwise the client is not updated and nothing is saved.

| Field | Type | Description |
| --- | --- | --- |
| `source` / `playerId` | `number` | Server ID of the player. |
| `identifier` | `string` | Database identifier. With multicharacter it looks like `char1:abc123...`. |
| `license` | `string` | Full identifier with prefix, for example `license:abc123...`. Used for ACE principals. |
| `ssn` | `string` | Social security number in `XXX-XX-XXXX` format, generated on character creation. |
| `name` | `string` | Character name (`"First Last"`) or the FiveM name when no identity exists. |
| `group` | `string` | Permission group, for example `user` or `admin`. |
| `accounts` | `table[]` | Array of accounts: `{ name, money, label, round, index }`. |
| `inventory` | `table[]` | Items with a count above 0, sorted by label (default inventory). |
| `loadout` | `table[]` | Weapons: `{ name, label, ammo, components, tintIndex }`. Empty with ox_inventory. |
| `job` | `table` | Current job, see [getJob](#getjob). |
| `weight` / `maxWeight` | `number` | Carried weight and limit. `maxWeight` starts at `Config.MaxWeight`. |
| `coords` | `table` | Last known `{ x, y, z, heading }`. |
| `metadata` | `table` | Persistent custom data, see [Metadata](#metadata). |
| `variables` | `table` | Non-persistent custom data set with [`set`](#set). |
| `lastPlaytime` | `number` | Saved playtime in seconds when the character loaded. |
| `paycheckEnabled` | `boolean` | Whether the player receives paychecks. |
| `admin` | `boolean` | Admin state when the object was created. Use [`isAdmin()`](#isadmin) for the live value. |
| `spawned` | `boolean` | Set to `true` once the client reports its first spawn. Players are not saved before that. |

Character identity data (`firstName`, `lastName`, `dateofbirth`, `sex`, `height`) is **not** a field on the server object. It is stored as variables, so read it with `xPlayer.get("firstName")`. See [PlayerData](/docs/es_extended/playerdata).

## Identity and session [#identity-and-session]

### getIdentifier [#getidentifier]

```lua
xPlayer.getIdentifier()
```

Returns the database identifier of the character (the `identifier` column in `users`).

**Returns:** `string`

```lua
local identifier = xPlayer.getIdentifier()
MySQL.update("UPDATE users SET phone_number = ? WHERE identifier = ?", { number, identifier })
```

### getSSN [#getssn]

```lua
xPlayer.getSSN()
```

Returns the character's social security number, for example `"482-17-3390"`. ESX generates a unique SSN when the character is created.

**Returns:** `string`

### getSource [#getsource]

```lua
xPlayer.getSource()
```

Returns the player's server ID. `xPlayer.getPlayerId()` is an alias.

**Returns:** `number`

### getPlayerId [#getplayerid]

```lua
xPlayer.getPlayerId()
```

Alias of [getSource](#getsource).

**Returns:** `number`

### getName [#getname]

```lua
xPlayer.getName()
```

Returns the character name. With esx_identity or multicharacter this is `"Firstname Lastname"`, otherwise the FiveM player name.

**Returns:** `string`

### setName [#setname]

```lua
xPlayer.setName(newName)
```

| Name | Type | Description |
| --- | --- | --- |
| `newName` | `string` | The new display name. |

Changes the name on the object and in the `name` state bag. It does **not** update `firstname` / `lastname` in the database, so the change is lost on reconnect.

```lua
xPlayer.setName("John Doe")
```

### getPlayTime [#getplaytime]

```lua
xPlayer.getPlayTime()
```

Returns the total playtime of the character in seconds: the saved playtime plus the time online in this session. The value is persisted in `metadata.lastPlaytime`.

**Returns:** `number`

```lua
local hours = math.floor(xPlayer.getPlayTime() / 3600)
xPlayer.showNotification(("You have played %d hours"):format(hours))
```

### isCurrent [#iscurrent]

```lua
xPlayer.isCurrent()
```

Returns `true` while this object is still the active entry in `ESX.Players`. Use it after a `Wait` or an awaited database query to make sure the player has not dropped or switched characters in the meantime.

**Returns:** `boolean`

```lua
local result = MySQL.single.await("SELECT * FROM my_table WHERE owner = ?", { xPlayer.identifier })

if not xPlayer.isCurrent() then
    return -- player left while we were waiting
end
```

### isAdmin [#isadmin]

```lua
xPlayer.isAdmin()
```

Returns `true` if the player is allowed the ACE `command`, if `sv_lan` is `"true"`, or if the player's group is listed in `Config.AdminGroups`.

**Returns:** `boolean`

<Callout type="warn">
`Config.AdminGroups` is deprecated in favor of `Config.CommandPermissions`. For your own permission checks, prefer ACE (`IsPlayerAceAllowed`) or [`getGroup`](#getgroup). See [admin permissions](/docs/guides/admin-permissions).
</Callout>

### kick [#kick]

```lua
xPlayer.kick(reason)
```

| Name | Type | Description |
| --- | --- | --- |
| `reason` | `string` | Message shown to the player. |

Drops the player from the server. ESX saves the character in the `playerDropped` handler.

```lua
xPlayer.kick("You have been kicked for exploiting.")
```

### triggerEvent [#triggerevent]

```lua
xPlayer.triggerEvent(eventName, ...)
```

| Name | Type | Description |
| --- | --- | --- |
| `eventName` | `string` | Client event name. Must be a string, otherwise the call asserts. |
| `...` | `any` | Arguments passed to the client handler. |

Shortcut for `TriggerClientEvent(eventName, xPlayer.source, ...)`.

```lua
xPlayer.triggerEvent("myjob:client:startShift", { route = 3 })
```

### executeCommand [#executecommand]

```lua
xPlayer.executeCommand(command)
```

| Name | Type | Description |
| --- | --- | --- |
| `command` | `string` | The command line to run, without the leading `/`. |

Makes the player's client run `ExecuteCommand(command)`. The command runs with the player's own permissions. Throws an error if `command` is not a string.

```lua
xPlayer.executeCommand("e sit")
```

## Position [#position]

### getCoords [#getcoords]

```lua
xPlayer.getCoords(vector, heading)
```

| Name | Type | Description |
| --- | --- | --- |
| `vector` | `boolean?` | `true` returns a `vector3` (or `vector4` with `heading`). Otherwise a plain table. |
| `heading` | `boolean?` | Include the heading (`w` in a vector4, `heading` in a table). |

Reads the live ped position when the ped exists, otherwise the last cached position. Falls back to a random `Config.DefaultSpawns` entry if nothing valid is known.

**Returns:** `vector3`, `vector4` or `{ x, y, z, heading? }`

```lua
local coords = xPlayer.getCoords(true)

if #(coords - vector3(441.8, -982.0, 30.7)) < 10.0 then
    print("Player is at Mission Row PD")
end
```

### setCoords [#setcoords]

```lua
xPlayer.setCoords(coordinates)
```

| Name | Type | Description |
| --- | --- | --- |
| `coordinates` | `vector3`, `vector4` or `table` | Target position. Tables need `x`, `y`, `z` and may have `heading` or `w`. |

Teleports the ped on the server side. Returns `false` if the coordinates are invalid (non-finite or beyond `Config.PlayerCoordinateLimit`) or the ped does not exist. Returns nothing on success.

```lua
xPlayer.setCoords(vector4(-269.4, -955.3, 31.2, 205.0))
```

## Permission group [#permission-group]

### getGroup [#getgroup]

```lua
xPlayer.getGroup()
```

Returns the player's permission group, for example `"user"`, `"admin"` or `"owner"`.

**Returns:** `string`

```lua
if xPlayer.getGroup() ~= "user" then
    print(xPlayer.getName() .. " is staff")
end
```

### setGroup [#setgroup]

```lua
xPlayer.setGroup(newGroup)
```

| Name | Type | Description |
| --- | --- | --- |
| `newGroup` | `string` | The new group name. |

Replaces the ACE principal `identifier.<license>` → `group.<old>` with `group.<new>`, updates the `group` state bag and fires `esx:setGroup` on the server (`source, group, lastGroup`) and on the client (`group, lastGroup`). The group is saved to the `users` table.

```lua
xPlayer.setGroup("admin")
```

## Variables [#variables]

Variables are temporary key/value pairs on the player. They are not saved to the database. ESX itself uses them for identity data (`firstName`, `lastName`, `dateofbirth`, `sex`, `height`).

### set [#set]

```lua
xPlayer.set(key, value, replicate)
```

| Name | Type | Description |
| --- | --- | --- |
| `key` | `string` | Variable name. |
| `value` | `any` | Value to store. |
| `replicate` | `boolean?` | Defaults to `true`. When not `false`, the whole `variables` table is sent to the client as `ESX.PlayerData.variables`. |

```lua
xPlayer.set("lastRobbery", os.time(), false) -- server-only flag
```

### get [#get]

```lua
xPlayer.get(key)
```

| Name | Type | Description |
| --- | --- | --- |
| `key` | `string` | Variable name. |

**Returns:** `any` (the stored value or `nil`)

```lua
local first, last = xPlayer.get("firstName"), xPlayer.get("lastName")
```

### updatePlayerData [#updateplayerdata]

<Callout type="error" title="Removed">
`xPlayer.updatePlayerData` no longer exists in ESX Legacy 1.16.4. `set`, `setMeta` and `clearMeta` send `esx:updatePlayerData` to the client themselves. To push other data, use `xPlayer.triggerEvent("esx:updatePlayerData", key, value)`.
</Callout>

## Money and accounts [#money-and-accounts]

Every character has the accounts defined in [`Config.Accounts`](/docs/es_extended/config#accounts). By default these are `money` (cash), `bank` and `black_money`. Account names are case-insensitive in `getAccount`. See the [money and accounts guide](/docs/guides/money-and-accounts).

<Callout type="warn" title="Invalid amounts throw errors">
With the default inventory, `setAccountMoney`, `addAccountMoney` and `removeAccountMoney` call Lua `error()` for unknown accounts and for amounts that are not valid numbers. `add` and `remove` also throw for amounts of `0` or below. Always validate user input (for example from NUI or events) before you pass it on. The ox_inventory versions return `false` instead.
</Callout>

### getAccounts [#getaccounts]

```lua
xPlayer.getAccounts(minimal)
```

| Name | Type | Description |
| --- | --- | --- |
| `minimal` | `boolean?` | `true` returns `{ [accountName] = money }` instead of the full array. |

**Returns:** `table[]` of `{ name, money, label, round, index }`, or a name → money map.

```lua
local balances = xPlayer.getAccounts(true)
print(balances.bank, balances.money, balances.black_money)
```

### getAccount [#getaccount]

```lua
xPlayer.getAccount(account)
```

| Name | Type | Description |
| --- | --- | --- |
| `account` | `string` | Account name, for example `"bank"`. |

**Returns:** `{ name, money, label, round, index }` or `nil` if the account does not exist.

```lua
local bank = xPlayer.getAccount("bank")

if bank and bank.money >= 2500 then
    -- player can afford it
end
```

### getMoney [#getmoney]

```lua
xPlayer.getMoney()
```

Returns the balance of the `money` (cash) account, or `0`.

**Returns:** `number`

### setMoney [#setmoney]

```lua
xPlayer.setMoney(money)
```

| Name | Type | Description |
| --- | --- | --- |
| `money` | `number` | New cash balance. Asserts that it is a number, then rounds it. |

Shortcut for `setAccountMoney("money", money)`.

**Returns:** `true` on success.

### addMoney [#addmoney]

```lua
xPlayer.addMoney(money, reason)
```

| Name | Type | Description |
| --- | --- | --- |
| `money` | `number` | Amount of cash to add. Rounded before use. |
| `reason` | `string?` | Reason passed to the `esx:addAccountMoney` event, useful for logs. |

Shortcut for `addAccountMoney("money", money, reason)`.

**Returns:** `true` on success, `false` on overflow.

```lua
xPlayer.addMoney(150, "Sold fish")
```

### removeMoney [#removemoney]

```lua
xPlayer.removeMoney(money, reason)
```

| Name | Type | Description |
| --- | --- | --- |
| `money` | `number` | Amount of cash to remove. Rounded before use. |
| `reason` | `string?` | Reason passed to the `esx:removeAccountMoney` event. |

Shortcut for `removeAccountMoney("money", money, reason)`.

**Returns:** `true` on success, `false` if the player does not have enough cash.

### setAccountMoney [#setaccountmoney]

```lua
xPlayer.setAccountMoney(accountName, money, reason)
```

| Name | Type | Description |
| --- | --- | --- |
| `accountName` | `string` | Account name. |
| `money` | `number` | New balance, `0` or higher. Rounded if the account has `round = true`. |
| `reason` | `string?` | Defaults to `"unknown"`. |

Sets the balance to an exact value. Fires `esx:setAccountMoney` on the server (`source, accountName, money, reason`) and on the client (the updated account table).

**Returns:** `true` on success. Throws for negative or invalid amounts and unknown accounts.

```lua
xPlayer.setAccountMoney("black_money", 0, "Money laundered")
```

### addAccountMoney [#addaccountmoney]

```lua
xPlayer.addAccountMoney(accountName, money, reason)
```

| Name | Type | Description |
| --- | --- | --- |
| `accountName` | `string` | Account name. |
| `money` | `number` | Amount to add, greater than `0`. |
| `reason` | `string?` | Defaults to `"Unknown"`. |

Fires `esx:addAccountMoney` on the server (`source, accountName, money, reason`) and syncs the account to the client.

**Returns:** `true` on success, `false` if the new balance would overflow. Throws for amounts of `0` or below.

```lua
local reward = 1200

if reward > 0 then
    xPlayer.addAccountMoney("bank", reward, "Delivery job")
end
```

### removeAccountMoney [#removeaccountmoney]

```lua
xPlayer.removeAccountMoney(accountName, money, reason)
```

| Name | Type | Description |
| --- | --- | --- |
| `accountName` | `string` | Account name. |
| `money` | `number` | Amount to remove, greater than `0`. Converted with `tonumber`. |
| `reason` | `string?` | Defaults to `"Unknown"`. |

Balances never go negative. Fires `esx:removeAccountMoney` on the server.

**Returns:** `true` on success, `false` if the balance is too low or rounding turned the amount into `0`. Throws for amounts of `0` or below.

```lua
local price = 500

if xPlayer.removeAccountMoney("bank", price, "Bought a phone") then
    xPlayer.addInventoryItem("phone", 1)
else
    xPlayer.showNotification("Not enough money in your bank account", "error")
end
```

<Callout type="info" title="ox_inventory">
Accounts listed in the `inventory:accounts` convar (default `["money", "black_money"]`, `black_money` is always included) are stored as ox_inventory items. For those, the account methods call `exports.ox_inventory` `SetItem` / `AddItem` / `RemoveItem`. All three return `false` instead of throwing for invalid input. Amounts below `1` are rejected.
</Callout>

## Paycheck [#paycheck]

### togglePaycheck [#togglepaycheck]

```lua
xPlayer.togglePaycheck(toggle)
```

| Name | Type | Description |
| --- | --- | --- |
| `toggle` | `boolean` | `false` stops paychecks for this player until set back to `true`. |

Not saved. Every new session starts with paychecks enabled.

```lua
xPlayer.togglePaycheck(false) -- e.g. while the player is in jail
```

### isPaycheckEnabled [#ispaycheckenabled]

```lua
xPlayer.isPaycheckEnabled()
```

**Returns:** `boolean`

## Job [#job]

### getJob [#getjob]

```lua
xPlayer.getJob()
```

Returns the live job table.

| Field | Type | Description |
| --- | --- | --- |
| `id` | `number?` | Copied from an `id` column in `jobs`. The default schema has none, so this is usually `nil`. |
| `name` | `string` | Job name, for example `"police"`. |
| `label` | `string` | Display label. |
| `type` | `string` | Job type from the `jobs.type` column (default `"civ"`). |
| `onDuty` | `boolean` | Duty state. |
| `grade` | `number` | Grade number. |
| `grade_name` | `string` | Grade name, for example `"boss"`. |
| `grade_label` | `string` | Grade label. |
| `grade_salary` | `number` | Salary per paycheck for this grade. |
| `skin_male` | `table` | Male outfit for the grade. |
| `skin_female` | `table` | Female outfit for the grade. |

```lua
local job = xPlayer.getJob()

if job.name == "police" and job.onDuty then
    -- allow cuffing
end
```

### setJob [#setjob]

```lua
xPlayer.setJob(newJob, grade, onDuty)
```

| Name | Type | Description |
| --- | --- | --- |
| `newJob` | `string` | Job name. Must exist in `ESX.Jobs`. |
| `grade` | `number` or `string` | Grade number. Converted with `tostring`. |
| `onDuty` | `boolean?` | Duty state. Defaults to `Config.DefaultJobDuty`. Always `false` for `unemployed`. |

If the job or grade does not exist, ESX prints a warning and does nothing. On success it stores the duty state in `metadata.jobDuty`, updates the `job` state bag and fires `esx:setJob` on the server (`source, job, lastJob`) and the client (`job, lastJob`).

```lua
if ESX.DoesJobExist("mechanic", 0) then
    xPlayer.setJob("mechanic", 0)
end

-- Toggle duty without changing the job
local job = xPlayer.getJob()
xPlayer.setJob(job.name, job.grade, not job.onDuty)
```

### refreshJob [#refreshjob]

```lua
xPlayer.refreshJob()
```

Reloads label, grade name, salary and outfits from `ESX.Jobs` while keeping the grade and duty state. Fires `esx:jobDataRefreshed`. [`ESX.RefreshJob(jobName)`](/docs/es_extended/server/functions#refreshjob) calls this for every player in that job, so you rarely need it directly.

**Returns:** `true`, or `false` if the grade no longer exists. In that case the player is set to `unemployed` grade `0`.

## Inventory [#inventory]

These methods describe the default es_extended inventory. Items must exist in the `items` table (or be added with [`ESX.AddItems`](/docs/es_extended/server/functions#additems)). See [adding items](/docs/guides/add-items).

### getInventory [#getinventory]

```lua
xPlayer.getInventory(minimal)
```

| Name | Type | Description |
| --- | --- | --- |
| `minimal` | `boolean?` | `true` returns `{ [itemName] = count }`. |

**Returns:** an array of items the player owns (count above `0`), sorted by label. Each item is `{ name, count, label, weight, usable, rare, canRemove }`.

```lua
for _, item in ipairs(xPlayer.getInventory()) do
    print(item.label, item.count)
end
```

<Callout type="info" title="ox_inventory">
`getInventory()` returns ox_inventory's slot table. `getInventory(true)` returns an array of `{ name, count, slot, metadata }`.
</Callout>

### getInventoryItem [#getinventoryitem]

```lua
xPlayer.getInventoryItem(itemName)
```

| Name | Type | Description |
| --- | --- | --- |
| `itemName` | `string` | Item name. |

**Returns:** the item table. For known items the player does not own, a new entry with `count = 0`. `nil` for items that do not exist.

```lua
local bread = xPlayer.getInventoryItem("bread")

if bread and bread.count >= 2 then
    -- player has at least 2 bread
end
```

<Callout type="info" title="ox_inventory">
Signature becomes `getInventoryItem(name, metadata)` and returns `exports.ox_inventory:GetItem(source, name, metadata)`.
</Callout>

### hasItem [#hasitem]

```lua
xPlayer.hasItem(itemName)
```

| Name | Type | Description |
| --- | --- | --- |
| `itemName` | `string` | Item name. |

**Returns:** `item, count` if the player has at least one, otherwise `false`.

```lua
local item, count = xPlayer.hasItem("lockpick")

if item then
    print(("Player has %d lockpicks"):format(count))
end
```

With ox_inventory the signature is `hasItem(name, metadata)`.

### addInventoryItem [#addinventoryitem]

```lua
xPlayer.addInventoryItem(itemName, count)
```

| Name | Type | Description |
| --- | --- | --- |
| `itemName` | `string` | Item name. |
| `count` | `number` | Amount to add. Rounded; values of `0` or below are ignored. |

Fires `esx:onAddInventoryItem` on the server (`source, itemName, newCount`) and `esx:addInventoryItem` on the client.

**Returns:** `true` on success, `false` for invalid counts or unknown items.

```lua
if xPlayer.canCarryItem("water", 3) then
    xPlayer.addInventoryItem("water", 3)
else
    xPlayer.showNotification("You cannot carry that much", "error")
end
```

<Callout type="warn">
The default inventory does not check weight here. Call [`canCarryItem`](#cancarryitem) first.
</Callout>

With ox_inventory the signature is `addInventoryItem(name, count, metadata, slot)`; `count` defaults to `1`.

### removeInventoryItem [#removeinventoryitem]

```lua
xPlayer.removeInventoryItem(itemName, count)
```

| Name | Type | Description |
| --- | --- | --- |
| `itemName` | `string` | Item name. |
| `count` | `number` | Amount to remove. Rounded. Must be greater than `0`. |

Fires `esx:onRemoveInventoryItem` on the server (`source, itemName, newCount`).

**Returns:** `true` on success, `false` if the player does not have the item or not enough of it. Throws for counts of `0` or below.

```lua
if xPlayer.removeInventoryItem("lockpick", 1) then
    TriggerClientEvent("myscript:client:pickLock", xPlayer.source)
end
```

With ox_inventory the signature is `removeInventoryItem(name, count, metadata, slot)`.

### setInventoryItem [#setinventoryitem]

```lua
xPlayer.setInventoryItem(itemName, count)
```

| Name | Type | Description |
| --- | --- | --- |
| `itemName` | `string` | Item name. |
| `count` | `number` | The exact count the player should end up with, `0` or higher. |

Adds or removes the difference using the methods above.

**Returns:** the result of `addInventoryItem` / `removeInventoryItem`, `nil` if the count is already correct, `false` for unknown items or negative counts.

```lua
xPlayer.setInventoryItem("bandage", 0) -- remove all bandages
```

With ox_inventory the signature is `setInventoryItem(name, count, metadata)`.

### canCarryItem [#cancarryitem]

```lua
xPlayer.canCarryItem(itemName, count)
```

| Name | Type | Description |
| --- | --- | --- |
| `itemName` | `string` | Item name. |
| `count` | `number` | Amount to test. |

**Returns:** `true` if `weight + itemWeight * count` stays within `maxWeight`. `false` for unknown items.

With ox_inventory the signature is `canCarryItem(name, count, metadata)`.

### canSwapItem [#canswapitem]

```lua
xPlayer.canSwapItem(firstItem, firstItemCount, testItem, testItemCount)
```

| Name | Type | Description |
| --- | --- | --- |
| `firstItem` | `string` | Item the player gives away. |
| `firstItemCount` | `number` | Amount given away. |
| `testItem` | `string` | Item the player receives. |
| `testItemCount` | `number` | Amount received. |

**Returns:** `true` if the player owns enough of `firstItem` and the weight after the swap fits.

```lua
if xPlayer.canSwapItem("fish", 10, "packaged_fish", 5) then
    xPlayer.removeInventoryItem("fish", 10)
    xPlayer.addInventoryItem("packaged_fish", 5)
end
```

### getWeight [#getweight]

```lua
xPlayer.getWeight()
```

**Returns:** `number`, the current inventory weight.

### getMaxWeight [#getmaxweight]

```lua
xPlayer.getMaxWeight()
```

**Returns:** `number`, the weight limit (starts at `Config.MaxWeight`).

### setMaxWeight [#setmaxweight]

```lua
xPlayer.setMaxWeight(newWeight)
```

| Name | Type | Description |
| --- | --- | --- |
| `newWeight` | `number` | New limit. Must be a finite number, `0` or higher. |

Not saved; resets to `Config.MaxWeight` on the next load. Returns `false` for invalid values. With ox_inventory, the value is also forwarded to `SetMaxWeight`.

```lua
-- backpack equipped
xPlayer.setMaxWeight(xPlayer.getMaxWeight() + 20)
```

### syncInventory [#syncinventory]

```lua
xPlayer.syncInventory(weight, maxWeight, items, money)
```

Only exists with ox_inventory. ox_inventory calls it to copy its weight, items and account balances into the xPlayer. Do not call it yourself.

## Weapons [#weapons]

Weapon methods only work with the default inventory. With ox_inventory, weapons are items: these methods do nothing, `getLoadout()` returns `{}`, and `hasWeapon` / `hasWeaponComponent` return `false`.

Weapon names are the upper-case names from [`Config.Weapons`](/docs/es_extended/config/weapons), for example `"WEAPON_PISTOL"`. Component names are the short ESX names from that config, for example `"clip_extended"` or `"suppressor"`.

### getLoadout [#getloadout]

```lua
xPlayer.getLoadout(minimal)
```

| Name | Type | Description |
| --- | --- | --- |
| `minimal` | `boolean?` | `true` returns `{ [weaponName] = { ammo, tintIndex?, components? } }`. Default clips are left out. |

**Returns:** array of `{ name, label, ammo, components, tintIndex }` or the minimal map.

```lua
for _, weapon in ipairs(xPlayer.getLoadout()) do
    print(weapon.label, weapon.ammo)
end
```

### getWeapon [#getweapon]

```lua
xPlayer.getWeapon(weaponName)
```

| Name | Type | Description |
| --- | --- | --- |
| `weaponName` | `string` | Weapon name. |

**Returns:** `loadoutIndex, weapon` (the live loadout entry) or `nil, nil`.

```lua
local _, weapon = xPlayer.getWeapon("WEAPON_PISTOL")

if weapon then
    print("Pistol ammo: " .. weapon.ammo)
end
```

### hasWeapon [#hasweapon]

```lua
xPlayer.hasWeapon(weaponName)
```

**Returns:** `boolean`

### addWeapon [#addweapon]

```lua
xPlayer.addWeapon(weaponName, ammo)
```

| Name | Type | Description |
| --- | --- | --- |
| `weaponName` | `string` | Weapon name. Must exist in `Config.Weapons`, otherwise the label lookup asserts. |
| `ammo` | `number` | Starting ammo. |

Gives the weapon to the ped and shows an inventory notification.

**Returns:** `true`, or `false` if the player already has the weapon.

```lua
if not xPlayer.hasWeapon("WEAPON_STUNGUN") then
    xPlayer.addWeapon("WEAPON_STUNGUN", 0)
end
```

### removeWeapon [#removeweapon]

```lua
xPlayer.removeWeapon(weaponName)
```

Removes the weapon, its components and its ammo from the ped.

**Returns:** `true`, or `false` if the player did not have it.

### addWeaponAmmo [#addweaponammo]

```lua
xPlayer.addWeaponAmmo(weaponName, ammoCount)
```

| Name | Type | Description |
| --- | --- | --- |
| `weaponName` | `string` | Weapon name. |
| `ammoCount` | `number` | Ammo to add. |

**Returns:** `true`, or `false` if the player does not have the weapon.

### removeWeaponAmmo [#removeweaponammo]

```lua
xPlayer.removeWeaponAmmo(weaponName, ammoCount)
```

Subtracts ammo. There is no lower bound, so check the current ammo first if it matters.

**Returns:** `true`, or `false` if the player does not have the weapon.

### updateWeaponAmmo [#updateweaponammo]

```lua
xPlayer.updateWeaponAmmo(weaponName, ammoCount)
```

Sets the stored ammo to an exact value without touching the ped (ESX uses it to sync ammo reported by the client). Throwable weapons (`throwable = true` in the config) are removed when the ammo reaches `0`.

**Returns:** `true`, or `false` if the player does not have the weapon.

### addWeaponComponent [#addweaponcomponent]

```lua
xPlayer.addWeaponComponent(weaponName, weaponComponent)
```

| Name | Type | Description |
| --- | --- | --- |
| `weaponName` | `string` | Weapon name. |
| `weaponComponent` | `string` | Component name from `Config.Weapons`, for example `"suppressor"`. |

**Returns:** `true`, or `false` if the player lacks the weapon, the component does not exist for it, or it is already attached.

```lua
xPlayer.addWeaponComponent("WEAPON_PISTOL", "flashlight")
```

### removeWeaponComponent [#removeweaponcomponent]

```lua
xPlayer.removeWeaponComponent(weaponName, weaponComponent)
```

**Returns:** `true`, or `false` if nothing was removed.

### hasWeaponComponent [#hasweaponcomponent]

```lua
xPlayer.hasWeaponComponent(weaponName, weaponComponent)
```

**Returns:** `boolean`

### setWeaponTint [#setweapontint]

```lua
xPlayer.setWeaponTint(weaponName, weaponTintIndex)
```

| Name | Type | Description |
| --- | --- | --- |
| `weaponName` | `string` | Weapon name. |
| `weaponTintIndex` | `number` | Tint index. Must exist in the weapon's `tints` table. |

**Returns:** `true`, or `false` if the player lacks the weapon or the tint is invalid.

```lua
xPlayer.setWeaponTint("WEAPON_PISTOL", 2) -- gold
```

### getWeaponTint [#getweapontint]

```lua
xPlayer.getWeaponTint(weaponName)
```

**Returns:** `number`, the tint index, or `0` if the player does not have the weapon.

## Metadata [#metadata]

Metadata is a persistent table saved in the `metadata` column of `users`. Changes are synced to `ESX.PlayerData.metadata` on the client.

<Callout type="info" title="Keys used by ESX">
ESX writes `health`, `armor`, `lastPlaytime` and `jobDuty` into metadata. Do not overwrite them with unrelated data.
</Callout>

### getMeta [#getmeta]

```lua
xPlayer.getMeta(index, subIndex)
```

| Name | Type | Description |
| --- | --- | --- |
| `index` | `string?` | Top-level key. Omit it to get the whole metadata table. |
| `subIndex` | `string` or `string[]` | Key (or list of keys) inside a table value. |

**Returns:** the value, a `{ [key] = value }` table when `subIndex` is a list, or `nil` if the key does not exist (an error when `Config.EnableDebug` is on).

```lua
local stress = xPlayer.getMeta("stress") or 0
local licenses = xPlayer.getMeta("licenses", { "driver", "weapon" })
```

### setMeta [#setmeta]

```lua
xPlayer.setMeta(index, value, subValue)
```

| Name | Type | Description |
| --- | --- | --- |
| `index` | `string` | Top-level key. |
| `value` | `number`, `string` or `table` | Value to store. When `subValue` is given, this is the key inside the table instead. |
| `subValue` | `any` | Optional. Stores `metadata[index][value] = subValue`, creating the table if needed. |

```lua
xPlayer.setMeta("stress", 35)
xPlayer.setMeta("licenses", "driver", true) -- metadata.licenses.driver = true
```

<Callout type="warn">
Booleans are rejected as a top-level value, and `false` as `subValue` is treated as "no subValue". To store a false flag, use `clearMeta` or a number.
</Callout>

### clearMeta [#clearmeta]

```lua
xPlayer.clearMeta(index, subValues)
```

| Name | Type | Description |
| --- | --- | --- |
| `index` | `string` | Top-level key. |
| `subValues` | `string` or `string[]` | Optional. Only remove these keys inside the table. |

```lua
xPlayer.clearMeta("stress")
xPlayer.clearMeta("licenses", { "weapon", "pilot" })
```

## Notifications [#notifications]

These methods trigger client events, so the notification shows on the player's screen.

### showNotification [#shownotification]

```lua
xPlayer.showNotification(msg, notifyType, length, title, position)
```

| Name | Type | Description |
| --- | --- | --- |
| `msg` | `string` | Message text. |
| `notifyType` | `string?` | `"info"` (default), `"success"`, `"error"` or `"warning"` with esx_notify. |
| `length` | `number?` | Duration in ms. Default `5000`. |
| `title` | `string?` | Optional title. |
| `position` | `string?` | esx_notify position, for example `"top-right"`. |

```lua
xPlayer.showNotification("Vehicle stored", "success", 3000)
```

### showAdvancedNotification [#showadvancednotification]

```lua
xPlayer.showAdvancedNotification(sender, subject, msg, textureDict, iconType, flash, saveToBrief, hudColorIndex)
```

| Name | Type | Description |
| --- | --- | --- |
| `sender` | `string` | Title line. |
| `subject` | `string` | Subtitle line. |
| `msg` | `string` | Message text. |
| `textureDict` | `string` | Picture, for example `"CHAR_BANK_MAZE"`. |
| `iconType` | `number` | GTA feed icon type. |
| `flash` | `boolean` | Flash the notification. |
| `saveToBrief` | `boolean?` | Keep it in the pause-menu brief. Default `true`. |
| `hudColorIndex` | `number?` | Background color index. |

```lua
xPlayer.showAdvancedNotification("Maze Bank", "Transfer", "You received $500", "CHAR_BANK_MAZE", 9, false)
```

### showHelpNotification [#showhelpnotification]

```lua
xPlayer.showHelpNotification(msg, thisFrame, beep, duration)
```

| Name | Type | Description |
| --- | --- | --- |
| `msg` | `string` | Help text. Supports input tokens like `~INPUT_CONTEXT~`. |
| `thisFrame` | `boolean?` | Show for one frame only (not useful from the server). |
| `beep` | `boolean?` | Play a sound. Default `true`. |
| `duration` | `number?` | Duration in ms. Default `-1`. |

## Internal methods [#internal-methods]

`beginAccountOperation`, `endAccountOperation`, `beginAccountShutdown` and `getPendingAccountOperations` exist on every xPlayer. es_extended uses them to finish pending money operations before the final save when a player drops. They are not meant for resource code.

## Common issues [#common-issues]

- **`attempt to index a nil value (local 'xPlayer')`**: the player is not loaded yet or has left. Always check the result of `ESX.GetPlayerFromId`.
- **Money "disappears" after a restart**: you changed `xPlayer.accounts[i].money` directly. Use the account methods.
- **Items over the weight limit**: `addInventoryItem` does not check weight with the default inventory. Use `canCarryItem`.
- **Weapon methods do nothing**: you run ox_inventory. Give weapons as items with `exports.ox_inventory:AddItem`.
- **Trusting client input**: never pass amounts from a client event straight into `addAccountMoney`. Validate type, range and the player's distance on the server.

## Related [#related]

- [Server functions](/docs/es_extended/server/functions)
- [PlayerData](/docs/es_extended/playerdata)
- [Server events](/docs/es_extended/events/server)
- [Money and accounts guide](/docs/guides/money-and-accounts)
- [Create a job](/docs/guides/create-a-job)
