Jacob Schmidt 827f3303a2 feat: add initial Docus setup and sync tool for documentation generation
- Created package.json for Docus with necessary scripts and dependencies.
- Implemented sync-docus-docs.mjs to automate the generation of documentation files from source markdown.
- Defined mappings for generated pages and virtual routes to ensure proper linking in documentation.
- Added static content files for the documentation structure, including navigation and index pages.
2026-04-21 16:07:27 -05:00

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.