495 lines
7.8 KiB
Markdown
495 lines
7.8 KiB
Markdown
# 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.
|
|
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>
|
|
Command
|
|
</th>
|
|
|
|
<th>
|
|
Arguments
|
|
</th>
|
|
|
|
<th>
|
|
Returns
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
|
|
<tbody>
|
|
<tr>
|
|
<td>
|
|
<code>
|
|
locker:create
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
<code>
|
|
uid
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
Empty item map as JSON.
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>
|
|
<code>
|
|
locker:get
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
<code>
|
|
uid
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
Item map as JSON.
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>
|
|
<code>
|
|
locker:add
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
<code>
|
|
uid
|
|
</code>
|
|
|
|
, <code>
|
|
item_json
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
Updated item map as JSON.
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>
|
|
<code>
|
|
locker:update
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
<code>
|
|
uid
|
|
</code>
|
|
|
|
, <code>
|
|
items_json
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
Replaced item map as JSON.
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>
|
|
<code>
|
|
locker:patch
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
<code>
|
|
uid
|
|
</code>
|
|
|
|
, <code>
|
|
patch_json
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
Updated item map as JSON.
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>
|
|
<code>
|
|
locker:remove
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
<code>
|
|
uid
|
|
</code>
|
|
|
|
, <code>
|
|
classname
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
Updated item map as JSON.
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>
|
|
<code>
|
|
locker:delete
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
<code>
|
|
uid
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
<code>
|
|
OK
|
|
</code>
|
|
|
|
.
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>
|
|
<code>
|
|
locker:exists
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
<code>
|
|
uid
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
<code>
|
|
true
|
|
</code>
|
|
|
|
or <code>
|
|
false
|
|
</code>
|
|
|
|
.
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
## 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.
|
|
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>
|
|
Command
|
|
</th>
|
|
|
|
<th>
|
|
Arguments
|
|
</th>
|
|
|
|
<th>
|
|
Returns
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
|
|
<tbody>
|
|
<tr>
|
|
<td>
|
|
<code>
|
|
locker:hot:init
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
<code>
|
|
uid
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
Item map as JSON.
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>
|
|
<code>
|
|
locker:hot:get
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
<code>
|
|
uid
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
Item map as JSON.
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>
|
|
<code>
|
|
locker:hot:override
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
<code>
|
|
uid
|
|
</code>
|
|
|
|
, <code>
|
|
items_json
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
Item map as JSON.
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>
|
|
<code>
|
|
locker:hot:save
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
<code>
|
|
uid
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
Current hot item map as JSON.
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>
|
|
<code>
|
|
locker:hot:remove
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
<code>
|
|
uid
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
<code>
|
|
OK
|
|
</code>
|
|
|
|
.
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
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.
|