> For the complete documentation index, see [llms.txt](https://docs.tworst.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tworst.com/scripts/builder-job/installation.md).

# Installation

Step-by-step guide to install the Multiplayer Builder Job (`tw-builderjob`) on your FiveM server.

***

## Prerequisites

Before installing, make sure you have:

| Requirement      | Description                           |
| ---------------- | ------------------------------------- |
| **Framework**    | QBCore, QBox, ESX, vRP, or Standalone |
| **ox\_lib**      | Required — hard dependency            |
| **oxmysql**      | Required — hard dependency            |
| **FiveM Server** | Build 2802 or higher recommended      |

***

## Step 1: Download the Script

### From Cfx.re Portal (Recommended)

1. Go to [portal.cfx.re](https://portal.cfx.re)
2. Navigate to **Granted Assets**
3. Find **tw-builderjob** in your purchased scripts
4. Click **Download** to get the latest version

### From Tebex

1. Log in to your Tebex account
2. Go to your purchases
3. Download the script package

***

## Step 2: Extract Files

Extract the downloaded archive. You will find **four folders** — all four are required:

| Folder                | What it is                                                             |
| --------------------- | ---------------------------------------------------------------------- |
| `tw-builderjob`       | The main script                                                        |
| `tw-builder-stream`   | **Map 1** — the construction sites themselves                          |
| `tw-builder-map`      | **Map 2** — the company HQ: office, changing room and garage interiors |
| `tw-builder-vehicles` | The custom machines (crane, pump, excavator, haulers)                  |

Place **all four folders** in your server's `resources` directory.

```
resources/
├── tw-builderjob/              ← Main script
│   ├── config/
│   │   ├── config.lua
│   │   ├── economy.lua
│   │   ├── keybinds.lua
│   │   ├── jobs/
│   │   ├── systems/
│   │   └── secrets/
│   │       └── discord.lua
│   ├── client/
│   ├── server/
│   ├── locales/
│   ├── html/
│   └── fxmanifest.lua
├── tw-builder-stream/          ← MAP 1 — construction sites (REQUIRED)
│   ├── stream/                 ← .ymap / .ytyp / .ydr / .ybn site files
│   └── fxmanifest.lua          ← this_is_a_map 'yes'
├── tw-builder-map/             ← MAP 2 — company HQ (REQUIRED)
│   ├── stream/                 ← .ymap / .ytyp / .ydr / .ybn HQ files
│   ├── client.lua              ← enables the interior props
│   └── fxmanifest.lua          ← this_is_a_map 'yes'
└── tw-builder-vehicles/        ← Custom machines (REQUIRED)
    ├── stream/
    ├── vehicles.meta
    ├── handling.meta
    ├── carvariations.meta
    └── vehicle_names.lua
```

### About the two maps

The job ships **two separate map resources**. They cover different places and you need both.

| Resource            | What it builds                                                                                                                                     |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tw-builder-stream` | Every **construction site** — the buildings under construction, scaffolding, piles, props and collisions for all 12 regions                        |
| `tw-builder-map`    | The **company HQ** — the office interior, the changing room and the garage where players start the job, rent machines and change into work clothes |

{% hint style="danger" %}
**Both are map resources, not prop packs.** Each declares `this_is_a_map 'yes'` and registers its `.ytyp` archetypes through `data_file 'DLC_ITYP_REQUEST'`. Without that registration the entities in the `.ymap` files never spawn — the map opens fine in CodeWalker but the game cannot see it.

If a map is missing or not started, that part of the world **simply does not exist**: players stand in an empty field, props do not spawn, and placement fails.
{% endhint %}

{% hint style="warning" %}
**`tw-builder-map` also ships a `client.lua`.** It calls `EnableInteriorProp` + `RefreshInterior` on the HQ interior so the office and garage rooms are actually populated. Do not delete it — without it the building loads as an empty shell.
{% endhint %}

{% hint style="warning" %}
Do **not** rename either map folder and do **not** merge their `stream/` folders into another resource. The `.ytyp` archetype registration is tied to each resource's own manifest.
{% endhint %}

## Step 3: Database Setup

{% hint style="success" %}
**Automatic Setup:** The script creates and migrates its own database tables on first start. No manual SQL import needed.
{% endhint %}

***

## Step 4: Configure the Script

Open `config/config.lua`. The most important keys are at the top:

```lua
-- Framework: auto | qb | oldqb | qbox | esx | oldesx | vrp | vrp2 | standalone
Config.Framework = 'auto'

-- Inventory: auto | qb_inventory | esx_inventory | ox_inventory
--            | codem-inventory | qs_inventory | tgiann-inventory
Config.Inventory = "auto"

Config.SQL = "oxmysql"

-- Server default language only — players pick their own in the menu.
-- 12 shipped: en tr de es fr hu ja nl pt ro ru sv
Config.Locale = 'en'

-- Scales money + XP of every region. 1.5 = 50% richer server-wide.
Config.RewardMultiplier  = 1.0
-- Scales every rental deposit. 0 = no deposits at all.
Config.DepositMultiplier = 1.0

-- How the [E] prompt looks. ONE setting:
--   'badge'      our world-space badge with an icon  (default)
--   'text'       plain 3D text, no badge
--   'oxlib'      ox_lib TextUI in the screen corner
--   'ox-target'  hand it to ox_target instead
--   'qb-target'  hand it to qb-target instead
--   'auto'       use ox_target / qb-target if installed, else 'badge'
Config.InteractionStyle = 'badge'
```

{% hint style="info" %}
For the three built-in styles (`badge`, `text`, `oxlib`) focus, distance and the **E** key are **identical** — only where the prompt is drawn changes. Pick `'oxlib'` or one of the target modes if you want the job to match your server's existing interaction look.
{% endhint %}

{% hint style="warning" %}
`Config.InteractionHandler` and `Config.Interaction.style` in `config/systems/interaction.lua` are **derived** from `Config.InteractionStyle`. Don't set them by hand — they get overwritten.
{% endhint %}

{% hint style="info" %}
**Leave `auto` alone unless you need to override it.** Framework, inventory, vehicle keys and the item image path all detect the resources running on your server. A value you type by hand always wins.
{% endhint %}

{% hint style="success" %}
**The two multipliers are the quickest way to balance the job for your server** — you never have to touch a per-region number.
{% endhint %}

See the [Configuration](/scripts/builder-job/configuration.md) page for all available options.

***

## Step 5: Set Your Admins

Field tools, the admin panel and the site builder all check this list:

```lua
Config.Admins = {
    'discord:000000000000000000',
}
```

{% hint style="danger" %}
**The script ships with the developer's own Discord IDs in this list.** Replace them with yours before going live, or those accounts keep admin access on your server.
{% endhint %}

Run `/cranesiteid` in-game to print your own identifier in the right format.

This one list gates both `/builderadmin` (the admin panel) and `/tw_bp` (the site builder) — see [Interface](/scripts/builder-job/interface.md).

***

## Step 6: Discord Logging (Optional)

{% hint style="danger" %}
**Never put a webhook URL in `config/config.lua`** — that file is a **shared** script and ships to every client, so a URL there is a published secret. A player can dump it, flood your staff channel with fake logs, or delete the webhook.
{% endhint %}

URLs and the bot token belong in **`config/secrets/discord.lua`**, which is loaded only as a `server_script`:

```lua
TwSecrets.Discord = {
    botToken = "",             -- only used to fetch avatars for embeds
    webhooks = {
        default   = "",        -- fallback for any empty category below
        jobstart  = "",
        jobfinish = "",
        lobby     = "",
        economy   = "",
        admin     = "",
        security  = "",
        error     = "",
    },
}
```

Leaving everything empty is fine — no URL means no request is ever made, and the in-game log page (`/builderadmin` → Logs) works either way.

Which events fire is set separately in `config/config.lua`:

```lua
Config.Discord = {
    enabled = true,
    botName = "Tworst Store",
    events  = {
        jobStart = true, jobFinish = true, jobAbandon = true,
        lobbyJoin = true, lobbyLeave = true, reconnect = true,
        rewardFailure = true, security = true,
    },
}
```

{% hint style="info" %}
Prefer to keep secrets out of the resource folder entirely? Use convars in `server.cfg` instead — see Step 8. A convar that is set always wins over the table above.
{% endhint %}

***

## Step 7: Turn On the Release Profile

{% hint style="warning" %}
**Release blocker.** While debug mode is on, roughly 250 debug commands register and several server-side validations are bypassed. Before you go live, set:
{% endhint %}

```lua
Config.ReleaseBuild = true
```

This one switch force-disables `Debug`, `DebugCommands`, `DebugZoneDraw`, `DebugZones`, `DebugTowerScenery`, `RopeProbe`, `Profiler`, `FastTest` and `SoloTest`, and plays every region at full size. **Read the value — don't trust a note.**

***

## Step 8: Add to server.cfg

Order matters. Your framework, `oxmysql` and `ox_lib` must be running first, and all three asset resources must load **before** the script:

```cfg
## --- Dependencies (must start BEFORE tw-builderjob) ---
ensure oxmysql
ensure ox_lib
ensure qb-core                  # or es_extended / qbx_core — whichever you run

## --- Tworst Builder Job ---
ensure tw-builder-stream        # map 1 — the construction sites
ensure tw-builder-map           # map 2 — the company HQ (office, changing room, garage)
ensure tw-builder-vehicles      # custom machines — crane, pump, excavator, haulers
ensure tw-builderjob            # the script — starts last
```

### Optional: Discord secrets as convars

If you would rather keep webhook URLs out of the resource folder (git, backups, a shared file server), set them here instead of in `config/secrets/discord.lua`:

```cfg
## Discord — use `set`, NEVER `setr`
set tw_builder_discord_token    ""
set tw_builder_webhook_default   "https://discord.com/api/webhooks/..."
set tw_builder_webhook_jobstart  ""
set tw_builder_webhook_jobfinish ""
set tw_builder_webhook_lobby     ""
set tw_builder_webhook_economy   ""
set tw_builder_webhook_admin     ""
set tw_builder_webhook_security  ""
set tw_builder_webhook_error     ""
```

{% hint style="danger" %}
**Use `set`, not `setr`.** `setr` **replicates** the value to every connected client — which leaks the webhook to exactly the people you are hiding it from. Only ever use plain `set` for these.
{% endhint %}

{% hint style="warning" %}
**Load Order recap:**

* `oxmysql`, `ox_lib` and your framework start **before** `tw-builderjob`
* `tw-builder-stream`, `tw-builder-map` and `tw-builder-vehicles` start **before** `tw-builderjob`
* Starting the script first will produce missing sites, missing vehicles, or a failed database migration
  {% endhint %}

***

## Step 9: Restart Server

1. Save all configuration changes
2. Restart your server completely
3. Check the server console for any errors

***

## Verification

After starting the server, verify the installation:

### Check Console

* Database tables created successfully (first run only)
* No error messages related to `tw-builderjob`
* No streaming errors from `tw-builder-stream` or `tw-builder-map`

### In-Game Test

1. Go to the builder job location and look for the job blip
2. Confirm the construction site is built in the world and the HQ interior is furnished (both maps are loading)
3. Interact with the NPC to open the job menu
4. Create a lobby and pick a region
5. Rent a vehicle from the company lot
6. Drive to the site and complete the first task

***

## Troubleshooting

### Script Won't Start

1. Confirm `ox_lib` and `oxmysql` are started **before** `tw-builderjob`
2. Check the resource folder is named exactly `tw-builderjob` (case-sensitive on Linux)
3. Check for syntax errors in your edited config files

### Site or HQ Missing / Empty Field / Props Not Loading

1. Make sure **both** `tw-builder-stream` (sites) and `tw-builder-map` (HQ) are in your resources folder
2. Verify both are ensured in `server.cfg` **before** `tw-builderjob`
3. Confirm neither folder was renamed and both `stream/` folders are intact
4. Check the server console for `DLC_ITYP_REQUEST` or streaming errors

### HQ Building Loads But the Rooms Are Empty

1. Confirm `tw-builder-map/client.lua` is present and untouched — it enables the interior props
2. Restart `tw-builder-map`, then rejoin; interior props are applied on resource start

### Vehicles Missing or Spawning as a Default Car

1. Make sure `tw-builder-vehicles` is started
2. Confirm the meta files were not modified

### Rewards Not Paid / Items Not Given

1. Verify `Config.Inventory` matches your inventory (or leave `auto`)
2. On ESX servers a leftover `qb_inventory` value silently gives nothing

### Framework Not Detected

1. Leave `Config.Framework = 'auto'`, or set it explicitly
2. Verify the framework resource starts before `tw-builderjob`

### No Discord Logs

1. Check the URL is in `config/secrets/discord.lua` (or a `set tw_builder_webhook_*` convar), **not** in `config.lua`
2. Confirm `Config.Discord.enabled = true` and the event is on
3. An empty category falls back to `default`; if `default` is empty too, that category is off

***

## Need Help?

{% hint style="info" %}
Having installation issues? Join our [Discord server](https://discord.gg/tworst) and open a support ticket with:

* Server console errors
* Your `Config.Framework` and `Config.Inventory` settings
* Screenshots of any issues
  {% endhint %}
