2026-05-23 09:23:12 -05:00

7.8 KiB

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.

Storage Model

Locker data is persisted through SurrealDB by the server extension.

{
  "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.

<th>
  Arguments
</th>

<th>
  Returns
</th>
<td>
  <code>
    uid
  </code>
</td>

<td>
  Empty item map as JSON.
</td>
<td>
  <code>
    uid
  </code>
</td>

<td>
  Item map as JSON.
</td>
<td>
  <code>
    uid
  </code>
  
  , <code>
    item_json
  </code>
</td>

<td>
  Updated item map as JSON.
</td>
<td>
  <code>
    uid
  </code>
  
  , <code>
    items_json
  </code>
</td>

<td>
  Replaced item map as JSON.
</td>
<td>
  <code>
    uid
  </code>
  
  , <code>
    patch_json
  </code>
</td>

<td>
  Updated item map as JSON.
</td>
<td>
  <code>
    uid
  </code>
  
  , <code>
    classname
  </code>
</td>

<td>
  Updated item map as JSON.
</td>
<td>
  <code>
    uid
  </code>
</td>

<td>
  <code>
    OK
  </code>
  
  .
</td>
<td>
  <code>
    uid
  </code>
</td>

<td>
  <code>
    true
  </code>
  
   or <code>
    false
  </code>
  
  .
</td>
Command
locker:create
locker:get
locker:add
locker:update
locker:patch
locker:remove
locker:delete
locker:exists

Error Handling

Every command returns a string payload. Always check for the Error: prefix before parsing JSON.

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.

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.

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.

private _result = "forge_server" callExtension ["locker:remove", [
    getPlayerUID player,
    "arifle_MX_F"
]];

Retrieve an Item

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.

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.

<th>
  Arguments
</th>

<th>
  Returns
</th>
<td>
  <code>
    uid
  </code>
</td>

<td>
  Item map as JSON.
</td>
<td>
  <code>
    uid
  </code>
</td>

<td>
  Item map as JSON.
</td>
<td>
  <code>
    uid
  </code>
  
  , <code>
    items_json
  </code>
</td>

<td>
  Item map as JSON.
</td>
<td>
  <code>
    uid
  </code>
</td>

<td>
  Current hot item map as JSON.
</td>
<td>
  <code>
    uid
  </code>
</td>

<td>
  <code>
    OK
  </code>
  
  .
</td>
Command
locker:hot:init
locker:hot:get
locker:hot:override
locker:hot:save
locker:hot:remove

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.