# Locker Usage Guide
The locker module stores physical player inventory items by classname. It is
separate from the virtual arsenal unlock module documented in
[Owned Storage Usage Guide](/server-modules/owned-storage).
## Storage Model
Locker data is persisted through SurrealDB by the server extension.
```json
{
"arifle_MX_F": {
"category": "weapon",
"classname": "arifle_MX_F",
"amount": 1
}
}
```
Rules validated by the Rust service:
- A locker can contain up to 25 unique classnames.
- `category` and `classname` cannot be empty.
- `amount` must be greater than `0`.
- `locker:add` creates an empty locker automatically when one does not exist.
- `locker:get`, `locker:patch`, and `locker:remove` require an existing locker.
- `locker:remove` takes the classname directly, not a JSON object.
## Commands
All commands are called on the `locker` group.
|
Command
|
Arguments
|
Returns
|
locker:create
|
uid
|
Empty item map as JSON.
|
locker:get
|
uid
|
Item map as JSON.
|
locker:add
|
uid
,
item_json
|
Updated item map as JSON.
|
locker:update
|
uid
,
items_json
|
Replaced item map as JSON.
|
locker:patch
|
uid
,
patch_json
|
Updated item map as JSON.
|
locker:remove
|
uid
,
classname
|
Updated item map as JSON.
|
locker:delete
|
uid
|
OK
.
|
locker:exists
|
uid
|
true
or
false
.
|
## Error Handling
Every command returns a string payload. Always check for the `Error:` prefix
before parsing JSON.
```sqf
private _result = "forge_server" callExtension ["locker:get", [getPlayerUID player]];
private _payload = _result select 0;
if (_payload find "Error:" == 0) exitWith {
systemChat format ["Locker error: %1", _payload];
};
private _locker = fromJSON _payload;
```
## Add an Item
`locker:add` creates or overwrites one classname entry.
```sqf
private _item = createHashMapFromArray [
["category", "weapon"],
["classname", "arifle_MX_F"],
["amount", 1]
];
private _result = "forge_server" callExtension ["locker:add", [
getPlayerUID player,
toJSON _item
]];
private _payload = _result select 0;
if (_payload find "Error:" == 0) exitWith {
hint format ["Failed to store item: %1", _payload];
};
private _locker = fromJSON _payload;
```
## Patch an Amount
`locker:patch` currently patches the `amount` field for an existing classname.
```sqf
private _patch = createHashMapFromArray [
["classname", "arifle_MX_F"],
["amount", 5]
];
private _result = "forge_server" callExtension ["locker:patch", [
getPlayerUID player,
toJSON _patch
]];
```
## Remove an Item
`locker:remove` takes the classname as the second argument.
```sqf
private _result = "forge_server" callExtension ["locker:remove", [
getPlayerUID player,
"arifle_MX_F"
]];
```
## Retrieve an Item
```sqf
fnc_retrieveLockerItem = {
params ["_classname"];
private _result = "forge_server" callExtension ["locker:get", [getPlayerUID player]];
private _payload = _result select 0;
if (_payload find "Error:" == 0) exitWith {
hint format ["Failed to load locker: %1", _payload];
false
};
private _locker = fromJSON _payload;
private _item = _locker getOrDefault [_classname, createHashMap];
if (_item isEqualTo createHashMap) exitWith {
hint "Item was not found in your locker.";
false
};
private _amount = _item getOrDefault ["amount", 0];
if (_amount <= 0) exitWith {
hint "Item is out of stock.";
false
};
if !(player canAdd _classname) exitWith {
hint "Not enough inventory space.";
false
};
player addItem _classname;
if (_amount > 1) then {
private _patch = createHashMapFromArray [
["classname", _classname],
["amount", _amount - 1]
];
"forge_server" callExtension ["locker:patch", [getPlayerUID player, toJSON _patch]];
} else {
"forge_server" callExtension ["locker:remove", [getPlayerUID player, _classname]];
};
true
};
```
## Replace the Whole Locker
`locker:update` replaces the whole item map. Use it for explicit bulk syncs,
not single-item changes.
```sqf
private _items = createHashMapFromArray [
["arifle_MX_F", createHashMapFromArray [
["category", "weapon"],
["classname", "arifle_MX_F"],
["amount", 1]
]]
];
private _result = "forge_server" callExtension ["locker:update", [
getPlayerUID player,
toJSON _items
]];
```
## Hot State
The `locker:hot:*` commands keep a runtime copy of a player's locker and write
it back only when `locker:hot:save` runs.
|
Command
|
Arguments
|
Returns
|
locker:hot:init
|
uid
|
Item map as JSON.
|
locker:hot:get
|
uid
|
Item map as JSON.
|
locker:hot:override
|
uid
,
items_json
|
Item map as JSON.
|
locker:hot:save
|
uid
|
Current hot item map as JSON.
|
locker:hot:remove
|
uid
|
OK
.
|
Use hot state for session-heavy locker workflows. Use the durable commands for
simple item deposits and withdrawals.
## Best Practices
- Keep categories normalized, for example `weapon`, `magazine`, `item`, or
`backpack`.
- Use `locker:patch` for quantity changes.
- Use `locker:remove` when quantity reaches zero.
- Treat the locker response as a hash map keyed by classname.
- Check capacity before bulk operations that may exceed 25 unique items.