> For the complete documentation index, see [llms.txt](https://docs.vames-store.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.vames-store.com/assets/vms_bossmenu/guides/creating-job2-on-esx.md).

# 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>
