diff --git a/arma/server/addons/task/XEH_postInit.sqf b/arma/server/addons/task/XEH_postInit.sqf index 7666467..e93f034 100644 --- a/arma/server/addons/task/XEH_postInit.sqf +++ b/arma/server/addons/task/XEH_postInit.sqf @@ -1,5 +1,6 @@ #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 QGVAR(TaskLifecycleEventLogTokens)) then { private _logTaskLifecycleEvent = { diff --git a/arma/server/addons/task/functions/fnc_initTaskStore.sqf b/arma/server/addons/task/functions/fnc_initTaskStore.sqf index b8aef1c..cb48ada 100644 --- a/arma/server/addons/task/functions/fnc_initTaskStore.sqf +++ b/arma/server/addons/task/functions/fnc_initTaskStore.sqf @@ -37,6 +37,21 @@ GVAR(TaskStore) = createHashMapObject [[ ["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 // startup rather than being treated as durable account data. ["task:reset", []] call EFUNC(extension,extCall) params ["_result", "_isSuccess"]; @@ -44,9 +59,12 @@ GVAR(TaskStore) = createHashMapObject [[ !_isSuccess || { !(_result isEqualType "") } || { (_result find "Error:") == 0 } - ) then { + ) exitWith { ["WARNING", "Failed to reset task backend state during task store initialization."] call EFUNC(common,log); + false }; + + true }], ["callTaskStateEnvelope", compileFinal { params [["_function", "", [""]], ["_arguments", [], [[]]]]; diff --git a/arma/server/addons/task/functions/objects/fnc_AttackTaskBaseClass.sqf b/arma/server/addons/task/functions/objects/fnc_AttackTaskBaseClass.sqf index 7bbcfab..e640dd2 100644 --- a/arma/server/addons/task/functions/objects/fnc_AttackTaskBaseClass.sqf +++ b/arma/server/addons/task/functions/objects/fnc_AttackTaskBaseClass.sqf @@ -89,6 +89,18 @@ GVAR(AttackTaskBaseClass) = createHashMapFromArray [ private _targets = _self getOrDefault ["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 { private _startedAt = _self getOrDefault ["startedAt", -1]; private _timeLimit = _self getOrDefault ["timeLimit", 0]; @@ -136,26 +148,7 @@ GVAR(AttackTaskBaseClass) = createHashMapFromArray [ }; }; - if (_timeLimit isNotEqualTo 0 && { _useTaskStore }) then { - 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 ["waitForAssignment", []]; _self call ["markActive", []]; while { (_self call ["getStatus", []]) isEqualTo "active" } do { diff --git a/arma/server/addons/task/functions/objects/fnc_DefuseTaskBaseClass.sqf b/arma/server/addons/task/functions/objects/fnc_DefuseTaskBaseClass.sqf index 0c316c2..7ec5bc3 100644 --- a/arma/server/addons/task/functions/objects/fnc_DefuseTaskBaseClass.sqf +++ b/arma/server/addons/task/functions/objects/fnc_DefuseTaskBaseClass.sqf @@ -114,6 +114,18 @@ GVAR(DefuseTaskBaseClass) = createHashMapFromArray [ 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 { if ((_self getOrDefault ["iedControllers", []]) isNotEqualTo []) exitWith { true }; @@ -234,6 +246,7 @@ GVAR(DefuseTaskBaseClass) = createHashMapFromArray [ }], ["runLoop", compileFinal { _self call ["waitForRequiredEntities", []]; + _self call ["waitForAssignment", []]; _self call ["startIedControllers", []]; _self call ["markActive", []]; diff --git a/arma/server/addons/task/functions/objects/fnc_DeliveryTaskBaseClass.sqf b/arma/server/addons/task/functions/objects/fnc_DeliveryTaskBaseClass.sqf index 606c035..37ae6b5 100644 --- a/arma/server/addons/task/functions/objects/fnc_DeliveryTaskBaseClass.sqf +++ b/arma/server/addons/task/functions/objects/fnc_DeliveryTaskBaseClass.sqf @@ -78,11 +78,10 @@ GVAR(DeliveryTaskBaseClass) = createHashMapFromArray [ _self set ["maxDamaged", _maxDamaged]; true }], - ["waitForAssignmentIfTimed", compileFinal { - private _timeLimit = _self getOrDefault ["timeLimit", 0]; + ["waitForAssignment", compileFinal { 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 { sleep 1; @@ -175,7 +174,7 @@ GVAR(DeliveryTaskBaseClass) = createHashMapFromArray [ }], ["runLoop", compileFinal { _self call ["waitForRequiredEntities", []]; - _self call ["waitForAssignmentIfTimed", []]; + _self call ["waitForAssignment", []]; _self call ["markActive", []]; while { (_self call ["getStatus", []]) isEqualTo "active" } do { diff --git a/arma/server/addons/task/functions/objects/fnc_DestroyTaskBaseClass.sqf b/arma/server/addons/task/functions/objects/fnc_DestroyTaskBaseClass.sqf index 2ccea27..54961d0 100644 --- a/arma/server/addons/task/functions/objects/fnc_DestroyTaskBaseClass.sqf +++ b/arma/server/addons/task/functions/objects/fnc_DestroyTaskBaseClass.sqf @@ -69,11 +69,10 @@ GVAR(DestroyTaskBaseClass) = createHashMapFromArray [ _self set ["requiredDestroyed", _requiredDestroyed]; true }], - ["waitForAssignmentIfTimed", compileFinal { - private _timeLimit = _self getOrDefault ["timeLimit", 0]; + ["waitForAssignment", compileFinal { 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 { sleep 1; @@ -155,7 +154,7 @@ GVAR(DestroyTaskBaseClass) = createHashMapFromArray [ }], ["runLoop", compileFinal { _self call ["waitForRequiredEntities", []]; - _self call ["waitForAssignmentIfTimed", []]; + _self call ["waitForAssignment", []]; _self call ["markActive", []]; while { (_self call ["getStatus", []]) isEqualTo "active" } do { diff --git a/arma/server/addons/task/functions/objects/fnc_HVTEntityController.sqf b/arma/server/addons/task/functions/objects/fnc_HVTEntityController.sqf index fa26c09..1c162cd 100644 --- a/arma/server/addons/task/functions/objects/fnc_HVTEntityController.sqf +++ b/arma/server/addons/task/functions/objects/fnc_HVTEntityController.sqf @@ -37,7 +37,7 @@ GVAR(HVTEntityController) = createHashMapFromArray [ if (isNull _entity || { !alive _entity }) exitWith { false }; _entity setCaptive true; - doStop _entity; + _entity enableAIFeature ["MOVE", true]; true }], ["runLoop", compileFinal { diff --git a/arma/server/addons/task/functions/objects/fnc_HVTTaskBaseClass.sqf b/arma/server/addons/task/functions/objects/fnc_HVTTaskBaseClass.sqf index 53c9145..a93fdf7 100644 --- a/arma/server/addons/task/functions/objects/fnc_HVTTaskBaseClass.sqf +++ b/arma/server/addons/task/functions/objects/fnc_HVTTaskBaseClass.sqf @@ -115,11 +115,10 @@ GVAR(HVTTaskBaseClass) = createHashMapFromArray [ _self set ["hvtControllers", _controllers]; true }], - ["waitForAssignmentIfTimed", compileFinal { - private _timeLimit = _self getOrDefault ["timeLimit", 0]; + ["waitForAssignment", compileFinal { 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 { sleep 1; @@ -210,8 +209,8 @@ GVAR(HVTTaskBaseClass) = createHashMapFromArray [ }], ["runLoop", compileFinal { _self call ["waitForRequiredEntities", []]; + _self call ["waitForAssignment", []]; _self call ["startHvtControllers", []]; - _self call ["waitForAssignmentIfTimed", []]; _self call ["markActive", []]; while { (_self call ["getStatus", []]) isEqualTo "active" } do { diff --git a/arma/server/addons/task/functions/objects/fnc_HostageTaskBaseClass.sqf b/arma/server/addons/task/functions/objects/fnc_HostageTaskBaseClass.sqf index efa44f2..db7fb13 100644 --- a/arma/server/addons/task/functions/objects/fnc_HostageTaskBaseClass.sqf +++ b/arma/server/addons/task/functions/objects/fnc_HostageTaskBaseClass.sqf @@ -183,11 +183,10 @@ GVAR(HostageTaskBaseClass) = createHashMapFromArray [ _self set ["maxHostageLosses", _maxHostageLosses]; true }], - ["waitForAssignmentIfTimed", compileFinal { - private _timeLimit = _self getOrDefault ["timeLimit", 0]; + ["waitForAssignment", compileFinal { 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 { sleep 1; @@ -337,8 +336,8 @@ GVAR(HostageTaskBaseClass) = createHashMapFromArray [ }], ["runLoop", compileFinal { _self call ["waitForRequiredEntities", []]; + _self call ["waitForAssignment", []]; _self call ["startHostageControllers", []]; - _self call ["waitForAssignmentIfTimed", []]; _self call ["markActive", []]; while { (_self call ["getStatus", []]) isEqualTo "active" } do { diff --git a/arma/server/config.example.toml b/arma/server/config.example.toml index e25d1a4..67d1947 100644 --- a/arma/server/config.example.toml +++ b/arma/server/config.example.toml @@ -1,5 +1,7 @@ # Forge Server Configuration # 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] endpoint = "127.0.0.1:8000" diff --git a/arma/server/docs/README.md b/arma/server/docs/README.md index ea53226..d2cac9f 100644 --- a/arma/server/docs/README.md +++ b/arma/server/docs/README.md @@ -21,7 +21,9 @@ SQF module ## 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 [surreal] @@ -33,6 +35,9 @@ password = "root" 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 [SurrealDB Setup](../../../docs/surrealdb-setup.md). diff --git a/arma/server/extension/README.md b/arma/server/extension/README.md index ea72deb..9eeda21 100644 --- a/arma/server/extension/README.md +++ b/arma/server/extension/README.md @@ -6,6 +6,19 @@ persists durable state through SurrealDB. 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 - Register extension command groups for actor, bank, garage, locker, org, diff --git a/arma/server/extension/config.example.toml b/arma/server/extension/config.example.toml index bd1bc63..346efe3 100644 --- a/arma/server/extension/config.example.toml +++ b/arma/server/extension/config.example.toml @@ -1,5 +1,7 @@ # Forge Server Configuration # 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] # SurrealDB HTTP endpoint. Use "127.0.0.1:8000" for a local server. diff --git a/docs/DEVELOPMENT_GUIDE.md b/docs/DEVELOPMENT_GUIDE.md index e8b4588..ce7f54e 100644 --- a/docs/DEVELOPMENT_GUIDE.md +++ b/docs/DEVELOPMENT_GUIDE.md @@ -5,7 +5,9 @@ This guide covers the usual path for adding or changing a Forge module. ## Local Checks 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: diff --git a/docs/FRAMEWORK_ARCHITECTURE.md b/docs/FRAMEWORK_ARCHITECTURE.md index a6bfeff..08f4529 100644 --- a/docs/FRAMEWORK_ARCHITECTURE.md +++ b/docs/FRAMEWORK_ARCHITECTURE.md @@ -127,6 +127,13 @@ password = "root" 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 [SurrealDB Setup](./surrealdb-setup.md). diff --git a/docs/README.md b/docs/README.md index e4b4a81..0b560b5 100644 --- a/docs/README.md +++ b/docs/README.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 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 - [Framework Architecture](./FRAMEWORK_ARCHITECTURE.md): how SQF, web UIs, diff --git a/docs/SURREALDB_SETUP.md b/docs/SURREALDB_SETUP.md index b865024..b26119c 100644 --- a/docs/SURREALDB_SETUP.md +++ b/docs/SURREALDB_SETUP.md @@ -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 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 ### Developer or Server Operator @@ -73,11 +93,11 @@ password = "root" 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. -2. Let the extension connect and apply the Forge schema modules. -3. Verify the connection state: +1. Let the extension connect and apply the Forge schema modules. +2. Verify the connection state: ```sqf "forge_server" callExtension ["status", []]; diff --git a/docs/surrealdb-setup.md b/docs/surrealdb-setup.md index b865024..b26119c 100644 --- a/docs/surrealdb-setup.md +++ b/docs/surrealdb-setup.md @@ -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 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 ### Developer or Server Operator @@ -73,11 +93,11 @@ password = "root" 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. -2. Let the extension connect and apply the Forge schema modules. -3. Verify the connection state: +1. Let the extension connect and apply the Forge schema modules. +2. Verify the connection state: ```sqf "forge_server" callExtension ["status", []]; diff --git a/docus/content/1.getting-started/0.index.md b/docus/content/1.getting-started/0.index.md index 840d99c..ae32302 100644 --- a/docus/content/1.getting-started/0.index.md +++ b/docus/content/1.getting-started/0.index.md @@ -11,6 +11,17 @@ Forge combines: - shared Rust crates for models, repositories, and services - 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 ```powershell diff --git a/docus/content/1.getting-started/1.architecture.md b/docus/content/1.getting-started/1.architecture.md index f6785f9..f0c9c4a 100644 --- a/docus/content/1.getting-started/1.architecture.md +++ b/docus/content/1.getting-started/1.architecture.md @@ -126,6 +126,13 @@ password = "root" 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 [SurrealDB Setup](/getting-started/surrealdb-setup). diff --git a/docus/content/1.getting-started/3.development.md b/docus/content/1.getting-started/3.development.md index 569c902..d9bd7e0 100644 --- a/docus/content/1.getting-started/3.development.md +++ b/docus/content/1.getting-started/3.development.md @@ -6,7 +6,9 @@ description: "This guide covers the usual path for adding or changing a Forge mo ## Local Checks 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: diff --git a/docus/content/1.getting-started/5.surrealdb-setup.md b/docus/content/1.getting-started/5.surrealdb-setup.md index 2d84f09..abf2272 100644 --- a/docus/content/1.getting-started/5.surrealdb-setup.md +++ b/docus/content/1.getting-started/5.surrealdb-setup.md @@ -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." --- +## 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 ### Developer or Server Operator @@ -72,11 +92,11 @@ password = "root" 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. -2. Let the extension connect and apply the Forge schema modules. -3. Verify the connection state: +1. Let the extension connect and apply the Forge schema modules. +2. Verify the connection state: ```sqf "forge_server" callExtension ["status", []]; diff --git a/docus/content/2.server-extension/0.index.md b/docus/content/2.server-extension/0.index.md index 4413988..6e8f284 100644 --- a/docus/content/2.server-extension/0.index.md +++ b/docus/content/2.server-extension/0.index.md @@ -20,7 +20,9 @@ SQF module ## 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 [surreal] @@ -32,6 +34,9 @@ password = "root" 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 [SurrealDB Setup](/getting-started/surrealdb-setup). diff --git a/docus/content/index.md b/docus/content/index.md index 7ea050e..dee8f26 100644 --- a/docus/content/index.md +++ b/docus/content/index.md @@ -16,6 +16,10 @@ browser-backed player interfaces. Use these docs to understand the runtime architecture, extension API surface, 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 :::u-button --- diff --git a/lib/services/src/task.rs b/lib/services/src/task.rs index 292367f..e66dd94 100644 --- a/lib/services/src/task.rs +++ b/lib/services/src/task.rs @@ -80,9 +80,10 @@ impl TaskStateService { self.repository .save_ownership(entry_id.clone(), ownership.clone())?; + let accepted = !ownership.requester_uid.trim().is_empty(); let entry = self.patch_catalog_ownership( &entry_id, - true, + accepted, &ownership.requester_uid, &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] fn get_status_falls_back_to_completed_status() { let repository = InMemoryTaskRepository::new(); diff --git a/tools/sync-docus-docs.mjs b/tools/sync-docus-docs.mjs index 386ceaa..46a5953 100644 --- a/tools/sync-docus-docs.mjs +++ b/tools/sync-docus-docs.mjs @@ -171,6 +171,10 @@ browser-backed player interfaces. Use these docs to understand the runtime architecture, extension API surface, 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 :::u-button --- @@ -372,6 +376,17 @@ Forge combines: - shared Rust crates for models, repositories, and services - 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 \`\`\`powershell