# Database

## The table

One table holds every saved inventory. It is created on start; nothing to import.

```sql
CREATE TABLE IF NOT EXISTS `koja_inventory_v2` (
    `id` VARCHAR(120) NOT NULL,
    `type` VARCHAR(16) NOT NULL,
    `data` LONGTEXT NULL,
    `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    INDEX `idx_type` (`type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```

`data` is JSON with the grid size and the items (name, count, position, rotation, metadata). If a table called `koja_grid_inventory` from an older build exists and `koja_inventory_v2` does not, it is renamed on start.

## Container ids

| Id | What it is |
|---|---|
| `player:<identifier>` | Pockets of a character. |
| `slot:<identifier>:<key>` | One equipment or clothing slot, e.g. `slot:char1:abc:backpack`. |
| `bag:<itemId>` | The grid inside a backpack, outfit bag or any item with `container`. |
| `stash:<name>` | A shared stash. Personal stashes add the owner: `stash:<name>:<identifier>`. |
| `trunk:<plate>` | A vehicle trunk. |
| `glovebox:<plate>` | A vehicle glovebox. |

Drops, shops, crafting benches, dumpsters and test dummies are not saved.

## Saving

Changed inventories are written every `Config.SaveInterval` seconds (60 by default), when a player leaves, and when the resource stops. **Save all** in the admin panel writes everything immediately.

## Resource KVP

Some data is stored in the resource KVP (FiveM's key-value store), not in MySQL:

| Key prefix | What |
|---|---|
| `cfg:` | Values changed in the admin panel Settings tab. |
| `item:` | Runtime item definitions (`RegisterItem` and the admin item editor). |
| `location:` | Stashes, shops and benches created in the admin panel or with `CreateLocation`. |
| `trunkarea:` | Trunk prop areas calibrated with `/trunkarea`. |

KVP belongs to the resource name. Renaming the resource folder starts these with empty values.
