# esx_service (/docs/addons/esx_service)

`esx_service` tracks which players of a job are on duty. Job resources such as [esx_taxijob](/docs/addons/esx_taxijob) and [esx_mechanicjob](/docs/addons/esx_mechanicjob) use it to cap the number of players in service, show how many colleagues are active and send a notification to everyone on duty.

## Overview [#overview]

| Fact | Value |
| --- | --- |
| Resource | `esx_service` |
| Side | Server (state, callbacks) and client (notification) |
| Dependencies | `esx_lib`, `es_extended` |
| SQL | none |
| Config | none |
| State | In memory, reset on restart. Counts are mirrored to `GlobalState[serviceName]`. |

## Installation [#installation]

```bash title="server.cfg"
ensure esx_service
# job resources that use it
ensure esx_taxijob
```

A service name is the job name, for example `taxi`. A player can only enter the service of their own job.

## Server API [#server-api]

All server events are local and protected: `GetInvokingResource()` must be set, so calls coming straight from a client are blocked and logged as `Blocked external call`.

### esx_service:activateService [#esx_serviceactivateservice]

Creates a service and sets its player limit. Call it once on resource start.

```lua
TriggerEvent('esx_service:activateService', name, max)
```

| Name | Type | Description |
| --- | --- | --- |
| `name` | string | Service (job) name. |
| `max` | number or nil | Maximum players on duty. `-1` or a non-number means unlimited. |

```lua title="server/main.lua"
TriggerEvent('esx_service:activateService', 'taxi', Config.MaxInService)
```

A service that was never activated is created on first use with no limit.

### esx_service:disableService [#esx_servicedisableservice]

Takes a player off duty.

```lua
TriggerEvent('esx_service:disableService', name, playerId)
```

| Name | Type | Description |
| --- | --- | --- |
| `name` | string | Service name. |
| `playerId` | number | Server ID of the player. |

### esx_service:notifyAllInService [#esx_servicenotifyallinservice]

Sends an advanced notification to everyone on duty in a service, except the sender. The sender's mugshot is used as the icon.

```lua
TriggerEvent('esx_service:notifyAllInService', notification, name, sender)
```

| Name | Type | Description |
| --- | --- | --- |
| `notification` | table | `{ title, subject, msg, iconType }` passed to `ESX.ShowAdvancedNotification`. |
| `name` | string | Service name. |
| `sender` | number or nil | Server ID of the sender. |

```lua title="server/main.lua"
TriggerEvent('esx_service:notifyAllInService', {
    title = 'Dispatch',
    subject = 'Backup',
    msg = 'Officer needs assistance at Legion Square',
    iconType = 1,
}, 'police', source)
```

### GlobalState count [#globalstate-count]

```lua
local onDuty = GlobalState.taxi or 0
```

The value updates on every enable, disable and disconnect.

## Server callbacks [#server-callbacks]

Call these from the client with `ESX.TriggerServerCallback`, `ESX.AwaitServerCallback` or `xLib.callback`. See [Callbacks](/docs/guides/callbacks).

| Callback | Arguments | Returns | Check |
| --- | --- | --- | --- |
| `esx_service:enableService` | `name` | `canTakeService`, `maxInService`, `inServiceCount` | Caller's job must equal `name`; count must be below the limit. |
| `esx_service:disableService` | `name` | `boolean` | Caller's job must equal `name`. |
| `esx_service:isInService` | `name` | `boolean` | Caller only. |
| `esx_service:isPlayerInService` | `name`, `targetId` | `boolean` | Caller and target must both have job `name`. |
| `esx_service:getInServiceList` | `name` | table `{ [serverId] = true }` | Caller's job must equal `name`. |

```lua title="client/main.lua"
ESX.TriggerServerCallback('esx_service:enableService', function(canTakeService, maxInService, inServiceCount)
    if canTakeService then
        ESX.ShowNotification('You are now on duty')
    else
        ESX.ShowNotification(('Service full: %s/%s'):format(inServiceCount, maxInService))
    end
end, 'taxi')
```

```lua title="client/main.lua"
-- go off duty
ESX.TriggerServerCallback('esx_service:disableService', function() end, 'taxi')

-- check own duty state
local onDuty = ESX.AwaitServerCallback('esx_service:isInService', 'taxi')
```

## Client event [#client-event]

`esx_service:notifyAllInService` (net, server to client) shows the notification. It is triggered by the server event above; you do not call it yourself.

## Integration tips [#integration-tips]

- esx_service is separate from the `onDuty` field of the ESX job object. If you also use `xPlayer.setJob(job, grade, onDuty)`, keep both in sync yourself.
- Players are removed from every service when they disconnect, but not when they change job. Call `esx_service:disableService` when you fire someone.
- Duty state does not survive a restart of esx_service.

## Common issues [#common-issues]

- **enableService always returns false**: the player's job name differs from the service name you pass.
- **Count above the limit**: `activateService` ran after players were already on duty, or with a different `max`. Activate on resource start.
- **Old scripts use `TriggerServerEvent('esx_service:disableService', ...)`**: replace it with the callback.

## Related [#related]

- [esx_taxijob](/docs/addons/esx_taxijob)
- [esx_mechanicjob](/docs/addons/esx_mechanicjob)
- [esx_policejob](/docs/addons/esx_policejob)
- [Create a job](/docs/guides/create-a-job)
