# Creating job2 on ESX

If you want to use the gang menu, but don't have job2 registered on your server, here is a step-by-step tutorial on how to register it.

***

{% hint style="danger" %}

## Important

The following tutorial is prepared for ESX 1.12.4 - if you have a different version of your ESX, the lines may not match, you need to make changes to the equivalent lines in your es\_extended.
{% endhint %}

<details>

<summary>STEP 1</summary>

Create new columns for your users table.

{% hint style="warning" %}

## Not sure how to do it?

No worries - we've prepared a short guide that shows you step by step how to import an SQL file into your database:\
👉 [**Click here to view the tutorial.**](/helpful/basic-server-knowledge/how-to-import-sql-to-database.md)
{% endhint %}

```sql
ALTER TABLE `users`
    ADD COLUMN `job2` VARCHAR(20) DEFAULT 'unemployed',
    ADD COLUMN `job_grade2` INT(11) DEFAULT 0;
```

</details>

<details>

<summary>STEP 2</summary>

1. Go to your **es\_extended/client/modules/events.lua**
2. Register a new event:

```lua
ESX.SecureNetEvent("esx:setJob2", function(Job2)
    ESX.SetPlayerData('job2', Job2)
end)
```

</details>

<details>

<summary>STEP 3</summary>

1. Go to your **es\_extended/server/main.lua**
2. Find `local loadPlayer` ([will be available in the first lines](https://github.com/esx-framework/esx_core/blob/74e76ed6137942b5aa826b9c0031a8cde84f3b23/%5Bcore%5D/es_extended/server/main.lua#L6))
3. Replace with the following code:

```lua
local loadPlayer = "SELECT `accounts`, `job`, `job_grade`, `job2`, `job_grade2`, `group`, `position`, `inventory`, `skin`, `loadout`, `metadata`"
```

4. Find function [`function loadESXPlayer(`](https://github.com/esx-framework/esx_core/blob/74e76ed6137942b5aa826b9c0031a8cde84f3b23/%5Bcore%5D/es_extended/server/main.lua#L132)
5. Inside this function, find [`local job, grade =`](https://github.com/esx-framework/esx_core/blob/74e76ed6137942b5aa826b9c0031a8cde84f3b23/%5Bcore%5D/es_extended/server/main.lua#L167)
6. Find the `job` registration end [**here**](https://github.com/esx-framework/esx_core/blob/74e76ed6137942b5aa826b9c0031a8cde84f3b23/%5Bcore%5D/es_extended/server/main.lua#L189) and add the following code:

```lua
-- Job2
local job2, grade2 = result.job2, tostring(result.job_grade2)

if not ESX.DoesJobExist(job2, grade2) then
    print(("[^3WARNING^7] Ignoring invalid job2 for ^5%s^7 [job2: ^5%s^7, grade: ^5%s^7]"):format(identifier, job2, grade2))
    job2, grade2 = "unemployed", "0"
end

local job2Object, grade2Object = ESX.Jobs[job2], ESX.Jobs[job2].grades[grade2]

userData.job2 = {
    id = job2Object.id,
    name = job2Object.name,
    label = job2Object.label,
    
    grade = tonumber(grade),
    grade_name = grade2Object.name,
    grade_label = grade2Object.label,
    grade_salary = grade2Object.salary,
    
    skin_male = grade2Object.skin_male and json.decode(grade2Object.skin_male) or {},
    skin_female = grade2Object.skin_female and json.decode(grade2Object.skin_female) or {},
}
```

7. Find [`local xPlayer = CreateExtendedPlayer`](https://github.com/esx-framework/esx_core/blob/74e76ed6137942b5aa826b9c0031a8cde84f3b23/%5Bcore%5D/es_extended/server/main.lua#L258)&#x20;
8. Just after `userData.job` add `userData.job2`

```lua
local xPlayer = CreateExtendedPlayer(
    playerId,
    identifier,
    userData.group,
    userData.accounts,
    userData.inventory,
    userData.weight,
    userData.job,
    userData.job2, -- HERE ADDED
    userData.loadout,
    GetPlayerName(playerId),
    userData.coords,
    userData.metadata
)
```

9. Find [`ESX.RegisterServerCallback("esx:getPlayerData"`](https://github.com/esx-framework/esx_core/blob/74e76ed6137942b5aa826b9c0031a8cde84f3b23/%5Bcore%5D/es_extended/server/main.lua#L596-L609)
10. Add `job2 = xPlayer.getJob2(),` to callback data:

```lua
ESX.RegisterServerCallback("esx:getPlayerData", function(source, cb)
    local xPlayer = ESX.GetPlayerFromId(source)

    cb({
        identifier = xPlayer.identifier,
        accounts = xPlayer.getAccounts(),
        inventory = xPlayer.getInventory(),
        job = xPlayer.getJob(),
        job2 = xPlayer.getJob2(), -- HERE ADDED
        loadout = xPlayer.getLoadout(),
        money = xPlayer.getMoney(),
        position = xPlayer.getCoords(true),
        metadata = xPlayer.getMeta(),
    })
end)
```

11. Find [`ESX.RegisterServerCallback("esx:getOtherPlayerData"`](https://github.com/esx-framework/esx_core/blob/74e76ed6137942b5aa826b9c0031a8cde84f3b23/%5Bcore%5D/es_extended/server/main.lua#L619-L632)
12. Add `job2 = xPlayer.getJob2(),` to callback data:

```lua
ESX.RegisterServerCallback("esx:getOtherPlayerData", function(_, cb, target)
    local xPlayer = ESX.GetPlayerFromId(target)

    cb({
        identifier = xPlayer.identifier,
        accounts = xPlayer.getAccounts(),
        inventory = xPlayer.getInventory(),
        job = xPlayer.getJob(),
        job2 = xPlayer.getJob2(), -- HERE ADDED
        loadout = xPlayer.getLoadout(),
        money = xPlayer.getMoney(),
        position = xPlayer.getCoords(true),
        metadata = xPlayer.getMeta(),
    })
end)
```

</details>

<details>

<summary>STEP 4</summary>

1. Go to your **es\_extended/server/functions.lua**
2. Find [`function Core.SavePlayer(`](https://github.com/esx-framework/esx_core/blob/74e76ed6137942b5aa826b9c0031a8cde84f3b23/%5Bcore%5D/es_extended/server/functions.lua#L201-L232)
3. Replace with the following code:

```lua
function Core.SavePlayer(xPlayer, cb)
    if not xPlayer.spawned then
        return cb and cb()
    end

    updateHealthAndArmorInMetadata(xPlayer)
    local parameters <const> = {
        json.encode(xPlayer.getAccounts(true)),
        xPlayer.job.name,
        xPlayer.job.grade,
        xPlayer.job2.name, -- HERE ADDED
        xPlayer.job2.grade, -- HERE ADDED
        xPlayer.group,
        json.encode(xPlayer.getCoords(false, true)),
        json.encode(xPlayer.getInventory(true)),
        json.encode(xPlayer.getLoadout(true)),
        json.encode(xPlayer.getMeta()),
        xPlayer.identifier,
    }

    MySQL.prepare( -- HERE ADDED
        "UPDATE `users` SET `accounts` = ?, `job` = ?, `job_grade` = ?, `job2` = ?, `job_grade2` = ?, `group` = ?, `position` = ?, `inventory` = ?, `loadout` = ?, `metadata` = ? WHERE `identifier` = ?",
        parameters,
        function(affectedRows)
            if affectedRows == 1 then
                print(('[^2INFO^7] Saved player ^5"%s^7"'):format(xPlayer.name))
                TriggerEvent("esx:playerSaved", xPlayer.playerId, xPlayer)
            end
            if cb then
                cb()
            end
        end
    )
end
```

4. Find [`function Core.SavePlayers(`](https://github.com/esx-framework/esx_core/blob/74e76ed6137942b5aa826b9c0031a8cde84f3b23/%5Bcore%5D/es_extended/server/functions.lua#L236-L275)
5. Replace with the following code:

```lua
function Core.SavePlayers(cb)
    local xPlayers <const> = ESX.Players
    if not next(xPlayers) then
        return
    end

    local startTime <const> = os.time()
    local parameters = {}

    for _, xPlayer in pairs(ESX.Players) do
        updateHealthAndArmorInMetadata(xPlayer)
        parameters[#parameters + 1] = {
            json.encode(xPlayer.getAccounts(true)),
            xPlayer.job.name,
            xPlayer.job.grade,
            xPlayer.job2.name, -- HERE ADDED
            xPlayer.job2.grade, -- HERE ADDED
            xPlayer.group,
            json.encode(xPlayer.getCoords(false, true)),
            json.encode(xPlayer.getInventory(true)),
            json.encode(xPlayer.getLoadout(true)),
            json.encode(xPlayer.getMeta()),
            xPlayer.identifier,
        }
    end

    MySQL.prepare( -- HERE ADDED
        "UPDATE `users` SET `accounts` = ?, `job` = ?, `job_grade` = ?, `job2` = ?, `job_grade2` = ?,`group` = ?, `position` = ?, `inventory` = ?, `loadout` = ?, `metadata` = ? WHERE `identifier` = ?",
        parameters,
        function(results)
            if not results then
                return
            end

            if type(cb) == "function" then
                return cb()
            end

            print(("[^2INFO^7] Saved ^5%s^7 %s over ^5%s^7 ms"):format(#parameters, #parameters > 1 and "players" or "player", ESX.Math.Round((os.time() - startTime) / 1000000, 2)))
        end
    )
end
```

</details>

<details>

<summary>STEP 5</summary>

1. Go to your **es\_extended/server/classes/player.lua**
2. Find [`function CreateExtendedPlayer(`](https://github.com/esx-framework/esx_core/blob/74e76ed6137942b5aa826b9c0031a8cde84f3b23/%5Bcore%5D/es_extended/server/classes/player.lua#L31C1-L31C126)
3. Enter inside the parameters of the `job2` function just after the `job`

```lua
function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, weight, job, job2, loadout, name, coords, metadata)
```

4. A few lines below find [`self.job = job`](https://github.com/esx-framework/esx_core/blob/74e76ed6137942b5aa826b9c0031a8cde84f3b23/%5Bcore%5D/es_extended/server/classes/player.lua#L40) and just below it add `self.job2 = job2`

```lua
self.job = job
self.job2 = job2 -- HERE ADDED
```

5. Another few lines below find [`stateBag:set("job", self.job, true)`](https://github.com/esx-framework/esx_core/blob/74e76ed6137942b5aa826b9c0031a8cde84f3b23/%5Bcore%5D/es_extended/server/classes/player.lua#L71)
6. And under it, add job2 statebag registration - `stateBag:set("job2", self.job2, true)`

```lua
stateBag:set("job", self.job, true)
stateBag:set("job2", self.job2, true) -- HERE ADDED
```

7. Find [`function self.getJob()`](https://github.com/esx-framework/esx_core/blob/74e76ed6137942b5aa826b9c0031a8cde84f3b23/%5Bcore%5D/es_extended/server/classes/player.lua#L253-L255)
8. Register a new function for job2 below:

```lua
function self.getJob2()
    return self.job2
end
```

9. Find [`function self.setJob(`](https://github.com/esx-framework/esx_core/blob/74e76ed6137942b5aa826b9c0031a8cde84f3b23/%5Bcore%5D/es_extended/server/classes/player.lua#L514-L551)
10. Register a new function for job2 below:

```lua
function self.setJob2(newJob2, grade2, onDuty)
    grade2 = tostring(grade2)
    local lastJob = self.job2
    if not ESX.DoesJobExist(newJob2, grade2) then
        return print(("[ESX] [^3WARNING^7] Ignoring invalid ^5.setJob2()^7 usage for ID: ^5%s^7, Job2: ^5%s^7"):format(self.source, newJob2))
    end
    local jobObject2, gradeObject2 = ESX.Jobs[newJob2], ESX.Jobs[newJob2].grades[grade2]
    self.job2 = {
        id = jobObject2.id,
        name = jobObject2.name,
        label = jobObject2.label,
        grade = tonumber(grade2),
        grade_name = gradeObject2.name,
        grade_label = gradeObject2.label,
        grade_salary = gradeObject2.salary,
        skin_male = gradeObject2.skin_male and json.decode(gradeObject2.skin_male) or {},
        skin_female = gradeObject2.skin_female and json.decode(gradeObject2.skin_female) or {},
    }
    TriggerEvent("esx:setJob2", self.source, self.job2, lastJob)
    self.triggerEvent("esx:setJob2", self.job2, lastJob)
    Player(self.source).state:set("job2", self.job2, true)
end
```

</details>

<details>

<summary>STEP 6</summary>

1. Go to your **es\_extended/server/modules/commands.lua**
2. Register a new command:

```lua
ESX.RegisterCommand(
    "setjob2",
    "admin",
    function(xPlayer, args, showError)
        if not ESX.DoesJobExist(args.job2, args.grade2) then
            return showError(TranslateCap("command_setjob_invalid"))
        end

        args.playerId.setJob2(args.job2, args.grade2)
    end,
    true,
    {
        help = TranslateCap("command_setjob"),
        validate = true,
        arguments = {
            { name = "playerId", help = TranslateCap("commandgeneric_playerid"), type = "player" },
            { name = "job2", help = "Job2", type = "string" },
            { name = "grade2", help = "Grade2", type = "number" },
        },
    }
)
```

**Done! Restart the server and test.** :tada:\
Use the `/setjob2 [PLAYER_ID] [JOB2_NAME] [JOB2_GRADE]` command.

Job2 works on a job basis, but is separate, so you still use the **jobs** table and **job\_grades** in your database. You can set the jobs you have there as both jobs and job2.

</details>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.vames-store.com/assets/vms_bossmenu/guides/creating-job2-on-esx.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
