> For the complete documentation index, see [llms.txt](https://code-after-sex.gitbook.io/script-documentation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://code-after-sex.gitbook.io/script-documentation/cas-gunsmith/developer/how-it-works.md).

# How It Works

How CAS Gunsmith works under the hood — framework bridges, languages, server authority, KVP data storage, equip sync and the VORP and RSG touchpoints.

This page is for developers adapting the script to their own server.

## Frameworks

`shared/framework.lua` detects VORP or RSG Core when the resource starts. The matching `bridge/sv_*.lua` and `bridge/cl_*.lua` fill a shared `Bridge` table: character, money, weapons, weapon parts, ammo, items, notifications, inventory lock and the equip event. Nothing else in the resource talks to a framework, so supporting another framework means writing one server and one client bridge.

## Languages

`shared/locale.lua` loads `locales/<Config.Locale>.json` over `locales/en.json` on the server and on the client, and provides `_L(key, vars)`. The server writes its notifications, work order lines and option names in that language. The client uses it for the prompt and blip and sends the whole text table to the interface with every open.

## Server authority

The client only handles the interface, the camera and the preview. Every decision about money, parts, ammo and maintenance is made on the server.

1. When the player opens the bench, the server checks the distance, reads the weapons and their components from the inventory, and sends the catalogue to the interface based on the player's rank.
2. At payment, the client sends only the selections and the total it displays.
3. The server cleans the selections against the configuration, applies locks and carry limits, and calculates the price itself.
4. If the calculated total does not match the interface, the order is rejected and the player gets the notification "The price changed. Check the order and try again."
5. The money is taken, components are changed in the inventory and ammo is added. If the component step fails, the old parts are put back and the charge is refunded. If some ammo cannot be added, only that line is refunded.

Every payment request carries a single-use id. The same request is never processed twice, and only one order per player is processed at a time.

## Data storage

The script does not use SQL. Data is kept in the server's resource KVP store:

| Key                   | Contents                                                                                                                     |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `gs:w:<weapon id>`    | Date and shop the weapon was first logged, last service date, bought parts and engravings, selected ammo type and condition. |
| `gs:p:<character id>` | The character's presets.                                                                                                     |

Weapon parts are stored in the inventory as `{ [category] = component }`: on VORP in the weapon's components (`loadout.comps`), on RSG in the weapon item's `info.componentshash`, the same place `rsg-weaponcomp` uses. Neither inventory reliably puts them back on its own (VORP only with its `USE_WEAPON_COMPONENTS` setting on, RSG only with `rsg-weaponcomp` installed), so this resource puts them back itself (see Equip sync).

Weapons saved by older versions of this resource hold the pairs the other way round. They are rewritten in VORP's form the first time the bench opens with them; until then they are still read correctly.

## Equip sync

Every time the player equips a weapon, including when VORP gives the weapons back after login, the client catches the equip event (`vorp_inventory:onWeaponEquipped` on VORP, `rsg-weapons:client:UseWeapon` on RSG) and the server verifies that the weapon belongs to the player. The server then reads the weapon's saved components and sends back the gunsmith parts, which the client fits a second later, the same delay VORP uses for its own components. If the weapon was serviced or had its ammo type changed while in the satchel, that pending state is applied at the same time.

## Framework touchpoints

### VORP

| Type             | Name                                                                                                                                                                    |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Core             | `getUser`, `NotifyRightTip`, the character's `addCurrency` and `removeCurrency` functions                                                                               |
| Inventory export | `getUserInventoryWeapons`, `getWeaponComponents`, `addWeaponComponents`, `subWeaponComponents`, `getUserAmmo`, `addBullets`, `getItemCount`, `subItem`, `getUserWeapon` |
| Event            | `vorp_inventory:blockInventory`, `vorp_inventory:onWeaponEquipped`                                                                                                      |

For a custom inventory on either framework, replace these calls in the matching `bridge/sv_*.lua` and `bridge/cl_*.lua`.

### RSG Core

| Type             | Name                                                                                                                          |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| Core             | `GetCoreObject`, `GetPlayer`, the player's `GetMoney`, `RemoveMoney`, `AddMoney` and `GetRep` functions                       |
| Inventory export | `SetInventory`, `GetItemCount`, `AddItem`, `RemoveItem`                                                                       |
| Weapons export   | `GetUsedWeapons` (client)                                                                                                     |
| Event            | `rsg-weapons:client:UseWeapon`, `rsg-inventory:client:ItemBox`, `ox_lib:notify`; the `inv_busy` state bag locks the inventory |
