- Added support for displaying product tags in the UI, including side and faction labels. - Updated CSS to style product tags for better visibility. - Modified StoreView and store registry to include side and faction information for items. - Enhanced server-side catalog service to filter items based on the player's side. - Updated documentation to reflect changes in store behavior and metadata handling.
125 lines
5.3 KiB
Markdown
125 lines
5.3 KiB
Markdown
# Forge Server Store
|
|
|
|
## Overview
|
|
The store addon manages server-side storefront entities, catalog hydration, and
|
|
checkout coordination.
|
|
|
|
SQF owns Arma-facing storefront discovery and request validation. The Rust
|
|
extension owns authoritative checkout calculation through `store:checkout`.
|
|
|
|
## Dependencies
|
|
- `forge_server_main`
|
|
- `forge_server_common`
|
|
- `forge_server_extension` at runtime for checkout calls
|
|
- `forge_server_actor`, `forge_server_bank`, and `forge_server_org` at runtime
|
|
for checkout context and payment state
|
|
- `forge_client_store` for response RPCs
|
|
|
|
## Main Components
|
|
- `fnc_initStore.sqf` marks editor-placed store objects with `isStore = true`.
|
|
- `fnc_initCatalogService.sqf` scans live Arma config categories, builds
|
|
catalog responses, resolves checkout entries, and calculates authoritative
|
|
catalog prices. It also applies the optional mission `CfgStore` filter and
|
|
overrides before payloads or checkout validation use catalog entries.
|
|
- `fnc_initStorefrontStore.sqf` builds hydrate payloads, validates checkout
|
|
requests, calls `store:checkout`, syncs client patches, and coordinates
|
|
related bank/org persistence. Purchased units are fulfilled by spawning the
|
|
granted unit classes at discovered `unit_spawn` markers after the backend
|
|
charge succeeds.
|
|
|
|
## Mission Catalog Filter
|
|
Missions can include `CfgStore.hpp` from `description.ext` to control the
|
|
generated catalog without changing the addon.
|
|
|
|
```cpp
|
|
class CfgStore {
|
|
mode = "allowlist"; // dynamic, allowlist, or denylist
|
|
modMode = "dynamic"; // dynamic, allowlist, or denylist
|
|
mods[] = {}; // ModSources class names used when modMode is not dynamic
|
|
|
|
class ModSources {
|
|
class rhs {
|
|
patches[] = {"rhs_main", "rhsusf_main"};
|
|
addons[] = {"rhs_", "rhsusf_", "rhsgref_", "rhsafrf_"};
|
|
prefixes[] = {"rhs_", "rhsusf_", "rhsgref_", "rhsafrf_"};
|
|
contains[] = {"rhs_", "rhsusf_", "rhsgref_", "rhsafrf_"};
|
|
dlcs[] = {};
|
|
};
|
|
|
|
class ace3 {
|
|
patches[] = {"ace_main"};
|
|
addons[] = {"ace_"};
|
|
prefixes[] = {"ace_"};
|
|
contains[] = {"ace_"};
|
|
dlcs[] = {};
|
|
};
|
|
};
|
|
|
|
class Categories {
|
|
primary[] = {"arifle_MX_F", "arifle_MXC_F"};
|
|
cars[] = {"B_MRAP_01_F"};
|
|
units[] = {"B_Soldier_F"};
|
|
};
|
|
|
|
class Overrides {
|
|
class arifle_MX_F {
|
|
price = 2500;
|
|
displayName = "MX Rifle";
|
|
description = "Approved PMC service rifle.";
|
|
};
|
|
};
|
|
};
|
|
```
|
|
|
|
`modMode` is applied first. After that, any non-empty
|
|
`Categories.<category>[]` array acts as an explicit allowlist for only that
|
|
category, regardless of `mode`. Categories with empty arrays follow `mode`:
|
|
`dynamic` keeps generated entries and `allowlist` hides the category. Overrides
|
|
are applied server-side, so checkout validation uses the same prices and
|
|
descriptions the UI displays.
|
|
|
|
`modMode` applies before category filtering. `dynamic` means no mod-source
|
|
filtering. `allowlist` only keeps generated entries that match one of the
|
|
configured `mods[]`; `denylist` removes matching entries. Each `ModSources`
|
|
child can define `patches[]` to detect whether the mod is loaded, `addons[]`
|
|
for exact config source addon/source mod names, `prefixes[]` for classname,
|
|
source addon, or source mod prefixes, `contains[]` for classname/source
|
|
metadata tokens that can appear anywhere, and `dlcs[]` for DLC/source/author
|
|
labels used by Creator DLC content. If a mod source defines no patches, it is
|
|
treated as available and only the source/prefix/contains/DLC checks are used.
|
|
|
|
Unit catalog responses are additionally filtered to the requesting player's
|
|
side. Unit entries include side and faction metadata for UI display, and
|
|
checkout validation rejects unit classnames from another side. Unit purchases
|
|
are immediate spawn grants, not durable virtual garage unlocks.
|
|
|
|
The filter is currently global for the mission. Revisit per-store profile
|
|
support if individual vendors need different inventories.
|
|
|
|
## Unit Spawn Markers
|
|
Purchased units spawn at mission markers named `unit_spawn`, `unit_spawn_1`,
|
|
`unit_spawn_2`, and so on. The store resolves the closest initialized store
|
|
object to the requesting player, scans `allMapMarkers` at fulfillment time, and
|
|
uses the closest matching marker within 25 meters of that store.
|
|
|
|
If no matching marker exists within 25 meters, the store falls back to spawning
|
|
units around the store object. If no store object can be resolved, it falls back
|
|
to the requesting player so checkout still completes.
|
|
|
|
## Editor Entities
|
|
`fnc_initStore` matches non-null mission namespace objects whose variable names
|
|
contain `store`, mirroring the garage entity initialization pattern.
|
|
|
|
## Checkout Flow
|
|
Store checkout can charge cash, bank balance, organization funds, or approved
|
|
credit lines depending on the hydrated session context. Checkout results can
|
|
grant locker assets, organization assets, fleet vehicles, and immediate unit
|
|
spawns through the related domain stores and Arma server runtime.
|
|
|
|
Checkout results emit notifications and syncs through the event bus:
|
|
- `notification.requested` - receipt and transaction alerts
|
|
- `bank.account.sync.requested` - player balance updates
|
|
- `org.sync.requested` - organization balance and asset updates
|
|
- `locker.sync.requested` - item grant notifications
|
|
- `garage.vgarage.sync.requested` - vehicle grant notifications
|