feat: Implement task store reset functionality and update documentation for server launch prerequisites
This commit is contained in:
parent
b7f7ae3a01
commit
96718996b2
@ -1,5 +1,6 @@
|
|||||||
#include "script_component.hpp"
|
#include "script_component.hpp"
|
||||||
|
|
||||||
|
if !(isNil QGVAR(TaskStore)) then { GVAR(TaskStore) call ["resetMissionState", []]; };
|
||||||
if (isNil QEGVAR(common,EventBus)) then { call EFUNC(common,eventBus); };
|
if (isNil QEGVAR(common,EventBus)) then { call EFUNC(common,eventBus); };
|
||||||
if (isNil QGVAR(TaskLifecycleEventLogTokens)) then {
|
if (isNil QGVAR(TaskLifecycleEventLogTokens)) then {
|
||||||
private _logTaskLifecycleEvent = {
|
private _logTaskLifecycleEvent = {
|
||||||
|
|||||||
@ -37,6 +37,21 @@ GVAR(TaskStore) = createHashMapObject [[
|
|||||||
["targets", createHashMap]
|
["targets", createHashMap]
|
||||||
]];
|
]];
|
||||||
|
|
||||||
|
_self call ["resetMissionState", []];
|
||||||
|
}],
|
||||||
|
["resetMissionState", compileFinal {
|
||||||
|
_self set ["participantRegistry", createHashMap];
|
||||||
|
_self set ["taskLifecycleRegistry", createHashMap];
|
||||||
|
_self set ["taskEntityRegistries", createHashMapFromArray [
|
||||||
|
["cargo", createHashMap],
|
||||||
|
["hostages", createHashMap],
|
||||||
|
["hvts", createHashMap],
|
||||||
|
["ieds", createHashMap],
|
||||||
|
["entities", createHashMap],
|
||||||
|
["shooters", createHashMap],
|
||||||
|
["targets", createHashMap]
|
||||||
|
]];
|
||||||
|
|
||||||
// Task extension state is mission-scoped and intentionally reset on
|
// Task extension state is mission-scoped and intentionally reset on
|
||||||
// startup rather than being treated as durable account data.
|
// startup rather than being treated as durable account data.
|
||||||
["task:reset", []] call EFUNC(extension,extCall) params ["_result", "_isSuccess"];
|
["task:reset", []] call EFUNC(extension,extCall) params ["_result", "_isSuccess"];
|
||||||
@ -44,9 +59,12 @@ GVAR(TaskStore) = createHashMapObject [[
|
|||||||
!_isSuccess
|
!_isSuccess
|
||||||
|| { !(_result isEqualType "") }
|
|| { !(_result isEqualType "") }
|
||||||
|| { (_result find "Error:") == 0 }
|
|| { (_result find "Error:") == 0 }
|
||||||
) then {
|
) exitWith {
|
||||||
["WARNING", "Failed to reset task backend state during task store initialization."] call EFUNC(common,log);
|
["WARNING", "Failed to reset task backend state during task store initialization."] call EFUNC(common,log);
|
||||||
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
true
|
||||||
}],
|
}],
|
||||||
["callTaskStateEnvelope", compileFinal {
|
["callTaskStateEnvelope", compileFinal {
|
||||||
params [["_function", "", [""]], ["_arguments", [], [[]]]];
|
params [["_function", "", [""]], ["_arguments", [], [[]]]];
|
||||||
|
|||||||
@ -89,6 +89,18 @@ GVAR(AttackTaskBaseClass) = createHashMapFromArray [
|
|||||||
private _targets = _self getOrDefault ["targets", []];
|
private _targets = _self getOrDefault ["targets", []];
|
||||||
{ !alive _x } count _targets
|
{ !alive _x } count _targets
|
||||||
}],
|
}],
|
||||||
|
["waitForAssignment", compileFinal {
|
||||||
|
private _taskID = _self getOrDefault ["taskID", ""];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "" || { !(_self getOrDefault ["useTaskStore", false]) }) exitWith { true };
|
||||||
|
|
||||||
|
waitUntil {
|
||||||
|
sleep 1;
|
||||||
|
GVAR(TaskStore) call ["isTaskAccepted", [_taskID]]
|
||||||
|
};
|
||||||
|
|
||||||
|
true
|
||||||
|
}],
|
||||||
["tick", compileFinal {
|
["tick", compileFinal {
|
||||||
private _startedAt = _self getOrDefault ["startedAt", -1];
|
private _startedAt = _self getOrDefault ["startedAt", -1];
|
||||||
private _timeLimit = _self getOrDefault ["timeLimit", 0];
|
private _timeLimit = _self getOrDefault ["timeLimit", 0];
|
||||||
@ -136,26 +148,7 @@ GVAR(AttackTaskBaseClass) = createHashMapFromArray [
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
if (_timeLimit isNotEqualTo 0 && { _useTaskStore }) then {
|
_self call ["waitForAssignment", []];
|
||||||
private _catalogEntry = GVAR(TaskStore) call ["getTaskCatalogEntry", [_taskID]];
|
|
||||||
["INFO", format [
|
|
||||||
"Attack task %1 initial state before acceptance wait. Accepted=%2, RequesterUid='%3', Source='%4', TimeLimit=%5s",
|
|
||||||
_taskID,
|
|
||||||
_catalogEntry getOrDefault ["accepted", false],
|
|
||||||
_catalogEntry getOrDefault ["requesterUid", ""],
|
|
||||||
_catalogEntry getOrDefault ["source", ""],
|
|
||||||
_timeLimit
|
|
||||||
]] call EFUNC(common,log);
|
|
||||||
|
|
||||||
["INFO", format ["Attack task %1 waiting for acceptance before starting %2s time limit.", _taskID, _timeLimit]] call EFUNC(common,log);
|
|
||||||
waitUntil {
|
|
||||||
sleep 1;
|
|
||||||
GVAR(TaskStore) call ["isTaskAccepted", [_taskID]]
|
|
||||||
};
|
|
||||||
|
|
||||||
["INFO", format ["Attack task %1 accepted. Starting %2s time limit.", _taskID, _timeLimit]] call EFUNC(common,log);
|
|
||||||
};
|
|
||||||
|
|
||||||
_self call ["markActive", []];
|
_self call ["markActive", []];
|
||||||
|
|
||||||
while { (_self call ["getStatus", []]) isEqualTo "active" } do {
|
while { (_self call ["getStatus", []]) isEqualTo "active" } do {
|
||||||
|
|||||||
@ -114,6 +114,18 @@ GVAR(DefuseTaskBaseClass) = createHashMapFromArray [
|
|||||||
|
|
||||||
true
|
true
|
||||||
}],
|
}],
|
||||||
|
["waitForAssignment", compileFinal {
|
||||||
|
private _taskID = _self getOrDefault ["taskID", ""];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "" || { !(_self getOrDefault ["useTaskStore", false]) }) exitWith { true };
|
||||||
|
|
||||||
|
waitUntil {
|
||||||
|
sleep 1;
|
||||||
|
GVAR(TaskStore) call ["isTaskAccepted", [_taskID]]
|
||||||
|
};
|
||||||
|
|
||||||
|
true
|
||||||
|
}],
|
||||||
["startIedControllers", compileFinal {
|
["startIedControllers", compileFinal {
|
||||||
if ((_self getOrDefault ["iedControllers", []]) isNotEqualTo []) exitWith { true };
|
if ((_self getOrDefault ["iedControllers", []]) isNotEqualTo []) exitWith { true };
|
||||||
|
|
||||||
@ -234,6 +246,7 @@ GVAR(DefuseTaskBaseClass) = createHashMapFromArray [
|
|||||||
}],
|
}],
|
||||||
["runLoop", compileFinal {
|
["runLoop", compileFinal {
|
||||||
_self call ["waitForRequiredEntities", []];
|
_self call ["waitForRequiredEntities", []];
|
||||||
|
_self call ["waitForAssignment", []];
|
||||||
_self call ["startIedControllers", []];
|
_self call ["startIedControllers", []];
|
||||||
_self call ["markActive", []];
|
_self call ["markActive", []];
|
||||||
|
|
||||||
|
|||||||
@ -78,11 +78,10 @@ GVAR(DeliveryTaskBaseClass) = createHashMapFromArray [
|
|||||||
_self set ["maxDamaged", _maxDamaged];
|
_self set ["maxDamaged", _maxDamaged];
|
||||||
true
|
true
|
||||||
}],
|
}],
|
||||||
["waitForAssignmentIfTimed", compileFinal {
|
["waitForAssignment", compileFinal {
|
||||||
private _timeLimit = _self getOrDefault ["timeLimit", 0];
|
|
||||||
private _taskID = _self getOrDefault ["taskID", ""];
|
private _taskID = _self getOrDefault ["taskID", ""];
|
||||||
|
|
||||||
if (_timeLimit <= 0 || { _taskID isEqualTo "" } || { !(_self getOrDefault ["useTaskStore", false]) }) exitWith { true };
|
if (_taskID isEqualTo "" || { !(_self getOrDefault ["useTaskStore", false]) }) exitWith { true };
|
||||||
|
|
||||||
waitUntil {
|
waitUntil {
|
||||||
sleep 1;
|
sleep 1;
|
||||||
@ -175,7 +174,7 @@ GVAR(DeliveryTaskBaseClass) = createHashMapFromArray [
|
|||||||
}],
|
}],
|
||||||
["runLoop", compileFinal {
|
["runLoop", compileFinal {
|
||||||
_self call ["waitForRequiredEntities", []];
|
_self call ["waitForRequiredEntities", []];
|
||||||
_self call ["waitForAssignmentIfTimed", []];
|
_self call ["waitForAssignment", []];
|
||||||
_self call ["markActive", []];
|
_self call ["markActive", []];
|
||||||
|
|
||||||
while { (_self call ["getStatus", []]) isEqualTo "active" } do {
|
while { (_self call ["getStatus", []]) isEqualTo "active" } do {
|
||||||
|
|||||||
@ -69,11 +69,10 @@ GVAR(DestroyTaskBaseClass) = createHashMapFromArray [
|
|||||||
_self set ["requiredDestroyed", _requiredDestroyed];
|
_self set ["requiredDestroyed", _requiredDestroyed];
|
||||||
true
|
true
|
||||||
}],
|
}],
|
||||||
["waitForAssignmentIfTimed", compileFinal {
|
["waitForAssignment", compileFinal {
|
||||||
private _timeLimit = _self getOrDefault ["timeLimit", 0];
|
|
||||||
private _taskID = _self getOrDefault ["taskID", ""];
|
private _taskID = _self getOrDefault ["taskID", ""];
|
||||||
|
|
||||||
if (_timeLimit <= 0 || { _taskID isEqualTo "" } || { !(_self getOrDefault ["useTaskStore", false]) }) exitWith { true };
|
if (_taskID isEqualTo "" || { !(_self getOrDefault ["useTaskStore", false]) }) exitWith { true };
|
||||||
|
|
||||||
waitUntil {
|
waitUntil {
|
||||||
sleep 1;
|
sleep 1;
|
||||||
@ -155,7 +154,7 @@ GVAR(DestroyTaskBaseClass) = createHashMapFromArray [
|
|||||||
}],
|
}],
|
||||||
["runLoop", compileFinal {
|
["runLoop", compileFinal {
|
||||||
_self call ["waitForRequiredEntities", []];
|
_self call ["waitForRequiredEntities", []];
|
||||||
_self call ["waitForAssignmentIfTimed", []];
|
_self call ["waitForAssignment", []];
|
||||||
_self call ["markActive", []];
|
_self call ["markActive", []];
|
||||||
|
|
||||||
while { (_self call ["getStatus", []]) isEqualTo "active" } do {
|
while { (_self call ["getStatus", []]) isEqualTo "active" } do {
|
||||||
|
|||||||
@ -37,7 +37,7 @@ GVAR(HVTEntityController) = createHashMapFromArray [
|
|||||||
if (isNull _entity || { !alive _entity }) exitWith { false };
|
if (isNull _entity || { !alive _entity }) exitWith { false };
|
||||||
|
|
||||||
_entity setCaptive true;
|
_entity setCaptive true;
|
||||||
doStop _entity;
|
_entity enableAIFeature ["MOVE", true];
|
||||||
true
|
true
|
||||||
}],
|
}],
|
||||||
["runLoop", compileFinal {
|
["runLoop", compileFinal {
|
||||||
|
|||||||
@ -115,11 +115,10 @@ GVAR(HVTTaskBaseClass) = createHashMapFromArray [
|
|||||||
_self set ["hvtControllers", _controllers];
|
_self set ["hvtControllers", _controllers];
|
||||||
true
|
true
|
||||||
}],
|
}],
|
||||||
["waitForAssignmentIfTimed", compileFinal {
|
["waitForAssignment", compileFinal {
|
||||||
private _timeLimit = _self getOrDefault ["timeLimit", 0];
|
|
||||||
private _taskID = _self getOrDefault ["taskID", ""];
|
private _taskID = _self getOrDefault ["taskID", ""];
|
||||||
|
|
||||||
if (_timeLimit <= 0 || { _taskID isEqualTo "" } || { !(_self getOrDefault ["useTaskStore", false]) }) exitWith { true };
|
if (_taskID isEqualTo "" || { !(_self getOrDefault ["useTaskStore", false]) }) exitWith { true };
|
||||||
|
|
||||||
waitUntil {
|
waitUntil {
|
||||||
sleep 1;
|
sleep 1;
|
||||||
@ -210,8 +209,8 @@ GVAR(HVTTaskBaseClass) = createHashMapFromArray [
|
|||||||
}],
|
}],
|
||||||
["runLoop", compileFinal {
|
["runLoop", compileFinal {
|
||||||
_self call ["waitForRequiredEntities", []];
|
_self call ["waitForRequiredEntities", []];
|
||||||
|
_self call ["waitForAssignment", []];
|
||||||
_self call ["startHvtControllers", []];
|
_self call ["startHvtControllers", []];
|
||||||
_self call ["waitForAssignmentIfTimed", []];
|
|
||||||
_self call ["markActive", []];
|
_self call ["markActive", []];
|
||||||
|
|
||||||
while { (_self call ["getStatus", []]) isEqualTo "active" } do {
|
while { (_self call ["getStatus", []]) isEqualTo "active" } do {
|
||||||
|
|||||||
@ -183,11 +183,10 @@ GVAR(HostageTaskBaseClass) = createHashMapFromArray [
|
|||||||
_self set ["maxHostageLosses", _maxHostageLosses];
|
_self set ["maxHostageLosses", _maxHostageLosses];
|
||||||
true
|
true
|
||||||
}],
|
}],
|
||||||
["waitForAssignmentIfTimed", compileFinal {
|
["waitForAssignment", compileFinal {
|
||||||
private _timeLimit = _self getOrDefault ["timeLimit", 0];
|
|
||||||
private _taskID = _self getOrDefault ["taskID", ""];
|
private _taskID = _self getOrDefault ["taskID", ""];
|
||||||
|
|
||||||
if (_timeLimit <= 0 || { _taskID isEqualTo "" } || { !(_self getOrDefault ["useTaskStore", false]) }) exitWith { true };
|
if (_taskID isEqualTo "" || { !(_self getOrDefault ["useTaskStore", false]) }) exitWith { true };
|
||||||
|
|
||||||
waitUntil {
|
waitUntil {
|
||||||
sleep 1;
|
sleep 1;
|
||||||
@ -337,8 +336,8 @@ GVAR(HostageTaskBaseClass) = createHashMapFromArray [
|
|||||||
}],
|
}],
|
||||||
["runLoop", compileFinal {
|
["runLoop", compileFinal {
|
||||||
_self call ["waitForRequiredEntities", []];
|
_self call ["waitForRequiredEntities", []];
|
||||||
|
_self call ["waitForAssignment", []];
|
||||||
_self call ["startHostageControllers", []];
|
_self call ["startHostageControllers", []];
|
||||||
_self call ["waitForAssignmentIfTimed", []];
|
|
||||||
_self call ["markActive", []];
|
_self call ["markActive", []];
|
||||||
|
|
||||||
while { (_self call ["getStatus", []]) isEqualTo "active" } do {
|
while { (_self call ["getStatus", []]) isEqualTo "active" } do {
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
# Forge Server Configuration
|
# Forge Server Configuration
|
||||||
# Copy this file to config.toml and place it beside forge_server_x64.dll.
|
# Copy this file to config.toml and place it beside forge_server_x64.dll.
|
||||||
|
# Start SurrealDB before launching the Arma server, and keep these values
|
||||||
|
# aligned with the running database.
|
||||||
|
|
||||||
[surreal]
|
[surreal]
|
||||||
endpoint = "127.0.0.1:8000"
|
endpoint = "127.0.0.1:8000"
|
||||||
|
|||||||
@ -21,7 +21,9 @@ SQF module
|
|||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
Copy `config.example.toml` to `config.toml` next to the extension DLL.
|
Copy `config.example.toml` to `config.toml` next to the extension DLL before
|
||||||
|
launching a Forge-enabled server. SurrealDB must also be running before the
|
||||||
|
server starts, and the values in `config.toml` must match that database.
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[surreal]
|
[surreal]
|
||||||
@ -33,6 +35,9 @@ password = "root"
|
|||||||
connect_timeout_ms = 5000
|
connect_timeout_ms = 5000
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Players and mission designers do not need this file unless they are hosting
|
||||||
|
locally. Server owners and developers do.
|
||||||
|
|
||||||
For install links and Forge-specific setup steps, see
|
For install links and Forge-specific setup steps, see
|
||||||
[SurrealDB Setup](../../../docs/surrealdb-setup.md).
|
[SurrealDB Setup](../../../docs/surrealdb-setup.md).
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,19 @@ persists durable state through SurrealDB.
|
|||||||
|
|
||||||
This extension build targets SurrealDB `3.x`.
|
This extension build targets SurrealDB `3.x`.
|
||||||
|
|
||||||
|
## Launch Prerequisites
|
||||||
|
|
||||||
|
Before starting the Arma server with Forge enabled:
|
||||||
|
|
||||||
|
1. Start SurrealDB.
|
||||||
|
2. Copy `config.example.toml` to `config.toml` beside `forge_server_x64.dll`.
|
||||||
|
3. Match the `config.toml` endpoint, namespace, database, username, and password
|
||||||
|
to the running SurrealDB instance.
|
||||||
|
|
||||||
|
The extension reads configuration during startup. If SurrealDB is offline or
|
||||||
|
the config values do not match, persistence-backed commands are not ready for
|
||||||
|
normal gameplay.
|
||||||
|
|
||||||
## Responsibilities
|
## Responsibilities
|
||||||
|
|
||||||
- Register extension command groups for actor, bank, garage, locker, org,
|
- Register extension command groups for actor, bank, garage, locker, org,
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
# Forge Server Configuration
|
# Forge Server Configuration
|
||||||
# Copy this file to config.toml and place it beside forge_server_x64.dll.
|
# Copy this file to config.toml and place it beside forge_server_x64.dll.
|
||||||
|
# Start SurrealDB before launching the Arma server, and keep these values
|
||||||
|
# aligned with the running database.
|
||||||
|
|
||||||
[surreal]
|
[surreal]
|
||||||
# SurrealDB HTTP endpoint. Use "127.0.0.1:8000" for a local server.
|
# SurrealDB HTTP endpoint. Use "127.0.0.1:8000" for a local server.
|
||||||
|
|||||||
@ -5,7 +5,9 @@ This guide covers the usual path for adding or changing a Forge module.
|
|||||||
## Local Checks
|
## Local Checks
|
||||||
|
|
||||||
Before running storage-backed workflows locally, complete
|
Before running storage-backed workflows locally, complete
|
||||||
[SurrealDB Setup](./surrealdb-setup.md).
|
[SurrealDB Setup](./surrealdb-setup.md). A local or dedicated server launch must
|
||||||
|
have SurrealDB running and a `config.toml` beside `forge_server_x64.dll` that
|
||||||
|
matches the running database.
|
||||||
|
|
||||||
Run these before pushing Rust or extension changes:
|
Run these before pushing Rust or extension changes:
|
||||||
|
|
||||||
|
|||||||
@ -127,6 +127,13 @@ password = "root"
|
|||||||
connect_timeout_ms = 5000
|
connect_timeout_ms = 5000
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`config.toml` is a launch prerequisite for server owners and developers. The
|
||||||
|
file must exist beside `forge_server_x64.dll`, and SurrealDB must already be
|
||||||
|
running at the configured endpoint before starting a Forge-enabled dedicated
|
||||||
|
server or local multiplayer test. Clients and mission designers do not run this
|
||||||
|
configuration unless they are hosting locally, but the server they connect to
|
||||||
|
must have it in place.
|
||||||
|
|
||||||
For install links and role-based setup guidance, see
|
For install links and role-based setup guidance, see
|
||||||
[SurrealDB Setup](./surrealdb-setup.md).
|
[SurrealDB Setup](./surrealdb-setup.md).
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,20 @@ Forge is split into Arma client addons, Arma server addons, a Rust server
|
|||||||
extension, shared Rust domain crates, and web UI build tooling. This directory
|
extension, shared Rust domain crates, and web UI build tooling. This directory
|
||||||
collects framework-level documentation for those pieces.
|
collects framework-level documentation for those pieces.
|
||||||
|
|
||||||
|
## Launch Prerequisites
|
||||||
|
|
||||||
|
Before starting a Forge-enabled dedicated server or local multiplayer test,
|
||||||
|
server owners and developers must:
|
||||||
|
|
||||||
|
1. Start SurrealDB.
|
||||||
|
2. Place `config.toml` beside `forge_server_x64.dll`.
|
||||||
|
3. Keep the `config.toml` SurrealDB endpoint, namespace, database, username,
|
||||||
|
and password aligned with the running database.
|
||||||
|
|
||||||
|
Mission designers and players do not need to run SurrealDB unless they are
|
||||||
|
hosting locally, but the server they join must have these prerequisites ready.
|
||||||
|
See [SurrealDB Setup](./surrealdb-setup.md) for the full setup path.
|
||||||
|
|
||||||
## Start Here
|
## Start Here
|
||||||
|
|
||||||
- [Framework Architecture](./FRAMEWORK_ARCHITECTURE.md): how SQF, web UIs,
|
- [Framework Architecture](./FRAMEWORK_ARCHITECTURE.md): how SQF, web UIs,
|
||||||
|
|||||||
@ -4,6 +4,26 @@ Forge uses SurrealDB for durable storage. The Rust server extension connects to
|
|||||||
SurrealDB on startup and applies Forge schema modules automatically, so setup
|
SurrealDB on startup and applies Forge schema modules automatically, so setup
|
||||||
comes down to running a reachable database and matching the Forge config.
|
comes down to running a reachable database and matching the Forge config.
|
||||||
|
|
||||||
|
## Launch Requirement
|
||||||
|
|
||||||
|
Before launching an Arma server or local multiplayer test with Forge enabled:
|
||||||
|
|
||||||
|
1. Start SurrealDB and confirm it is listening on the endpoint Forge will use.
|
||||||
|
2. Copy `arma/server/extension/config.example.toml` to `config.toml` beside
|
||||||
|
`forge_server_x64.dll`.
|
||||||
|
3. Make sure `config.toml` matches the running SurrealDB endpoint, namespace,
|
||||||
|
database, username, and password.
|
||||||
|
|
||||||
|
Server owners and developers must do this before starting the dedicated server
|
||||||
|
or hosting a test session. Mission designers and players do not need their own
|
||||||
|
SurrealDB instance unless they are running the server locally, but the server
|
||||||
|
they connect to must have SurrealDB running and configured.
|
||||||
|
|
||||||
|
If SurrealDB is not running, or if `config.toml` points at the wrong endpoint
|
||||||
|
or credentials, persistence-backed systems such as actors, bank accounts,
|
||||||
|
garages, lockers, organizations, phone data, stores, and tasks will not be
|
||||||
|
ready for normal gameplay.
|
||||||
|
|
||||||
## Choose the Right Path
|
## Choose the Right Path
|
||||||
|
|
||||||
### Developer or Server Operator
|
### Developer or Server Operator
|
||||||
@ -73,11 +93,11 @@ password = "root"
|
|||||||
connect_timeout_ms = 5000
|
connect_timeout_ms = 5000
|
||||||
```
|
```
|
||||||
|
|
||||||
After that:
|
Before starting the game server, confirm SurrealDB is still running. After
|
||||||
|
launching the Arma server:
|
||||||
|
|
||||||
1. Start the Arma server with the Forge extension enabled.
|
1. Let the extension connect and apply the Forge schema modules.
|
||||||
2. Let the extension connect and apply the Forge schema modules.
|
2. Verify the connection state:
|
||||||
3. Verify the connection state:
|
|
||||||
|
|
||||||
```sqf
|
```sqf
|
||||||
"forge_server" callExtension ["status", []];
|
"forge_server" callExtension ["status", []];
|
||||||
|
|||||||
@ -4,6 +4,26 @@ Forge uses SurrealDB for durable storage. The Rust server extension connects to
|
|||||||
SurrealDB on startup and applies Forge schema modules automatically, so setup
|
SurrealDB on startup and applies Forge schema modules automatically, so setup
|
||||||
comes down to running a reachable database and matching the Forge config.
|
comes down to running a reachable database and matching the Forge config.
|
||||||
|
|
||||||
|
## Launch Requirement
|
||||||
|
|
||||||
|
Before launching an Arma server or local multiplayer test with Forge enabled:
|
||||||
|
|
||||||
|
1. Start SurrealDB and confirm it is listening on the endpoint Forge will use.
|
||||||
|
2. Copy `arma/server/extension/config.example.toml` to `config.toml` beside
|
||||||
|
`forge_server_x64.dll`.
|
||||||
|
3. Make sure `config.toml` matches the running SurrealDB endpoint, namespace,
|
||||||
|
database, username, and password.
|
||||||
|
|
||||||
|
Server owners and developers must do this before starting the dedicated server
|
||||||
|
or hosting a test session. Mission designers and players do not need their own
|
||||||
|
SurrealDB instance unless they are running the server locally, but the server
|
||||||
|
they connect to must have SurrealDB running and configured.
|
||||||
|
|
||||||
|
If SurrealDB is not running, or if `config.toml` points at the wrong endpoint
|
||||||
|
or credentials, persistence-backed systems such as actors, bank accounts,
|
||||||
|
garages, lockers, organizations, phone data, stores, and tasks will not be
|
||||||
|
ready for normal gameplay.
|
||||||
|
|
||||||
## Choose the Right Path
|
## Choose the Right Path
|
||||||
|
|
||||||
### Developer or Server Operator
|
### Developer or Server Operator
|
||||||
@ -73,11 +93,11 @@ password = "root"
|
|||||||
connect_timeout_ms = 5000
|
connect_timeout_ms = 5000
|
||||||
```
|
```
|
||||||
|
|
||||||
After that:
|
Before starting the game server, confirm SurrealDB is still running. After
|
||||||
|
launching the Arma server:
|
||||||
|
|
||||||
1. Start the Arma server with the Forge extension enabled.
|
1. Let the extension connect and apply the Forge schema modules.
|
||||||
2. Let the extension connect and apply the Forge schema modules.
|
2. Verify the connection state:
|
||||||
3. Verify the connection state:
|
|
||||||
|
|
||||||
```sqf
|
```sqf
|
||||||
"forge_server" callExtension ["status", []];
|
"forge_server" callExtension ["status", []];
|
||||||
|
|||||||
@ -11,6 +11,17 @@ Forge combines:
|
|||||||
- shared Rust crates for models, repositories, and services
|
- shared Rust crates for models, repositories, and services
|
||||||
- SurrealDB for durable storage
|
- SurrealDB for durable storage
|
||||||
|
|
||||||
|
## Launch Prerequisites
|
||||||
|
|
||||||
|
Before starting a Forge-enabled dedicated server or local multiplayer test,
|
||||||
|
server owners and developers must start SurrealDB and make sure
|
||||||
|
`config.toml` is beside `forge_server_x64.dll`. The config values must match
|
||||||
|
the running SurrealDB endpoint, namespace, database, username, and password.
|
||||||
|
|
||||||
|
Mission designers and players do not need their own SurrealDB instance unless
|
||||||
|
they are hosting locally, but the server they join must have these prerequisites
|
||||||
|
ready.
|
||||||
|
|
||||||
## Common Commands
|
## Common Commands
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
|
|||||||
@ -126,6 +126,13 @@ password = "root"
|
|||||||
connect_timeout_ms = 5000
|
connect_timeout_ms = 5000
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`config.toml` is a launch prerequisite for server owners and developers. The
|
||||||
|
file must exist beside `forge_server_x64.dll`, and SurrealDB must already be
|
||||||
|
running at the configured endpoint before starting a Forge-enabled dedicated
|
||||||
|
server or local multiplayer test. Clients and mission designers do not run this
|
||||||
|
configuration unless they are hosting locally, but the server they connect to
|
||||||
|
must have it in place.
|
||||||
|
|
||||||
For install links and role-based setup guidance, see
|
For install links and role-based setup guidance, see
|
||||||
[SurrealDB Setup](/getting-started/surrealdb-setup).
|
[SurrealDB Setup](/getting-started/surrealdb-setup).
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,9 @@ description: "This guide covers the usual path for adding or changing a Forge mo
|
|||||||
## Local Checks
|
## Local Checks
|
||||||
|
|
||||||
Before running storage-backed workflows locally, complete
|
Before running storage-backed workflows locally, complete
|
||||||
[SurrealDB Setup](/getting-started/surrealdb-setup).
|
[SurrealDB Setup](/getting-started/surrealdb-setup). A local or dedicated server launch must
|
||||||
|
have SurrealDB running and a `config.toml` beside `forge_server_x64.dll` that
|
||||||
|
matches the running database.
|
||||||
|
|
||||||
Run these before pushing Rust or extension changes:
|
Run these before pushing Rust or extension changes:
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,26 @@ title: "SurrealDB Setup"
|
|||||||
description: "Forge uses SurrealDB for durable storage. The Rust server extension connects to SurrealDB on startup and applies Forge schema modules automatically, so setup comes down to running a reachable database and matching the Forge config."
|
description: "Forge uses SurrealDB for durable storage. The Rust server extension connects to SurrealDB on startup and applies Forge schema modules automatically, so setup comes down to running a reachable database and matching the Forge config."
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Launch Requirement
|
||||||
|
|
||||||
|
Before launching an Arma server or local multiplayer test with Forge enabled:
|
||||||
|
|
||||||
|
1. Start SurrealDB and confirm it is listening on the endpoint Forge will use.
|
||||||
|
2. Copy `arma/server/extension/config.example.toml` to `config.toml` beside
|
||||||
|
`forge_server_x64.dll`.
|
||||||
|
3. Make sure `config.toml` matches the running SurrealDB endpoint, namespace,
|
||||||
|
database, username, and password.
|
||||||
|
|
||||||
|
Server owners and developers must do this before starting the dedicated server
|
||||||
|
or hosting a test session. Mission designers and players do not need their own
|
||||||
|
SurrealDB instance unless they are running the server locally, but the server
|
||||||
|
they connect to must have SurrealDB running and configured.
|
||||||
|
|
||||||
|
If SurrealDB is not running, or if `config.toml` points at the wrong endpoint
|
||||||
|
or credentials, persistence-backed systems such as actors, bank accounts,
|
||||||
|
garages, lockers, organizations, phone data, stores, and tasks will not be
|
||||||
|
ready for normal gameplay.
|
||||||
|
|
||||||
## Choose the Right Path
|
## Choose the Right Path
|
||||||
|
|
||||||
### Developer or Server Operator
|
### Developer or Server Operator
|
||||||
@ -72,11 +92,11 @@ password = "root"
|
|||||||
connect_timeout_ms = 5000
|
connect_timeout_ms = 5000
|
||||||
```
|
```
|
||||||
|
|
||||||
After that:
|
Before starting the game server, confirm SurrealDB is still running. After
|
||||||
|
launching the Arma server:
|
||||||
|
|
||||||
1. Start the Arma server with the Forge extension enabled.
|
1. Let the extension connect and apply the Forge schema modules.
|
||||||
2. Let the extension connect and apply the Forge schema modules.
|
2. Verify the connection state:
|
||||||
3. Verify the connection state:
|
|
||||||
|
|
||||||
```sqf
|
```sqf
|
||||||
"forge_server" callExtension ["status", []];
|
"forge_server" callExtension ["status", []];
|
||||||
|
|||||||
@ -20,7 +20,9 @@ SQF module
|
|||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
Copy `config.example.toml` to `config.toml` next to the extension DLL.
|
Copy `config.example.toml` to `config.toml` next to the extension DLL before
|
||||||
|
launching a Forge-enabled server. SurrealDB must also be running before the
|
||||||
|
server starts, and the values in `config.toml` must match that database.
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[surreal]
|
[surreal]
|
||||||
@ -32,6 +34,9 @@ password = "root"
|
|||||||
connect_timeout_ms = 5000
|
connect_timeout_ms = 5000
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Players and mission designers do not need this file unless they are hosting
|
||||||
|
locally. Server owners and developers do.
|
||||||
|
|
||||||
For install links and Forge-specific setup steps, see
|
For install links and Forge-specific setup steps, see
|
||||||
[SurrealDB Setup](/getting-started/surrealdb-setup).
|
[SurrealDB Setup](/getting-started/surrealdb-setup).
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,10 @@ browser-backed player interfaces.
|
|||||||
Use these docs to understand the runtime architecture, extension API surface,
|
Use these docs to understand the runtime architecture, extension API surface,
|
||||||
server gameplay modules, and client addon integration patterns.
|
server gameplay modules, and client addon integration patterns.
|
||||||
|
|
||||||
|
Server owners and developers must start SurrealDB and place a matching
|
||||||
|
`config.toml` beside `forge_server_x64.dll` before launching a
|
||||||
|
Forge-enabled server or local multiplayer test.
|
||||||
|
|
||||||
#links
|
#links
|
||||||
:::u-button
|
:::u-button
|
||||||
---
|
---
|
||||||
|
|||||||
@ -80,9 +80,10 @@ impl<R: TaskRepository> TaskStateService<R> {
|
|||||||
|
|
||||||
self.repository
|
self.repository
|
||||||
.save_ownership(entry_id.clone(), ownership.clone())?;
|
.save_ownership(entry_id.clone(), ownership.clone())?;
|
||||||
|
let accepted = !ownership.requester_uid.trim().is_empty();
|
||||||
let entry = self.patch_catalog_ownership(
|
let entry = self.patch_catalog_ownership(
|
||||||
&entry_id,
|
&entry_id,
|
||||||
true,
|
accepted,
|
||||||
&ownership.requester_uid,
|
&ownership.requester_uid,
|
||||||
&ownership.org_id,
|
&ownership.org_id,
|
||||||
)?;
|
)?;
|
||||||
@ -326,6 +327,39 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bind_ownership_without_requester_does_not_accept_task() {
|
||||||
|
let repository = InMemoryTaskRepository::new();
|
||||||
|
let service = TaskStateService::new(repository.clone());
|
||||||
|
|
||||||
|
service
|
||||||
|
.upsert_catalog_entry("task-1".to_string(), r#"{"title":"Hostage"}"#.to_string())
|
||||||
|
.expect("catalog upsert should succeed");
|
||||||
|
|
||||||
|
let result = service
|
||||||
|
.bind_ownership(
|
||||||
|
"task-1".to_string(),
|
||||||
|
r#"{"requesterUid":"","orgId":"default"}"#.to_string(),
|
||||||
|
)
|
||||||
|
.expect("bind should succeed");
|
||||||
|
|
||||||
|
assert_eq!(result.requester_uid, "");
|
||||||
|
assert_eq!(result.org_id, "default");
|
||||||
|
assert_eq!(
|
||||||
|
result.entry.get("accepted").and_then(Value::as_bool),
|
||||||
|
Some(false)
|
||||||
|
);
|
||||||
|
|
||||||
|
let stored = repository
|
||||||
|
.get_catalog_entry("task-1")
|
||||||
|
.expect("catalog lookup should succeed")
|
||||||
|
.expect("catalog entry should exist");
|
||||||
|
assert_eq!(
|
||||||
|
stored.fields.get("requesterUid").and_then(Value::as_str),
|
||||||
|
Some("")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn get_status_falls_back_to_completed_status() {
|
fn get_status_falls_back_to_completed_status() {
|
||||||
let repository = InMemoryTaskRepository::new();
|
let repository = InMemoryTaskRepository::new();
|
||||||
|
|||||||
@ -171,6 +171,10 @@ browser-backed player interfaces.
|
|||||||
Use these docs to understand the runtime architecture, extension API surface,
|
Use these docs to understand the runtime architecture, extension API surface,
|
||||||
server gameplay modules, and client addon integration patterns.
|
server gameplay modules, and client addon integration patterns.
|
||||||
|
|
||||||
|
Server owners and developers must start SurrealDB and place a matching
|
||||||
|
\`config.toml\` beside \`forge_server_x64.dll\` before launching a
|
||||||
|
Forge-enabled server or local multiplayer test.
|
||||||
|
|
||||||
#links
|
#links
|
||||||
:::u-button
|
:::u-button
|
||||||
---
|
---
|
||||||
@ -372,6 +376,17 @@ Forge combines:
|
|||||||
- shared Rust crates for models, repositories, and services
|
- shared Rust crates for models, repositories, and services
|
||||||
- SurrealDB for durable storage
|
- SurrealDB for durable storage
|
||||||
|
|
||||||
|
## Launch Prerequisites
|
||||||
|
|
||||||
|
Before starting a Forge-enabled dedicated server or local multiplayer test,
|
||||||
|
server owners and developers must start SurrealDB and make sure
|
||||||
|
\`config.toml\` is beside \`forge_server_x64.dll\`. The config values must match
|
||||||
|
the running SurrealDB endpoint, namespace, database, username, and password.
|
||||||
|
|
||||||
|
Mission designers and players do not need their own SurrealDB instance unless
|
||||||
|
they are hosting locally, but the server they join must have these prerequisites
|
||||||
|
ready.
|
||||||
|
|
||||||
## Common Commands
|
## Common Commands
|
||||||
|
|
||||||
\`\`\`powershell
|
\`\`\`powershell
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user