> For the complete documentation index, see [llms.txt](https://visionstore.gitbook.io/visionstore-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://visionstore.gitbook.io/visionstore-docs/scripts/crafting/developers/bridges.md).

# Bridges and config hooks

The open-source bridge files - framework, inventory, housing, HUD - and the hooks in config.lua.

Everything that talks to other resources is **open source** and fully editable:

```
bridge/
├── framework/
│   ├── server.lua        ← adapter registry + auto-detect
│   ├── qbox.lua          ← qbx_core
│   ├── qbcore.lua        ← qb-core
│   ├── esx.lua           ← es_extended
│   └── standalone.lua    ← no framework (must stay last)
├── inventory/
│   └── ox_inventory.lua  ← every inventory call
├── housing/
│   └── server.lua        ← property ids + house door resync
└── client.lua            ← HUD hide/show, character events, house doors
```

{% hint style="info" %}
**Updating?** Back up `config/` and `bridge/` before replacing the folder, then copy your changes back.
{% endhint %}

## Framework

With `Config.Framework = 'auto'`, the first adapter whose `detect()` passes is used, in this order: **qbox → qb-core → esx → standalone**. The console prints which one: `[Chapo_Crafting] framework: qbox`.

| Adapter      | Identity              | Admin check                                  |
| ------------ | --------------------- | -------------------------------------------- |
| `qbox`       | citizenid             | `exports.qbx_core:HasPermission(src, group)` |
| `qb-core`    | citizenid             | `QBCore.Functions.HasPermission(src, group)` |
| `esx`        | ESX identifier        | `xPlayer.getGroup() == group`                |
| `standalone` | `license:` identifier | ACE only                                     |

### Adding another framework

{% stepper %}
{% step %}

### Copy an adapter

Copy `bridge/framework/qbcore.lua` to `bridge/framework/myframework.lua`.
{% endstep %}

{% step %}

### Fill in the functions

Every adapter provides:

```lua
detect()                                  -- -> boolean
init()                                    -- optional, runs after detection
GetIdentifier(src)                        -- -> stable character id | nil
GetName(src)                              -- -> display name
GetJobInfo(src)                           -- -> { name, type, grade, onDuty } | nil
GetGangInfo(src)                          -- -> { name, grade } | nil
GetMoney(src, account)                    -- -> number
RemoveMoney(src, account, amount, reason) -- -> boolean
AddMoney(src, account, amount, reason)    -- -> boolean
IsAdmin(src, groups)                      -- -> boolean (optional)
Notify(src, message, kind)                -- optional
```

Register it at the bottom, and call `Chapo.Framework.PlayerLoaded(src)` / `PlayerUnloaded(src)` from your framework's character load / logout events:

```lua
Chapo.Framework.Register('myframework', adapter)

AddEventHandler('myframework:playerLoaded', function(src)
    if not Chapo.Framework.Is('myframework') then return end
    Chapo.Framework.PlayerLoaded(src)
end)
```

{% endstep %}

{% step %}

### Load it

Add the file to `server_scripts` in `fxmanifest.lua`, next to the other adapters and **before** `bridge/framework/standalone.lua` (standalone always matches, so it must stay last). Then set `Config.Framework = 'myframework'` or leave it on `'auto'`.
{% endstep %}
{% endstepper %}

## Inventory

`bridge/inventory/ox_inventory.lua` holds every inventory call the script makes (counting and removing ingredients, giving results, bench item slots and metadata, stashes). Portable benches rely on **per-slot metadata** (each bench item carries its own `benchId`), so any replacement inventory must support item metadata and exact-slot reads and removals.

If your ox\_inventory build caps stack sizes, set `DEFAULT_STACK_LIMIT` at the top of this file so the stash capacity check matches.

## Housing

Any housing script that gives each interior its own **routing bucket** works with no setup: a bench placed inside a house is saved in that bucket and only appears there.

`bridge/housing/server.lua` adds two extras:

* **`Chapo.Housing.PropertyOf(src)`** records *which* property a bench was placed in (shown in logs and in `/crafting benches`). rtx\_housing is supported. For another script, return its property id here.
* The bench list is re-sent when a player walks through a house door.

For housing scripts that change the bucket without setting the `instance` state bag, report the door from the client. rtx\_housing is already wired in `bridge/client.lua`:

```lua
RegisterNetEvent('rtx_housing:Global:EnterProperty', houseDoor)
RegisterNetEvent('rtx_housing:Global:ExitProperty', houseDoor)
```

Add your housing script's events the same way, or call this from it (client side):

```lua
TriggerEvent('Chapo_Crafting:client:instanceChanged')
```

## Client bridge

`bridge/client.lua` also holds:

**HUD hiding.** Called when a bench opens / closes (with `Config.Crafting.hideHudAtBench = true`). The minimap and native HUD are already handled, and HUDs with hide / show exports can simply be listed in `Config.Hud.resources`. For anything else:

```lua
function Bridge.HideHud() TriggerEvent('my-hud:toggle', false) end
function Bridge.ShowHud() TriggerEvent('my-hud:toggle', true)  end
```

**Character load / logout.** Re-creates the hidden NPC peds after a character switch. QBCore / Qbox (`QBCore:Client:OnPlayerLoaded`) and ESX (`esx:playerLoaded`) are wired. Add your framework's events here.

## Config hooks

Two functions in `config/config.lua` let you change server-side behaviour without touching the core.

### `Config.CanPlaceBench`

An extra placement rule, checked on the server after all the normal checks. Return `false` plus an optional locale key to refuse.

```lua
Config.CanPlaceBench = function(src, benchType, coords, bucket)
    return true
end
```

Example: weapons benches only inside a private interior (a routing bucket other than the open world):

```lua
Config.CanPlaceBench = function(src, benchType, coords, bucket)
    if benchType == 'weapons' and not bucket then
        return false, 'placement.reasons.RESTRICTED_ZONE'
    end
    return true
end
```

### `Config.GetPlayerInstance`

Which "instance" a player is in. Benches are only visible to and usable by players in the same instance. `nil` / `0` = the open world.

```lua
Config.GetPlayerInstance = function(src)
    local instance = Player(src).state.instance
    if instance and instance ~= 0 then return instance end

    local bucket = GetPlayerRoutingBucket(src)
    if bucket ~= 0 then return bucket end

    return nil
end
```

The default reads the `instance` state bag, then the routing bucket. Change it if your instance system works differently.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

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

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://visionstore.gitbook.io/visionstore-docs/scripts/crafting/developers/bridges.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
