diff --git a/arma/server/addons/actor/XEH_preInit.sqf b/arma/server/addons/actor/XEH_preInit.sqf
index 46cf2f8..d47f97b 100644
--- a/arma/server/addons/actor/XEH_preInit.sqf
+++ b/arma/server/addons/actor/XEH_preInit.sqf
@@ -18,7 +18,7 @@ PREP_RECOMPILE_END;
if (_uid isEqualTo "") exitWith { diag_log "[FORGE:Server:Actor] Empty/Invalid UID!" };
- private _finalData = GVAR(ActorStore) call ["get", [GVAR(Registry), _uid, _field]];
+ private _finalData = GVAR(ActorStore) call ["get", [_uid, _field]];
private _player = [_uid] call EFUNC(common,getPlayer);
[CRPC(actor,responseSyncActor), [_finalData], _player] call CFUNC(targetEvent);
@@ -29,7 +29,7 @@ PREP_RECOMPILE_END;
if (_uid isEqualTo "" || _field isEqualTo "") exitWith { diag_log "[FORGE:Server:Actor] Empty/Invalid UID or Key!" };
- private _hashMap = GVAR(ActorStore) call ["set", [GVAR(Registry), "actor:update", _uid, _field, _value, _sync]];
+ private _hashMap = GVAR(ActorStore) call ["set", [_uid, _field, _value, _sync]];
private _player = [_uid] call EFUNC(common,getPlayer);
[CRPC(actor,responseSyncActor), [_hashMap], _player] call CFUNC(targetEvent);
@@ -41,7 +41,7 @@ PREP_RECOMPILE_END;
if (_uid isEqualTo "") exitWith { diag_log "[FORGE:Server:Actor] Empty/Invalid UID!" };
if ((_fieldValuePairs isEqualTo createHashMap) || !(_fieldValuePairs isEqualType createHashMap)) exitWith { diag_log "[FORGE:Server:Actor] Empty/Invalid field pairs!" };
- private _hashMap = GVAR(ActorStore) call ["mset", [GVAR(Registry), "actor:update", _uid, _fieldValuePairs, _sync]];
+ private _hashMap = GVAR(ActorStore) call ["mset", [_uid, _fieldValuePairs, _sync]];
private _player = [_uid] call EFUNC(common,getPlayer);
[CRPC(actor,responseSyncActor), [_hashMap], _player] call CFUNC(targetEvent);
@@ -53,7 +53,7 @@ PREP_RECOMPILE_END;
if (_uid isEqualTo "") exitWith { diag_log "[FORGE:Server:Actor] Empty/Invalid UID!" };
GVAR(ActorStore) call ["snapshot", [_uid]];
- private _finalData = GVAR(ActorStore) call ["save", [GVAR(Registry), "actor:update", _uid]];
+ private _finalData = GVAR(ActorStore) call ["save", [_uid]];
private _player = [_uid] call EFUNC(common,getPlayer);
[CRPC(actor,responseSyncActor), [_finalData], _player] call CFUNC(targetEvent);
@@ -63,5 +63,5 @@ PREP_RECOMPILE_END;
params [["_uid", "", [""]]];
if (_uid isEqualTo "") exitWith { diag_log "[FORGE:Server:Actor] Empty/Invalid UID!" };
- GVAR(ActorStore) call ["remove", [GVAR(Registry), _uid]];
+ GVAR(ActorStore) call ["remove", [_uid]];
}] call CFUNC(addEventHandler);
diff --git a/arma/server/addons/actor/functions/fnc_initActorStore.sqf b/arma/server/addons/actor/functions/fnc_initActorStore.sqf
index 5dbba52..7a83da8 100644
--- a/arma/server/addons/actor/functions/fnc_initActorStore.sqf
+++ b/arma/server/addons/actor/functions/fnc_initActorStore.sqf
@@ -9,8 +9,8 @@
*
* Description:
* Initializes the actor store for managing player actor data.
- * Actor hot state is owned by the extension; SQF maintains a compatibility
- * mirror for engine-adjacent consumers.
+ * Actor hot state is owned by the extension; SQF acts as a thin bridge for
+ * engine-adjacent reads, snapshots, and response fan-out.
*
* Arguments:
* None
@@ -38,7 +38,7 @@ GVAR(ActorModel) = compileFinal createHashMapObject [[
_actor set ["state", "HEALTHY"];
_actor set ["phone_number", ""];
_actor set ["email", ""];
- _actor set ["organization", ""];
+ _actor set ["organization", "default"];
_actor set ["holster", true];
_actor
@@ -109,7 +109,6 @@ GVAR(ActorBaseStore) = compileFinal createHashMapFromArray [
["#base", EGVAR(common,BaseStore)],
["#type", "ActorBaseStore"],
["#create", compileFinal {
- GVAR(Registry) = createHashMap;
["INFO", "Actor Store Initialized!"] call EFUNC(common,log);
}],
["cacheActor", compileFinal {
@@ -117,9 +116,7 @@ GVAR(ActorBaseStore) = compileFinal createHashMapFromArray [
if (_uid isEqualTo "" || { !(_actor isEqualType createHashMap) }) exitWith { createHashMap };
- private _finalActor = GVAR(ActorModel) call ["migrate", [+_actor]];
- GVAR(Registry) set [_uid, _finalActor];
- _finalActor
+ GVAR(ActorModel) call ["migrate", [+_actor]]
}],
["callHotActor", compileFinal {
params [["_function", "", [""]], ["_arguments", [], [[]]]];
@@ -138,6 +135,20 @@ GVAR(ActorBaseStore) = compileFinal createHashMapFromArray [
if !(_data isEqualType createHashMap) exitWith { createHashMap };
_data
}],
+ ["listHotUids", compileFinal {
+ ["actor:hot:keys", []] call EFUNC(extension,extCall) params ["_result", "_isSuccess"];
+ if !(_isSuccess) exitWith { [] };
+ if !(_result isEqualType "") exitWith { [] };
+ if ((_result find "Error:") == 0) exitWith {
+ ["ERROR", format ["Actor extension call '%1' failed: %2", "actor:hot:keys", _result]] call EFUNC(common,log);
+ []
+ };
+
+ private _uids = fromJSON _result;
+ if !(_uids isEqualType []) exitWith { [] };
+
+ _uids select { _x isEqualType "" && { _x isNotEqualTo "" } }
+ }],
["loadHotActor", compileFinal {
params [["_uid", "", [""]], ["_initialize", false, [false]]];
@@ -149,75 +160,10 @@ GVAR(ActorBaseStore) = compileFinal createHashMapFromArray [
_self call ["cacheActor", [_uid, _actor]]
}],
- ["normalizeGetArgs", compileFinal {
- params ["_rawArguments"];
-
- if ((_rawArguments param [0, createHashMap]) isEqualType createHashMap) exitWith {
- [
- _rawArguments param [1, "", [""]],
- _rawArguments param [2, "", [""]]
- ]
- };
-
- [
- _rawArguments param [0, "", [""]],
- _rawArguments param [1, "", [""]]
- ]
- }],
- ["normalizeSetArgs", compileFinal {
- params ["_rawArguments"];
-
- if ((_rawArguments param [0, createHashMap]) isEqualType createHashMap) exitWith {
- [
- _rawArguments param [2, "", [""]],
- _rawArguments param [3, "", [""]],
- _rawArguments param [4, nil, [0, "", [], false, createHashMap, objNull, grpNull]],
- _rawArguments param [5, false, [false]]
- ]
- };
-
- [
- _rawArguments param [0, "", [""]],
- _rawArguments param [1, "", [""]],
- _rawArguments param [2, nil, [0, "", [], false, createHashMap, objNull, grpNull]],
- _rawArguments param [3, false, [false]]
- ]
- }],
- ["normalizeMSetArgs", compileFinal {
- params ["_rawArguments"];
-
- if ((_rawArguments param [0, createHashMap]) isEqualType createHashMap) exitWith {
- [
- _rawArguments param [2, "", [""]],
- _rawArguments param [3, createHashMap, [createHashMap]],
- _rawArguments param [4, false, [false]]
- ]
- };
-
- [
- _rawArguments param [0, "", [""]],
- _rawArguments param [1, createHashMap, [createHashMap]],
- _rawArguments param [2, false, [false]]
- ]
- }],
- ["normalizeUidArg", compileFinal {
- params ["_rawArguments"];
-
- if ((_rawArguments param [0, createHashMap]) isEqualType createHashMap) exitWith {
- _rawArguments param [1, "", [""]]
- };
-
- _rawArguments param [0, "", [""]]
- }],
["init", compileFinal {
params [["_uid", "", [""]]];
private _player = [_uid] call EFUNC(common,getPlayer);
- private _cached = GVAR(Registry) getOrDefault [_uid, nil];
- if !(isNil { _cached }) exitWith {
- [CRPC(actor,responseInitActor), [_cached], _player] call CFUNC(targetEvent);
- _cached
- };
["actor:exists", [_uid]] call EFUNC(extension,extCall) params ["_result", "_isSuccess"];
if !(_isSuccess) exitWith {
@@ -264,7 +210,7 @@ GVAR(ActorBaseStore) = compileFinal createHashMapFromArray [
_finalActor
}],
["get", compileFinal {
- call (_self get "normalizeGetArgs") params ["_uid", "_field"];
+ params [["_uid", "", [""]], ["_field", "", [""]]];
private _actor = _self call ["loadHotActor", [_uid, false]];
if (_actor isEqualTo createHashMap) then {
@@ -274,6 +220,50 @@ GVAR(ActorBaseStore) = compileFinal createHashMapFromArray [
if (_field isEqualTo "") exitWith { _actor };
_actor getOrDefault [_field, nil]
}],
+ ["load", compileFinal {
+ params [["_uid", "", [""]]];
+
+ private _actor = _self call ["get", [_uid, ""]];
+ if !(_actor isEqualType createHashMap) exitWith { createHashMap };
+
+ _actor
+ }],
+ ["getFieldOrDefault", compileFinal {
+ params [["_uid", "", [""]], ["_field", "", [""]], ["_default", nil]];
+
+ if (_uid isEqualTo "" || { _field isEqualTo "" }) exitWith { _default };
+
+ private _actor = _self call ["load", [_uid]];
+ if !(_actor isEqualType createHashMap) exitWith { _default };
+ if (_actor isEqualTo createHashMap) exitWith { _default };
+
+ _actor getOrDefault [_field, _default]
+ }],
+ ["getOrganization", compileFinal {
+ params [["_uid", "", [""]], ["_default", "default", [""]]];
+
+ private _orgID = _self call ["getFieldOrDefault", [_uid, "organization", _default]];
+ if !(_orgID isEqualType "") exitWith { _default };
+ if (_orgID isEqualTo "") exitWith { _default };
+
+ _orgID
+ }],
+ ["getName", compileFinal {
+ params [["_uid", "", [""]], ["_default", "", [""]]];
+
+ private _name = _self call ["getFieldOrDefault", [_uid, "name", _default]];
+ if !(_name isEqualType "") exitWith { _default };
+
+ _name
+ }],
+ ["getPhoneNumber", compileFinal {
+ params [["_uid", "", [""]], ["_default", "", [""]]];
+
+ private _phoneNumber = _self call ["getFieldOrDefault", [_uid, "phone_number", _default]];
+ if !(_phoneNumber isEqualType "") exitWith { _default };
+
+ _phoneNumber
+ }],
["override", compileFinal {
params [
["_uid", "", [""]],
@@ -297,7 +287,12 @@ GVAR(ActorBaseStore) = compileFinal createHashMapFromArray [
_self call ["cacheActor", [_uid, _actor]]
}],
["set", compileFinal {
- call (_self get "normalizeSetArgs") params ["_uid", "_field", "_value", "_sync"];
+ params [
+ ["_uid", "", [""]],
+ ["_field", "", [""]],
+ ["_value", nil, [0, "", [], false, createHashMap, objNull, grpNull]],
+ ["_sync", false, [false]]
+ ];
if (_uid isEqualTo "" || { _field isEqualTo "" }) exitWith { createHashMap };
@@ -312,7 +307,7 @@ GVAR(ActorBaseStore) = compileFinal createHashMapFromArray [
createHashMapFromArray [[_field, _updatedActor getOrDefault [_field, _value]]]
}],
["mset", compileFinal {
- call (_self get "normalizeMSetArgs") params ["_uid", "_fieldValuePairs", "_sync"];
+ params [["_uid", "", [""]], ["_fieldValuePairs", createHashMap, [createHashMap]], ["_sync", false, [false]]];
if (_uid isEqualTo "" || { !(_fieldValuePairs isEqualType createHashMap) }) exitWith { createHashMap };
@@ -327,7 +322,7 @@ GVAR(ActorBaseStore) = compileFinal createHashMapFromArray [
+_fieldValuePairs
}],
["save", compileFinal {
- private _uid = call (_self get "normalizeUidArg");
+ params [["_uid", "", [""]]];
if (_uid isEqualTo "") exitWith { createHashMap };
private _actor = _self call ["callHotActor", ["actor:hot:save", [_uid]]];
@@ -336,11 +331,10 @@ GVAR(ActorBaseStore) = compileFinal createHashMapFromArray [
_self call ["cacheActor", [_uid, _actor]]
}],
["remove", compileFinal {
- private _uid = call (_self get "normalizeUidArg");
+ params [["_uid", "", [""]]];
if (_uid isEqualTo "") exitWith { false };
- GVAR(Registry) deleteAt _uid;
["actor:hot:remove", [_uid]] call EFUNC(extension,extCall) params ["_result", "_isSuccess"];
_isSuccess && { _result isEqualTo "OK" }
}],
diff --git a/arma/server/addons/bank/functions/fnc_initPayloadBuilder.sqf b/arma/server/addons/bank/functions/fnc_initPayloadBuilder.sqf
index 6cd9bcd..24f8615 100644
--- a/arma/server/addons/bank/functions/fnc_initPayloadBuilder.sqf
+++ b/arma/server/addons/bank/functions/fnc_initPayloadBuilder.sqf
@@ -62,10 +62,7 @@ GVAR(BankPayloadBuilder) = createHashMapObject [[
];
if (_uid isEqualTo "") exitWith { _defaultState };
- private _actor = EGVAR(actor,Registry) getOrDefault [_uid, createHashMap];
- private _orgID = _actor getOrDefault ["organization", "default"];
- if (_orgID isEqualTo "") then { _orgID = "default"; };
-
+ private _orgID = EGVAR(actor,ActorStore) call ["getOrganization", [_uid]];
private _org = EGVAR(org,OrgStore) call ["loadById", [_orgID]];
if (_org isEqualTo createHashMap) then {
_org = EGVAR(org,OrgStore) call ["loadById", ["default"]];
diff --git a/arma/server/addons/bank/functions/fnc_initSessionManager.sqf b/arma/server/addons/bank/functions/fnc_initSessionManager.sqf
index dc9077e..7d4774b 100644
--- a/arma/server/addons/bank/functions/fnc_initSessionManager.sqf
+++ b/arma/server/addons/bank/functions/fnc_initSessionManager.sqf
@@ -24,16 +24,20 @@
#pragma hemtt ignore_variables ["_self"]
GVAR(BankSessionManager) = createHashMapObject [[
["#type", "BankSessionManager"],
+ ["#create", compileFinal {
+ _self set ["sessions", createHashMap];
+ }],
["getSessionState", compileFinal {
params [["_uid", "", [""]]];
- private _session = GVAR(SessionRegistry) getOrDefault [_uid, createHashMap];
+ private _sessions = _self getOrDefault ["sessions", createHashMap];
+ private _session = _sessions getOrDefault [_uid, createHashMap];
if (_session isEqualTo createHashMap) then {
_session = createHashMapFromArray [
["atmAuthorized", false],
["mode", "bank"]
];
- GVAR(SessionRegistry) set [_uid, _session];
+ _sessions set [_uid, _session];
};
_session
@@ -44,9 +48,10 @@ GVAR(BankSessionManager) = createHashMapObject [[
if (_uid isEqualTo "") exitWith { createHashMap };
private _session = +(_self call ["getSessionState", [_uid]]);
+ private _sessions = _self getOrDefault ["sessions", createHashMap];
{ _session set [_x, _y]; } forEach _fieldValuePairs;
- GVAR(SessionRegistry) set [_uid, _session];
+ _sessions set [_uid, _session];
_session
}],
["resolveMode", compileFinal {
diff --git a/arma/server/addons/bank/functions/fnc_initStore.sqf b/arma/server/addons/bank/functions/fnc_initStore.sqf
index 507f236..d04e37b 100644
--- a/arma/server/addons/bank/functions/fnc_initStore.sqf
+++ b/arma/server/addons/bank/functions/fnc_initStore.sqf
@@ -18,7 +18,6 @@ GVAR(BankBaseStore) = compileFinal createHashMapFromArray [
["#base", EGVAR(common,BaseStore)],
["#type", "BankBaseStore"],
["#create", compileFinal {
- GVAR(SessionRegistry) = createHashMap;
["INFO", "Bank Store Initialized!"] call EFUNC(common,log);
}],
["normalizeAccount", compileFinal {
@@ -228,6 +227,17 @@ GVAR(BankBaseStore) = compileFinal createHashMapFromArray [
false
};
+ private _persistenceFailures = [];
+ private _savedBank = _self call ["save", [_uid]];
+ if (_savedBank isEqualTo createHashMap) then {
+ _persistenceFailures pushBack "bank";
+ };
+
+ private _orgPersistenceMessage = _orgResult getOrDefault ["persistenceMessage", ""];
+ if !(_orgResult getOrDefault ["persisted", false]) then {
+ _persistenceFailures pushBack "organization";
+ };
+
GVAR(BankMessenger) call ["sendAccountSync", [_uid, _bankPatch]];
GVAR(BankMessenger) call ["sendNotification", [_uid, "info", "Bank", _orgResult getOrDefault ["message", format ["Repaid $%1 toward the organization credit line.", [_amount] call EFUNC(common,formatNumber)]]]];
@@ -241,6 +251,19 @@ GVAR(BankBaseStore) = compileFinal createHashMapFromArray [
} forEach (_orgResult getOrDefault ["memberUids", []]);
};
+ if (_persistenceFailures isNotEqualTo []) then {
+ private _warning = format [
+ "Credit repayment posted, but durable save failed for: %1.",
+ _persistenceFailures joinString ", "
+ ];
+ if (_orgPersistenceMessage isNotEqualTo "") then {
+ _warning = format ["%1 %2", _warning, _orgPersistenceMessage];
+ };
+
+ ["ERROR", format ["Credit repayment for %1 completed with persistence failures: %2", _uid, _persistenceFailures joinString ", "]] call EFUNC(common,log);
+ GVAR(BankMessenger) call ["sendAlert", [_uid, "warning", _warning]];
+ };
+
_self call ["hydrateSession", [_uid, "", false]];
true
}],
diff --git a/arma/server/addons/cad/functions/fnc_initActivityRepository.sqf b/arma/server/addons/cad/functions/fnc_initActivityRepository.sqf
index 3fab742..fa9b05b 100644
--- a/arma/server/addons/cad/functions/fnc_initActivityRepository.sqf
+++ b/arma/server/addons/cad/functions/fnc_initActivityRepository.sqf
@@ -22,48 +22,19 @@
#pragma hemtt ignore_variables ["_self"]
GVAR(ActivityRepositoryBaseClass) = compileFinal createHashMapFromArray [
["#type", "CadActivityRepositoryBaseClass"],
- ["#create", compileFinal {
- _self set ["activityRegistry", []];
- _self set ["persistenceLoaded", false];
- }],
- ["restorePersistedActivity", compileFinal {
- if (_self getOrDefault ["persistenceLoaded", false]) exitWith { true };
-
- private _persistenceService = _self getOrDefault ["persistenceService", createHashMap];
- if (_persistenceService isEqualTo createHashMap) exitWith { false };
-
- private _result = _persistenceService call ["loadActivity", []];
- if !(_result getOrDefault ["success", false]) exitWith { false };
-
- _self set ["activityRegistry", +(_result getOrDefault ["data", []])];
- _self set ["persistenceLoaded", true];
- true
- }],
["appendEntry", compileFinal {
params [["_entry", createHashMap, [createHashMap]]];
if (_entry isEqualTo createHashMap) exitWith { false };
-
- _self call ["restorePersistedActivity", []];
-
- private _activityRegistry = +(_self getOrDefault ["activityRegistry", []]);
private _finalEntry = +_entry;
if ((_finalEntry getOrDefault ["timestamp", -1]) < 0) then {
_finalEntry set ["timestamp", serverTime];
};
- _activityRegistry pushBack _finalEntry;
-
- if ((count _activityRegistry) > 50) then {
- _activityRegistry deleteRange [0, (count _activityRegistry) - 50];
- };
-
- _self set ["activityRegistry", _activityRegistry];
private _persistenceService = _self getOrDefault ["persistenceService", createHashMap];
- if (_persistenceService isNotEqualTo createHashMap) then {
- _persistenceService call ["appendActivity", [_finalEntry]];
- };
- true
+ if (_persistenceService isEqualTo createHashMap) exitWith { false };
+
+ _persistenceService call ["appendActivity", [_finalEntry]]
}],
["appendActivity", compileFinal {
params [
@@ -85,8 +56,13 @@ GVAR(ActivityRepositoryBaseClass) = compileFinal createHashMapFromArray [
_self call ["appendEntry", [_entry]]
}],
["getActivity", compileFinal {
- _self call ["restorePersistedActivity", []];
- +(_self getOrDefault ["activityRegistry", []])
+ private _persistenceService = _self getOrDefault ["persistenceService", createHashMap];
+ if (_persistenceService isEqualTo createHashMap) exitWith { [] };
+
+ private _result = _persistenceService call ["loadActivity", []];
+ if !(_result getOrDefault ["success", false]) exitWith { [] };
+
+ +(_result getOrDefault ["data", []])
}]
];
diff --git a/arma/server/addons/cad/functions/fnc_initAssignmentRepository.sqf b/arma/server/addons/cad/functions/fnc_initAssignmentRepository.sqf
index cb3d5be..b8ab1ae 100644
--- a/arma/server/addons/cad/functions/fnc_initAssignmentRepository.sqf
+++ b/arma/server/addons/cad/functions/fnc_initAssignmentRepository.sqf
@@ -24,15 +24,51 @@
GVAR(AssignmentRepositoryBaseClass) = compileFinal createHashMapFromArray [
["#type", "CadAssignmentRepositoryBaseClass"],
["#create", compileFinal {
- _self set ["assignmentRegistry", createHashMap];
- _self set ["dispatchOrderRegistry", createHashMap];
- _self set ["persistenceLoaded", false];
+ _self set ["ownershipHydrated", false];
+ }],
+ ["loadState", compileFinal {
+ private _result = createHashMapFromArray [
+ ["success", false],
+ ["assignments", createHashMap],
+ ["dispatchOrders", createHashMap]
+ ];
+
+ private _persistenceService = _self getOrDefault ["persistenceService", createHashMap];
+ if (_persistenceService isEqualTo createHashMap) exitWith { _result };
+
+ private _assignmentsResult = _persistenceService call ["loadAssignments", []];
+ if !(_assignmentsResult getOrDefault ["success", false]) exitWith { _result };
+
+ private _ordersResult = _persistenceService call ["loadDispatchOrders", []];
+ if !(_ordersResult getOrDefault ["success", false]) exitWith { _result };
+
+ private _assignmentRegistry = +(_assignmentsResult getOrDefault ["data", createHashMap]);
+ private _dispatchOrderRegistry = +(_ordersResult getOrDefault ["data", createHashMap]);
+
+ if !(_self getOrDefault ["ownershipHydrated", false]) then {
+ {
+ if ((_y getOrDefault ["state", ""]) isNotEqualTo "acknowledged") then { continue; };
+ if ((_y getOrDefault ["acknowledgedByUid", ""]) isEqualTo "") then { continue; };
+ if ((_dispatchOrderRegistry getOrDefault [_x, createHashMap]) isNotEqualTo createHashMap) then { continue; };
+ if ((EGVAR(task,TaskStore) call ["getTaskStatus", [_x]]) isNotEqualTo "active") then { continue; };
+
+ EGVAR(task,TaskStore) call ["bindTaskOwnership", [_x, _y getOrDefault ["acknowledgedByUid", ""]]];
+ } forEach _assignmentRegistry;
+
+ _self set ["ownershipHydrated", true];
+ };
+
+ _result set ["success", true];
+ _result set ["assignments", _assignmentRegistry];
+ _result set ["dispatchOrders", _dispatchOrderRegistry];
+ _result
}],
["pruneAssignments", compileFinal {
- _self call ["restorePersistedState", []];
+ private _state = _self call ["loadState", []];
+ if !(_state getOrDefault ["success", false]) exitWith { 0 };
- private _assignmentRegistry = _self getOrDefault ["assignmentRegistry", createHashMap];
- private _dispatchOrderRegistry = _self getOrDefault ["dispatchOrderRegistry", createHashMap];
+ private _assignmentRegistry = _state getOrDefault ["assignments", createHashMap];
+ private _dispatchOrderRegistry = _state getOrDefault ["dispatchOrders", createHashMap];
private _keysToRemove = [];
{
@@ -46,12 +82,6 @@ GVAR(AssignmentRepositoryBaseClass) = compileFinal createHashMapFromArray [
};
} forEach _assignmentRegistry;
- {
- _assignmentRegistry deleteAt _x;
- } forEach _keysToRemove;
-
- _self set ["assignmentRegistry", _assignmentRegistry];
-
private _persistenceService = _self getOrDefault ["persistenceService", createHashMap];
if (_persistenceService isNotEqualTo createHashMap) then {
{
@@ -62,50 +92,73 @@ GVAR(AssignmentRepositoryBaseClass) = compileFinal createHashMapFromArray [
count _keysToRemove
}],
["getAssignments", compileFinal {
- _self call ["restorePersistedState", []];
- values (_self getOrDefault ["assignmentRegistry", createHashMap])
+ private _state = _self call ["loadState", []];
+ if !(_state getOrDefault ["success", false]) exitWith { [] };
+
+ values (_state getOrDefault ["assignments", createHashMap])
}],
["isDispatchOrder", compileFinal {
params [["_taskID", "", [""]]];
if (_taskID isEqualTo "") exitWith { false };
- ((_self getOrDefault ["dispatchOrderRegistry", createHashMap]) getOrDefault [_taskID, createHashMap]) isNotEqualTo createHashMap
+ private _state = _self call ["loadState", []];
+ if !(_state getOrDefault ["success", false]) exitWith { false };
+
+ ((_state getOrDefault ["dispatchOrders", createHashMap]) getOrDefault [_taskID, createHashMap]) isNotEqualTo createHashMap
}],
- ["restorePersistedState", compileFinal {
- if (_self getOrDefault ["persistenceLoaded", false]) exitWith { true };
+ ["getAssignmentByTaskId", compileFinal {
+ params [["_taskID", "", [""]]];
- private _persistenceService = _self getOrDefault ["persistenceService", createHashMap];
- if (_persistenceService isEqualTo createHashMap) exitWith { false };
+ if (_taskID isEqualTo "") exitWith { createHashMap };
- private _assignmentsResult = _persistenceService call ["loadAssignments", []];
- if !(_assignmentsResult getOrDefault ["success", false]) exitWith { false };
+ private _state = _self call ["loadState", []];
+ if !(_state getOrDefault ["success", false]) exitWith { createHashMap };
- private _ordersResult = _persistenceService call ["loadDispatchOrders", []];
- if !(_ordersResult getOrDefault ["success", false]) exitWith { false };
+ +((_state getOrDefault ["assignments", createHashMap]) getOrDefault [_taskID, createHashMap])
+ }],
+ ["getDispatchOrderByTaskId", compileFinal {
+ params [["_taskID", "", [""]]];
- private _assignmentRegistry = +(_assignmentsResult getOrDefault ["data", createHashMap]);
- private _dispatchOrderRegistry = +(_ordersResult getOrDefault ["data", createHashMap]);
+ if (_taskID isEqualTo "") exitWith { createHashMap };
- _self set ["assignmentRegistry", _assignmentRegistry];
- _self set ["dispatchOrderRegistry", _dispatchOrderRegistry];
- _self set ["persistenceLoaded", true];
+ private _state = _self call ["loadState", []];
+ if !(_state getOrDefault ["success", false]) exitWith { createHashMap };
+
+ +((_state getOrDefault ["dispatchOrders", createHashMap]) getOrDefault [_taskID, createHashMap])
+ }],
+ ["getCurrentTaskIdForGroup", compileFinal {
+ params [["_groupID", "", [""]]];
+
+ if (_groupID isEqualTo "") exitWith { "" };
+
+ private _state = _self call ["loadState", []];
+ if !(_state getOrDefault ["success", false]) exitWith { "" };
+
+ private _assignmentRegistry = _state getOrDefault ["assignments", createHashMap];
+ private _dispatchOrderRegistry = _state getOrDefault ["dispatchOrders", createHashMap];
+ private _taskID = "";
{
- if ((_y getOrDefault ["state", ""]) isNotEqualTo "acknowledged") then { continue; };
- if (((_y getOrDefault ["acknowledgedByUid", ""]) isEqualTo "")) then { continue; };
- if ((_dispatchOrderRegistry getOrDefault [_x, createHashMap]) isNotEqualTo createHashMap) then { continue; };
- if ((EGVAR(task,TaskStore) call ["getTaskStatus", [_x]]) isNotEqualTo "active") then { continue; };
- EGVAR(task,TaskStore) call ["bindTaskOwnership", [_x, _y getOrDefault ["acknowledgedByUid", ""]]];
+ if ((_y getOrDefault ["groupId", ""]) isNotEqualTo _groupID) then { continue; };
+ if !((_y getOrDefault ["state", ""]) in ["assigned", "acknowledged"]) then { continue; };
+
+ private _dispatchOrder = +(_dispatchOrderRegistry getOrDefault [_x, createHashMap]);
+ if (_dispatchOrder isEqualTo createHashMap) then {
+ if ((EGVAR(task,TaskStore) call ["getTaskStatus", [_x]]) isNotEqualTo "active") then { continue; };
+ _taskID = _x;
+ } else {
+ _taskID = _dispatchOrder getOrDefault ["title", _x];
+ };
} forEach _assignmentRegistry;
- true
+ _taskID
}],
["buildDispatchOrderEntry", compileFinal {
params [
["_taskID", "", [""]],
["_order", createHashMap, [createHashMap]],
- ["_assignmentRegistry", createHashMap, [createHashMap]],
+ ["_assignment", createHashMap, [createHashMap]],
["_groupRepository", createHashMap, [createHashMap]]
];
@@ -127,7 +180,6 @@ GVAR(AssignmentRepositoryBaseClass) = compileFinal createHashMapFromArray [
};
};
- private _assignment = _assignmentRegistry getOrDefault [_taskID, createHashMap];
_entry set ["taskId", _taskID];
_entry set ["taskID", _taskID];
_entry set ["type", _entry getOrDefault ["type", "dispatch_order"]];
@@ -136,6 +188,23 @@ GVAR(AssignmentRepositoryBaseClass) = compileFinal createHashMapFromArray [
_entry set ["assignmentState", [_assignment getOrDefault ["state", ""], "unassigned"] select (_assignment isEqualTo createHashMap)];
_entry
}],
+ ["buildDispatchOrderEntryForTask", compileFinal {
+ params [
+ ["_taskID", "", [""]],
+ ["_groupRepository", createHashMap, [createHashMap]]
+ ];
+
+ if (_taskID isEqualTo "") exitWith { createHashMap };
+
+ private _state = _self call ["loadState", []];
+ if !(_state getOrDefault ["success", false]) exitWith { createHashMap };
+
+ private _order = +((_state getOrDefault ["dispatchOrders", createHashMap]) getOrDefault [_taskID, createHashMap]);
+ if (_order isEqualTo createHashMap) exitWith { createHashMap };
+
+ private _assignment = +((_state getOrDefault ["assignments", createHashMap]) getOrDefault [_taskID, createHashMap]);
+ _self call ["buildDispatchOrderEntry", [_taskID, _order, _assignment, _groupRepository]]
+ }],
["assignTaskToGroup", compileFinal {
params [
["_requesterUid", "", [""]],
@@ -150,16 +219,20 @@ GVAR(AssignmentRepositoryBaseClass) = compileFinal createHashMapFromArray [
["assignment", createHashMap]
];
- _self call ["restorePersistedState", []];
-
private _permissionService = _self getOrDefault ["permissionService", createHashMap];
if !(_permissionService call ["canDispatch", [_requesterUid]]) exitWith {
_result set ["message", "You are not authorized to assign contracts."];
_result
};
- private _assignmentRegistry = _self getOrDefault ["assignmentRegistry", createHashMap];
- private _dispatchOrderRegistry = _self getOrDefault ["dispatchOrderRegistry", createHashMap];
+ private _state = _self call ["loadState", []];
+ if !(_state getOrDefault ["success", false]) exitWith {
+ _result set ["message", "CAD extension state is unavailable."];
+ _result
+ };
+
+ private _assignmentRegistry = _state getOrDefault ["assignments", createHashMap];
+ private _dispatchOrderRegistry = _state getOrDefault ["dispatchOrders", createHashMap];
private _isDispatchOrder = (_dispatchOrderRegistry getOrDefault [_taskID, createHashMap]) isNotEqualTo createHashMap;
if (!_isDispatchOrder && { (EGVAR(task,TaskStore) call ["getTaskStatus", [_taskID]]) isNotEqualTo "active" }) exitWith {
@@ -221,9 +294,6 @@ GVAR(AssignmentRepositoryBaseClass) = compileFinal createHashMapFromArray [
_result
};
- _assignmentRegistry set [_taskID, _assignment];
- _self set ["assignmentRegistry", _assignmentRegistry];
-
private _activityEntry = +(_assignData getOrDefault ["activity", createHashMap]);
if (_activityEntry isNotEqualTo createHashMap) then {
private _activityRepository = _self getOrDefault ["activityRepository", createHashMap];
@@ -235,6 +305,9 @@ GVAR(AssignmentRepositoryBaseClass) = compileFinal createHashMapFromArray [
_result set ["assignment", _assignment];
_result set ["leaderUid", _leaderUid];
_result set ["isDispatchOrder", _isDispatchOrder];
+ if (_isDispatchOrder) then {
+ _result set ["order", +(_dispatchOrderRegistry getOrDefault [_taskID, createHashMap])];
+ };
_result
}],
["createDispatchOrder", compileFinal {
@@ -254,8 +327,6 @@ GVAR(AssignmentRepositoryBaseClass) = compileFinal createHashMapFromArray [
["order", createHashMap]
];
- _self call ["restorePersistedState", []];
-
private _permissionService = _self getOrDefault ["permissionService", createHashMap];
if !(_permissionService call ["canDispatch", [_requesterUid]]) exitWith {
_result set ["message", "You are not authorized to create dispatch orders."];
@@ -337,14 +408,6 @@ GVAR(AssignmentRepositoryBaseClass) = compileFinal createHashMapFromArray [
_result
};
- private _dispatchOrderRegistry = _self getOrDefault ["dispatchOrderRegistry", createHashMap];
- _dispatchOrderRegistry set [_taskID, _order];
- _self set ["dispatchOrderRegistry", _dispatchOrderRegistry];
-
- private _assignmentRegistry = _self getOrDefault ["assignmentRegistry", createHashMap];
- _assignmentRegistry set [_taskID, _assignment];
- _self set ["assignmentRegistry", _assignmentRegistry];
-
private _activityEntry = +(_createData getOrDefault ["activity", createHashMap]);
if (_activityEntry isNotEqualTo createHashMap) then {
private _activityRepository = _self getOrDefault ["activityRepository", createHashMap];
@@ -368,23 +431,25 @@ GVAR(AssignmentRepositoryBaseClass) = compileFinal createHashMapFromArray [
["assignment", createHashMap]
];
- _self call ["restorePersistedState", []];
-
private _permissionService = _self getOrDefault ["permissionService", createHashMap];
if !(_permissionService call ["canDispatch", [_requesterUid]]) exitWith {
_result set ["message", "You are not authorized to close dispatch orders."];
_result
};
- private _dispatchOrderRegistry = _self getOrDefault ["dispatchOrderRegistry", createHashMap];
- private _order = +(_dispatchOrderRegistry getOrDefault [_taskID, createHashMap]);
+ private _state = _self call ["loadState", []];
+ if !(_state getOrDefault ["success", false]) exitWith {
+ _result set ["message", "CAD extension state is unavailable."];
+ _result
+ };
+
+ private _order = +((_state getOrDefault ["dispatchOrders", createHashMap]) getOrDefault [_taskID, createHashMap]);
if (_order isEqualTo createHashMap) exitWith {
_result set ["message", "Dispatch order could not be resolved."];
_result
};
- private _assignmentRegistry = _self getOrDefault ["assignmentRegistry", createHashMap];
- private _assignment = +(_assignmentRegistry getOrDefault [_taskID, createHashMap]);
+ private _assignment = +((_state getOrDefault ["assignments", createHashMap]) getOrDefault [_taskID, createHashMap]);
private _persistenceService = _self getOrDefault ["persistenceService", createHashMap];
if (_persistenceService isEqualTo createHashMap) exitWith {
@@ -399,14 +464,8 @@ GVAR(AssignmentRepositoryBaseClass) = compileFinal createHashMapFromArray [
};
private _closeData = +(_closeResult getOrDefault ["data", createHashMap]);
- _order = +(_closeData getOrDefault ["order", _order]);
_assignment = +(_closeData getOrDefault ["assignment", _assignment]);
- _dispatchOrderRegistry deleteAt _taskID;
- _self set ["dispatchOrderRegistry", _dispatchOrderRegistry];
- _assignmentRegistry deleteAt _taskID;
- _self set ["assignmentRegistry", _assignmentRegistry];
-
private _activityEntry = +(_closeData getOrDefault ["activity", createHashMap]);
if (_activityEntry isNotEqualTo createHashMap) then {
_activityEntry set ["actorUid", _requesterUid];
@@ -430,12 +489,14 @@ GVAR(AssignmentRepositoryBaseClass) = compileFinal createHashMapFromArray [
];
private _transition = _this param [2, "acknowledge", [""]];
+ private _state = _self call ["loadState", []];
+ if !(_state getOrDefault ["success", false]) exitWith {
+ _result set ["message", "CAD extension state is unavailable."];
+ _result
+ };
- _self call ["restorePersistedState", []];
-
- private _assignmentRegistry = _self getOrDefault ["assignmentRegistry", createHashMap];
- private _assignment = +(_assignmentRegistry getOrDefault [_taskID, createHashMap]);
- private _isDispatchOrder = _self call ["isDispatchOrder", [_taskID]];
+ private _assignment = +((_state getOrDefault ["assignments", createHashMap]) getOrDefault [_taskID, createHashMap]);
+ private _isDispatchOrder = ((_state getOrDefault ["dispatchOrders", createHashMap]) getOrDefault [_taskID, createHashMap]) isNotEqualTo createHashMap;
if (_assignment isEqualTo createHashMap) exitWith {
_result set ["message", "Task is not assigned."];
_result
@@ -508,16 +569,6 @@ GVAR(AssignmentRepositoryBaseClass) = compileFinal createHashMapFromArray [
_result
};
- switch (_transition) do {
- case "decline": {
- _assignmentRegistry deleteAt _taskID;
- };
- default {
- _assignmentRegistry set [_taskID, _assignment];
- };
- };
- _self set ["assignmentRegistry", _assignmentRegistry];
-
private _activityEntry = +(_transitionData getOrDefault ["activity", createHashMap]);
if (_activityEntry isNotEqualTo createHashMap) then {
if (_isDispatchOrder) then {
diff --git a/arma/server/addons/cad/functions/fnc_initCadStore.sqf b/arma/server/addons/cad/functions/fnc_initCadStore.sqf
index 07db1f0..ad70578 100644
--- a/arma/server/addons/cad/functions/fnc_initCadStore.sqf
+++ b/arma/server/addons/cad/functions/fnc_initCadStore.sqf
@@ -10,6 +10,11 @@
* Initializes the CAD store as a coordinator over activity, group,
* assignment, and permission domain objects.
*
+ * CAD operational state is extension-backed but intentionally transient.
+ * Orders, requests, assignments, hydrate state, and recent activity are
+ * scoped to the active server/mission lifecycle and start fresh after a
+ * restart.
+ *
* Arguments:
* None
*
@@ -133,17 +138,13 @@ GVAR(CadStoreBaseClass) = compileFinal createHashMapFromArray [
private _leaderUid = _result getOrDefault ["leaderUid", ""];
if (_leaderUid isEqualTo "") exitWith { false };
+ private _assignmentRepository = _self get "AssignmentRepository";
private _message = if (_result getOrDefault ["isDispatchOrder", false]) then {
private _order = _result getOrDefault ["order", createHashMap];
if (_order isEqualTo createHashMap) then {
private _assignment = _result getOrDefault ["assignment", createHashMap];
private _taskID = _assignment getOrDefault ["taskId", ""];
- _order = (_self get "AssignmentRepository") call ["buildDispatchOrderEntry", [
- _taskID,
- ((_self get "AssignmentRepository") getOrDefault ["dispatchOrderRegistry", createHashMap]) getOrDefault [_taskID, createHashMap],
- (_self get "AssignmentRepository") getOrDefault ["assignmentRegistry", createHashMap],
- _self get "GroupRepository"
- ]];
+ _order = _assignmentRepository call ["buildDispatchOrderEntryForTask", [_taskID, _self get "GroupRepository"]];
};
format ["Dispatch order assigned: %1. Open CAD to review and acknowledge.", _order getOrDefault ["title", "Dispatch Order"]]
@@ -203,15 +204,10 @@ GVAR(CadStoreBaseClass) = compileFinal createHashMapFromArray [
private _permissionService = _self get "PermissionService";
private _groupRepository = _self get "GroupRepository";
- private _actor = EGVAR(actor,Registry) getOrDefault [_uid, createHashMap];
- if (_actor isEqualTo createHashMap && { _uid isNotEqualTo "" }) then {
- _actor = EGVAR(actor,ActorStore) call ["init", [_uid]];
- };
-
private _groupID = _groupRepository call ["getPlayerGroupId", [_uid]];
private _session = createHashMapFromArray [
["uid", _uid],
- ["orgId", _actor getOrDefault ["organization", "default"]],
+ ["orgId", EGVAR(actor,ActorStore) call ["getOrganization", [_uid]]],
["isDispatcher", _permissionService call ["canDispatch", [_uid]]],
["groupId", _groupID],
["isLeader", _groupRepository call ["isGroupLeader", [_uid, _groupID]]]
diff --git a/arma/server/addons/cad/functions/fnc_initGroupRepository.sqf b/arma/server/addons/cad/functions/fnc_initGroupRepository.sqf
index 3a119a7..9d6f8dc 100644
--- a/arma/server/addons/cad/functions/fnc_initGroupRepository.sqf
+++ b/arma/server/addons/cad/functions/fnc_initGroupRepository.sqf
@@ -24,8 +24,6 @@
GVAR(GroupRepositoryBaseClass) = compileFinal createHashMapFromArray [
["#type", "CadGroupRepositoryBaseClass"],
["#create", compileFinal {
- _self set ["groupRegistry", createHashMap];
- _self set ["groupProfileRegistry", createHashMap];
_self set ["validStatuses", [
"available",
"en_route",
@@ -63,31 +61,11 @@ GVAR(GroupRepositoryBaseClass) = compileFinal createHashMapFromArray [
if (_groupID isEqualTo "") exitWith { "" };
private _assignmentRepository = _self getOrDefault ["assignmentRepository", createHashMap];
- private _assignmentRegistry = _assignmentRepository getOrDefault ["assignmentRegistry", createHashMap];
- private _dispatchOrderRegistry = _assignmentRepository getOrDefault ["dispatchOrderRegistry", createHashMap];
- private _taskID = "";
+ if (_assignmentRepository isEqualTo createHashMap) exitWith { "" };
- {
- if ((_y getOrDefault ["groupId", ""]) isNotEqualTo _groupID) then { continue; };
- if !((_y getOrDefault ["state", ""]) in ["assigned", "acknowledged"]) then { continue; };
- private _dispatchOrder = +(_dispatchOrderRegistry getOrDefault [_x, createHashMap]);
- if (_dispatchOrder isEqualTo createHashMap) then {
- if ((EGVAR(task,TaskStore) call ["getTaskStatus", [_x]]) isNotEqualTo "active") then { continue; };
- _taskID = _x;
- } else {
- _taskID = _dispatchOrder getOrDefault ["title", _x];
- };
-
- } forEach _assignmentRegistry;
-
- _taskID
+ _assignmentRepository call ["getCurrentTaskIdForGroup", [_groupID]]
}],
["syncGroups", compileFinal {
- private _assignmentRepository = _self getOrDefault ["assignmentRepository", createHashMap];
- if (_assignmentRepository isNotEqualTo createHashMap) then {
- _assignmentRepository call ["restorePersistedState", []];
- };
-
private _liveGroups = [];
{
@@ -106,16 +84,9 @@ GVAR(GroupRepositoryBaseClass) = compileFinal createHashMapFromArray [
if (_groupID isEqualTo "") then { continue; };
private _leaderUid = getPlayerUID _leader;
- private _actor = EGVAR(actor,Registry) getOrDefault [_leaderUid, createHashMap];
- if (_actor isEqualTo createHashMap && { _leaderUid isNotEqualTo "" }) then {
- _actor = EGVAR(actor,ActorStore) call ["init", [_leaderUid]];
- };
-
- private _orgID = _actor getOrDefault ["organization", "default"];
- if (_orgID isEqualTo "") then { _orgID = "default"; };
+ private _orgID = EGVAR(actor,ActorStore) call ["getOrganization", [_leaderUid]];
private _memberUids = [];
private _memberRoster = [];
-
{
private _memberUid = getPlayerUID _x;
private _memberState = toLowerANSI (lifeState _x);
@@ -158,7 +129,6 @@ GVAR(GroupRepositoryBaseClass) = compileFinal createHashMapFromArray [
};
private _nextRegistry = createHashMap;
- private _profileRegistry = createHashMap;
{
if !(_x isEqualType createHashMap) then { continue; };
private _groupID = _x getOrDefault ["groupId", ""];
@@ -166,15 +136,8 @@ GVAR(GroupRepositoryBaseClass) = compileFinal createHashMapFromArray [
private _groupRecord = +_x;
_nextRegistry set [_groupID, _groupRecord];
- _profileRegistry set [_groupID, createHashMapFromArray [
- ["groupId", _groupID],
- ["role", _groupRecord getOrDefault ["role", "infantry"]],
- ["status", _groupRecord getOrDefault ["status", "available"]]
- ]];
} forEach _mergedGroups;
- _self set ["groupProfileRegistry", _profileRegistry];
- _self set ["groupRegistry", _nextRegistry];
_nextRegistry
}],
["getGroupRecord", compileFinal {
@@ -309,12 +272,6 @@ GVAR(GroupRepositoryBaseClass) = compileFinal createHashMapFromArray [
_groupRecord set ["status", _profile getOrDefault ["status", _groupRecord getOrDefault ["status", "available"]]];
_groupRecord set ["lastUpdate", serverTime];
- private _profileRegistry = _self getOrDefault ["groupProfileRegistry", createHashMap];
- _groupRegistry set [_groupID, _groupRecord];
- _self set ["groupRegistry", _groupRegistry];
- _profileRegistry set [_groupID, _profile];
- _self set ["groupProfileRegistry", _profileRegistry];
-
private _activityEntry = +(_profileData getOrDefault ["activity", createHashMap]);
if (_activityEntry isNotEqualTo createHashMap) then {
private _activityRepository = _self getOrDefault ["activityRepository", createHashMap];
diff --git a/arma/server/addons/cad/functions/fnc_initPermissionService.sqf b/arma/server/addons/cad/functions/fnc_initPermissionService.sqf
index 07fea10..f27f215 100644
--- a/arma/server/addons/cad/functions/fnc_initPermissionService.sqf
+++ b/arma/server/addons/cad/functions/fnc_initPermissionService.sqf
@@ -27,10 +27,7 @@ GVAR(PermissionServiceBaseClass) = compileFinal createHashMapFromArray [
if (_uid isEqualTo "") exitWith { false };
- private _actor = EGVAR(actor,Registry) getOrDefault [_uid, createHashMap];
- if (_actor isEqualTo createHashMap) exitWith { false };
-
- private _orgID = _actor getOrDefault ["organization", "default"];
+ private _orgID = EGVAR(actor,ActorStore) call ["getOrganization", [_uid]];
private _org = EGVAR(org,OrgStore) call ["loadById", [_orgID]];
if (_org isEqualTo createHashMap) exitWith { false };
diff --git a/arma/server/addons/cad/functions/fnc_initPersistenceService.sqf b/arma/server/addons/cad/functions/fnc_initPersistenceService.sqf
index dd4c053..e1ad1e1 100644
--- a/arma/server/addons/cad/functions/fnc_initPersistenceService.sqf
+++ b/arma/server/addons/cad/functions/fnc_initPersistenceService.sqf
@@ -10,6 +10,10 @@
* Initializes the CAD extension-state service that bridges live SQF
* state to the Rust extension for hot CAD storage and recent history.
*
+ * This is a live operational cache, not a durable persistence layer.
+ * CAD extension state is expected to reset with the current server or
+ * mission lifecycle.
+ *
* Arguments:
* None
*
diff --git a/arma/server/addons/cad/functions/fnc_initRequestRepository.sqf b/arma/server/addons/cad/functions/fnc_initRequestRepository.sqf
index 7a6f89b..eeedad4 100644
--- a/arma/server/addons/cad/functions/fnc_initRequestRepository.sqf
+++ b/arma/server/addons/cad/functions/fnc_initRequestRepository.sqf
@@ -24,8 +24,6 @@
GVAR(RequestRepositoryBaseClass) = compileFinal createHashMapFromArray [
["#type", "CadRequestRepositoryBaseClass"],
["#create", compileFinal {
- _self set ["requestRegistry", createHashMap];
- _self set ["persistenceLoaded", false];
_self set ["validTypes", [
"medevac_9line",
"ace_lace",
@@ -39,20 +37,14 @@ GVAR(RequestRepositoryBaseClass) = compileFinal createHashMapFromArray [
"emergency"
]];
}],
- ["restorePersistedState", compileFinal {
- if (_self getOrDefault ["persistenceLoaded", false]) exitWith { true };
-
+ ["loadRequestRegistry", compileFinal {
private _persistenceService = _self getOrDefault ["persistenceService", createHashMap];
- if (_persistenceService isEqualTo createHashMap) exitWith { false };
+ if (_persistenceService isEqualTo createHashMap) exitWith { createHashMap };
private _result = _persistenceService call ["loadRequests", []];
- if !(_result getOrDefault ["success", false]) exitWith { false };
+ if !(_result getOrDefault ["success", false]) exitWith { createHashMap };
- private _requestRegistry = +(_result getOrDefault ["data", createHashMap]);
-
- _self set ["requestRegistry", _requestRegistry];
- _self set ["persistenceLoaded", true];
- true
+ +(_result getOrDefault ["data", createHashMap])
}],
["submitRequest", compileFinal {
params [
@@ -68,8 +60,6 @@ GVAR(RequestRepositoryBaseClass) = compileFinal createHashMapFromArray [
["request", createHashMap]
];
- _self call ["restorePersistedState", []];
-
private _finalType = toLowerANSI _type;
if !(_finalType in (_self getOrDefault ["validTypes", []])) exitWith {
_result set ["message", "Invalid support request type."];
@@ -132,10 +122,6 @@ GVAR(RequestRepositoryBaseClass) = compileFinal createHashMapFromArray [
_result
};
- private _requestRegistry = _self getOrDefault ["requestRegistry", createHashMap];
- _requestRegistry set [_requestID, _request];
- _self set ["requestRegistry", _requestRegistry];
-
private _activityEntry = +(_submitData getOrDefault ["activity", createHashMap]);
if (_activityEntry isNotEqualTo createHashMap) then {
private _activityRepository = _self getOrDefault ["activityRepository", createHashMap];
@@ -156,9 +142,7 @@ GVAR(RequestRepositoryBaseClass) = compileFinal createHashMapFromArray [
["request", createHashMap]
];
- _self call ["restorePersistedState", []];
-
- private _requestRegistry = _self getOrDefault ["requestRegistry", createHashMap];
+ private _requestRegistry = _self call ["loadRequestRegistry", []];
private _request = +(_requestRegistry getOrDefault [_requestID, createHashMap]);
if (_request isEqualTo createHashMap) exitWith {
_result set ["message", "Support request could not be resolved."];
@@ -188,8 +172,6 @@ GVAR(RequestRepositoryBaseClass) = compileFinal createHashMapFromArray [
private _closeData = +(_closeResult getOrDefault ["data", createHashMap]);
_request = +(_closeData getOrDefault ["request", _request]);
- _requestRegistry deleteAt _requestID;
- _self set ["requestRegistry", _requestRegistry];
private _activityEntry = +(_closeData getOrDefault ["activity", createHashMap]);
if (_activityEntry isNotEqualTo createHashMap) then {
diff --git a/arma/server/addons/economy/functions/fnc_initFEconomyStore.sqf b/arma/server/addons/economy/functions/fnc_initFEconomyStore.sqf
index 830cee0..613ef18 100644
--- a/arma/server/addons/economy/functions/fnc_initFEconomyStore.sqf
+++ b/arma/server/addons/economy/functions/fnc_initFEconomyStore.sqf
@@ -25,7 +25,7 @@ GVAR(FEconomyStore) = createHashMapObject [[
["#type", "IFuelEconomy"],
["#create", {
GVAR(FuelCost) = 5;
- GVAR(FuelRegistry) = createHashMap;
+ _self set ["fuelRegistry", createHashMap];
["INFO", "Fuel Store Initialized!", nil, nil] call EFUNC(common,log);
}],
@@ -34,15 +34,17 @@ GVAR(FEconomyStore) = createHashMapObject [[
private _index = netId _target;
private _uid = getPlayerUID _unit;
+ private _fuelRegistry = _self getOrDefault ["fuelRegistry", createHashMap];
- GVAR(FuelRegistry) set [_index, _uid];
+ _fuelRegistry set [_index, _uid];
SETVAR(_target,liters,0);
}],
["stop", {
params ["_source", "_target"];
private _index = netId _target;
- private _uid = GVAR(FuelRegistry) get _index;
+ private _fuelRegistry = _self getOrDefault ["fuelRegistry", createHashMap];
+ private _uid = _fuelRegistry get _index;
private _player = [_uid] call EFUNC(common,getPlayer);
private _totalLiters = GETVAR(_target,liters,0);
@@ -51,7 +53,7 @@ GVAR(FEconomyStore) = createHashMapObject [[
private _formattedTotalLiters = _totalLiters toFixed 2;
[CRPC(notifications,recieveNotification), ["info", "Refueling", format ["Refueling complete: %1L
Total Cost: $%2", _formattedTotalLiters, _formattedTotalCost]], _player] call CFUNC(targetEvent);
- GVAR(FuelRegistry) deleteAt _index;
+ _fuelRegistry deleteAt _index;
}]
]];
diff --git a/arma/server/addons/extension/functions/fnc_extCall.sqf b/arma/server/addons/extension/functions/fnc_extCall.sqf
index e91ae1a..7d7ec29 100644
--- a/arma/server/addons/extension/functions/fnc_extCall.sqf
+++ b/arma/server/addons/extension/functions/fnc_extCall.sqf
@@ -37,6 +37,7 @@ private _transportResponseFunctions = [
"actor:update",
"actor:hot:init",
"actor:hot:get",
+ "actor:hot:keys",
"actor:hot:save",
"bank:get",
"bank:create",
diff --git a/arma/server/addons/locker/functions/fnc_initVAStore.sqf b/arma/server/addons/locker/functions/fnc_initVAStore.sqf
index 567010b..ecbc234 100644
--- a/arma/server/addons/locker/functions/fnc_initVAStore.sqf
+++ b/arma/server/addons/locker/functions/fnc_initVAStore.sqf
@@ -4,7 +4,7 @@
* File: fnc_initVAStore.sqf
* Author: IDSolutions
* Date: 2025-12-17
- * Last Update: 2026-04-01
+ * Last Update: 2026-04-05
* Public: No
*
* Description:
@@ -28,7 +28,7 @@ GVAR(VArsenalModel) = compileFinal createHashMapObject [[
private _vArsenal = createHashMap;
_vArsenal set ["backpacks", ["B_AssaultPack_rgr"]];
- _vArsenal set ["items", ["FirstAidKit", "G_Combat", "H_Cap_blk_ION", "H_HelmetB", "ItemCompass", "ItemGPS", "ItemMap", "ItemRadio", "ItemWatch", "U_IG_Guerrilla_6_1", "V_TacVest_oli", "ACE_EarPlugs"]];
+ _vArsenal set ["items", ["FirstAidKit", "G_Combat", "H_Cap_blk_ION", "H_HelmetB", "ItemCompass", "ItemGPS", "ItemMap", "ItemRadio", "ItemWatch", "U_BG_Guerrilla_6_1", "V_TacVest_oli", "ACE_EarPlugs"]];
_vArsenal set ["magazines", ["16Rnd_9x21_Mag", "30Rnd_65x39_caseless_black_mag", "Chemlight_blue", "Chemlight_green", "Chemlight_red", "Chemlight_yellow", "HandGrenade", "SmokeShell", "SmokeShellBlue", "SmokeShellGreen", "SmokeShellOrange", "SmokeShellPurple", "SmokeShellRed", "SmokeShellYellow"]];
_vArsenal set ["weapons", ["arifle_MX_F", "hgun_P07_F"]];
diff --git a/arma/server/addons/main/XEH_PREP.hpp b/arma/server/addons/main/XEH_PREP.hpp
index 3a1cf90..f4df82a 100644
--- a/arma/server/addons/main/XEH_PREP.hpp
+++ b/arma/server/addons/main/XEH_PREP.hpp
@@ -1,2 +1,3 @@
PREP(initStores);
+PREP(initValidationHarness);
PREP(saveHotState);
diff --git a/arma/server/addons/main/XEH_preInit.sqf b/arma/server/addons/main/XEH_preInit.sqf
index 929f1ff..be9e270 100644
--- a/arma/server/addons/main/XEH_preInit.sqf
+++ b/arma/server/addons/main/XEH_preInit.sqf
@@ -4,8 +4,6 @@ PREP_RECOMPILE_START;
#include "XEH_PREP.hpp"
PREP_RECOMPILE_END;
-GVAR(PlayerBootstrapRegistry) = createHashMap;
-
["forge_icom_event", {
params [["_event", "", [""]], ["_data", createHashMap, [createHashMap]]];
diff --git a/arma/server/addons/main/functions/fnc_initStores.sqf b/arma/server/addons/main/functions/fnc_initStores.sqf
index 69f5259..d1b9275 100644
--- a/arma/server/addons/main/functions/fnc_initStores.sqf
+++ b/arma/server/addons/main/functions/fnc_initStores.sqf
@@ -47,3 +47,6 @@ if (isNil QEGVAR(org,OrgStore)) then { call EFUNC(org,initOrgStore); };
// Store
if (isNil QEGVAR(store,StoreStore)) then { call EFUNC(store,initStoreStore); };
+
+// Validation Harness
+if (isNil QGVAR(ValidationHarness)) then { call FUNC(initValidationHarness); };
diff --git a/arma/server/addons/main/functions/fnc_initValidationHarness.sqf b/arma/server/addons/main/functions/fnc_initValidationHarness.sqf
new file mode 100644
index 0000000..786531b
--- /dev/null
+++ b/arma/server/addons/main/functions/fnc_initValidationHarness.sqf
@@ -0,0 +1,213 @@
+#include "..\script_component.hpp"
+
+/*
+ * Author: IDSolutions
+ * Initializes the server-side validation harness for targeted runtime smoke
+ * checks around high-risk multi-module flows.
+ *
+ * Arguments:
+ * None
+ *
+ * Return Value:
+ * Validation harness object
+ *
+ * Example:
+ * call forge_server_main_fnc_initValidationHarness;
+ *
+ * Public: No
+ */
+
+#pragma hemtt ignore_variables ["_self"]
+GVAR(ValidationHarness) = createHashMapObject [[
+ ["#type", "ValidationHarness"],
+ ["buildResult", compileFinal {
+ params [
+ ["_action", "", [""]],
+ ["_success", false, [false]],
+ ["_message", "", [""]],
+ ["_data", createHashMap, [createHashMap]]
+ ];
+
+ createHashMapFromArray [
+ ["action", _action],
+ ["success", _success],
+ ["message", _message],
+ ["data", _data]
+ ]
+ }],
+ ["logResult", compileFinal {
+ params [["_result", createHashMap, [createHashMap]]];
+
+ if (_result isEqualTo createHashMap) exitWith { _result };
+
+ private _level = ["WARNING", "INFO"] select (_result getOrDefault ["success", false]);
+ private _action = _result getOrDefault ["action", "validation"];
+ private _message = _result getOrDefault ["message", ""];
+ [_level, format ["Validation harness '%1': %2", _action, _message]] call EFUNC(common,log);
+
+ _result
+ }],
+ ["normalizeMapArg", compileFinal {
+ params [
+ ["_value", createHashMap, [createHashMap, ""]],
+ ["_fallback", createHashMap, [createHashMap]]
+ ];
+
+ if (_value isEqualType createHashMap) exitWith { +_value };
+ if !(_value isEqualType "") exitWith { +_fallback };
+ if (_value isEqualTo "") exitWith { +_fallback };
+
+ private _parsed = fromJSON _value;
+ if !(_parsed isEqualType createHashMap) exitWith { +_fallback };
+
+ _parsed
+ }],
+ ["run", compileFinal {
+ params [["_action", "", [""]], ["_arguments", [], [[]]]];
+
+ private _actionLower = toLowerANSI _action;
+ if (_actionLower isEqualTo "") exitWith {
+ _self call ["logResult", [_self call ["buildResult", ["unknown", false, "A validation action is required.", createHashMap]]]]
+ };
+
+ switch (_actionLower) do {
+ case "save_hot_state": {
+ _arguments params [["_uid", "", [""]]];
+
+ private _success = [_uid] call FUNC(saveHotState);
+ private _message = [
+ format ["Hot-state save failed for '%1'.", _uid],
+ format ["Hot-state save completed for '%1'.", [_uid, "all hot state"] select (_uid isEqualTo "")]
+ ] select _success;
+
+ _self call ["logResult", [_self call ["buildResult", [
+ _actionLower,
+ _success,
+ _message,
+ createHashMapFromArray [["uid", _uid]]
+ ]]]]
+ };
+ case "store_checkout": {
+ _arguments params [["_uid", "", [""]], ["_payload", createHashMap, [createHashMap, ""]]];
+
+ private _player = [_uid] call EFUNC(common,getPlayer);
+ if (_uid isEqualTo "" || { isNull _player }) exitWith {
+ _self call ["logResult", [_self call ["buildResult", [_actionLower, false, "A valid online player UID is required for store checkout validation.", createHashMap]]]]
+ };
+
+ private _payloadMap = _self call ["normalizeMapArg", [_payload, createHashMap]];
+ if (_payloadMap isEqualTo createHashMap) exitWith {
+ _self call ["logResult", [_self call ["buildResult", [_actionLower, false, "Store checkout validation payload was invalid.", createHashMap]]]]
+ };
+
+ private _result = EGVAR(store,StoreStore) call ["checkout", [_uid, _player, toJSON _payloadMap]];
+ private _success = _result getOrDefault ["success", false];
+ private _message = _result getOrDefault ["message", "Store checkout validation completed."];
+
+ _self call ["logResult", [_self call ["buildResult", [_actionLower, _success, _message, _result]]]]
+ };
+ case "org_assign_credit_line": {
+ _arguments params [
+ ["_requesterUid", "", [""]],
+ ["_memberUid", "", [""]],
+ ["_memberName", "", [""]],
+ ["_amount", 0, [0]]
+ ];
+
+ private _result = EGVAR(org,OrgStore) call ["assignCreditLine", [_requesterUid, _memberUid, _memberName, _amount]];
+ private _success = _result getOrDefault ["success", false];
+ private _message = _result getOrDefault ["message", "Credit line validation completed."];
+
+ _self call ["logResult", [_self call ["buildResult", [_actionLower, _success, _message, _result]]]]
+ };
+ case "bank_credit_repayment": {
+ _arguments params [["_uid", "", [""]], ["_amount", 0, [0]]];
+
+ if (_uid isEqualTo "") exitWith {
+ _self call ["logResult", [_self call ["buildResult", [_actionLower, false, "A valid UID is required for bank credit repayment validation.", createHashMap]]]]
+ };
+
+ private _beforeAccount = EGVAR(bank,BankStore) call ["get", [_uid, ""]];
+ private _beforeOrgState = EGVAR(bank,BankPayloadBuilder) call ["resolveOrgState", [_uid]];
+ private _success = EGVAR(bank,BankStore) call ["repayCreditLine", [_uid, _amount]];
+ private _afterAccount = EGVAR(bank,BankStore) call ["get", [_uid, ""]];
+ private _afterOrgState = EGVAR(bank,BankPayloadBuilder) call ["resolveOrgState", [_uid]];
+
+ private _message = [
+ format ["Bank credit repayment validation failed for %1.", _uid],
+ format ["Bank credit repayment validation completed for %1.", _uid]
+ ] select _success;
+
+ _self call ["logResult", [_self call ["buildResult", [
+ _actionLower,
+ _success,
+ _message,
+ createHashMapFromArray [
+ ["beforeAccount", _beforeAccount],
+ ["afterAccount", _afterAccount],
+ ["beforeOrgState", _beforeOrgState],
+ ["afterOrgState", _afterOrgState]
+ ]
+ ]]]]
+ };
+ case "task_reward_context": {
+ _arguments params [["_taskID", "", [""]]];
+
+ private _context = EGVAR(task,TaskStore) call ["resolveRewardContext", [_taskID]];
+ private _success = _taskID isNotEqualTo "" && { (_context getOrDefault ["orgID", ""]) isNotEqualTo "" };
+ private _message = [
+ format ["No reward context was available for task %1.", _taskID],
+ format ["Resolved reward context for task %1.", _taskID]
+ ] select _success;
+
+ _self call ["logResult", [_self call ["buildResult", [_actionLower, _success, _message, _context]]]]
+ };
+ case "task_apply_rating": {
+ _arguments params [["_taskID", "", [""]], ["_delta", 0, [0]]];
+
+ private _result = EGVAR(task,TaskStore) call ["applyRatingOutcome", [_taskID, _delta]];
+ private _success = _result getOrDefault ["success", true];
+ private _message = [
+ _result getOrDefault ["message", format ["Task rating validation failed for %1.", _taskID]],
+ format ["Task rating validation completed for %1.", _taskID]
+ ] select _success;
+
+ _self call ["logResult", [_self call ["buildResult", [_actionLower, _success, _message, _result]]]]
+ };
+ case "task_apply_rewards": {
+ _arguments params [["_taskID", "", [""]], ["_rewards", createHashMap, [createHashMap, ""]]];
+
+ private _rewardsMap = _self call ["normalizeMapArg", [_rewards, createHashMap]];
+ if (_taskID isEqualTo "" || { _rewardsMap isEqualTo createHashMap }) exitWith {
+ _self call ["logResult", [_self call ["buildResult", [_actionLower, false, "Task reward validation requires a task ID and reward payload.", createHashMap]]]]
+ };
+
+ private _rewardContext = EGVAR(task,TaskStore) call ["resolveRewardContext", [_taskID]];
+ private _beforeOrg = EGVAR(org,OrgStore) call ["loadById", [_rewardContext getOrDefault ["orgID", ""]]];
+ private _success = [_taskID, _rewardsMap] call EFUNC(task,handleTaskRewards);
+ private _afterOrg = EGVAR(org,OrgStore) call ["loadById", [_rewardContext getOrDefault ["orgID", ""]]];
+
+ private _message = [
+ format ["Task reward validation failed for %1.", _taskID],
+ format ["Task reward validation completed for %1.", _taskID]
+ ] select _success;
+
+ _self call ["logResult", [_self call ["buildResult", [
+ _actionLower,
+ _success,
+ _message,
+ createHashMapFromArray [
+ ["rewardContext", _rewardContext],
+ ["beforeOrg", _beforeOrg],
+ ["afterOrg", _afterOrg]
+ ]
+ ]]]]
+ };
+ default {
+ _self call ["logResult", [_self call ["buildResult", [_actionLower, false, format ["Unknown validation action '%1'.", _actionLower], createHashMap]]]]
+ };
+ };
+ }]
+]];
+
+GVAR(ValidationHarness)
diff --git a/arma/server/addons/main/functions/fnc_saveHotState.sqf b/arma/server/addons/main/functions/fnc_saveHotState.sqf
index 5fbfb98..dc819da 100644
--- a/arma/server/addons/main/functions/fnc_saveHotState.sqf
+++ b/arma/server/addons/main/functions/fnc_saveHotState.sqf
@@ -28,12 +28,12 @@ if (_uid isEqualTo "") then {
};
} forEach allPlayers;
- if !(isNil QEGVAR(actor,Registry)) then {
+ if !(isNil QEGVAR(actor,ActorStore)) then {
{
if (_x isNotEqualTo "") then {
_uids pushBackUnique _x;
};
- } forEach keys EGVAR(actor,Registry);
+ } forEach (EGVAR(actor,ActorStore) call ["listHotUids", []]);
};
} else {
_uids pushBack _uid;
diff --git a/arma/server/addons/org/functions/fnc_initOrgStore.sqf b/arma/server/addons/org/functions/fnc_initOrgStore.sqf
index 230564e..f0de6a0 100644
--- a/arma/server/addons/org/functions/fnc_initOrgStore.sqf
+++ b/arma/server/addons/org/functions/fnc_initOrgStore.sqf
@@ -4,7 +4,7 @@
* File: fnc_initOrgStore.sqf
* Author: IDSolutions
* Date: 2026-02-13
- * Last Update: 2026-04-01
+ * Last Update: 2026-04-04
* Public: Yes
*
* Description:
@@ -256,10 +256,7 @@ GVAR(OrgBaseStore) = compileFinal createHashMapFromArray [
if (_uid isEqualTo "") exitWith { "default" };
- private _actor = EGVAR(actor,Registry) getOrDefault [_uid, createHashMap];
- private _orgID = _actor getOrDefault ["organization", "default"];
- if (_orgID isEqualTo "") then { _orgID = "default"; };
- _orgID
+ EGVAR(actor,ActorStore) call ["getOrganization", [_uid]]
}],
["resolveActorName", compileFinal {
params [["_uid", "", [""]], ["_player", objNull, [objNull]], ["_actor", createHashMap, [createHashMap]]];
@@ -276,8 +273,8 @@ GVAR(OrgBaseStore) = compileFinal createHashMapFromArray [
if (_uid isEqualTo "" || { _orgID isEqualTo "" }) exitWith { createHashMap };
- private _actorPatch = EGVAR(actor,ActorStore) call ["set", [EGVAR(actor,Registry), "actor:update", _uid, "organization", _orgID, false]];
- private _updatedActor = EGVAR(actor,ActorStore) call ["get", [_uid, ""]];
+ private _actorPatch = EGVAR(actor,ActorStore) call ["set", [_uid, "organization", _orgID, false]];
+ private _updatedActor = EGVAR(actor,ActorStore) call ["load", [_uid]];
if (
!(_updatedActor isEqualType createHashMap)
|| { _updatedActor isEqualTo createHashMap }
@@ -375,8 +372,8 @@ GVAR(OrgBaseStore) = compileFinal createHashMapFromArray [
};
private _player = [_uid] call EFUNC(common,getPlayer);
- private _actor = EGVAR(actor,Registry) getOrDefault [_uid, createHashMap];
- private _orgID = _actor getOrDefault ["organization", "default"];
+ private _actor = EGVAR(actor,ActorStore) call ["load", [_uid]];
+ private _orgID = EGVAR(actor,ActorStore) call ["getOrganization", [_uid]];
private _memberName = _self call ["resolveActorName", [_uid, _player, _actor]];
private _context = createHashMapFromArray [
["requesterUid", _uid],
@@ -418,8 +415,8 @@ GVAR(OrgBaseStore) = compileFinal createHashMapFromArray [
};
private _player = [_uid] call EFUNC(common,getPlayer);
- private _actor = EGVAR(actor,Registry) getOrDefault [_uid, createHashMap];
- private _orgID = _actor getOrDefault ["organization", "default"];
+ private _actor = EGVAR(actor,ActorStore) call ["load", [_uid]];
+ private _orgID = EGVAR(actor,ActorStore) call ["getOrganization", [_uid]];
private _memberName = _self call ["resolveActorName", [_uid, _player, _actor]];
private _context = createHashMapFromArray [
["requesterUid", _uid],
@@ -438,7 +435,7 @@ GVAR(OrgBaseStore) = compileFinal createHashMapFromArray [
private _memberUid = _x getOrDefault ["uid", ""];
if (_memberUid isEqualTo "") then { continue; };
- private _memberActor = EGVAR(actor,Registry) getOrDefault [_memberUid, createHashMap];
+ private _memberActor = EGVAR(actor,ActorStore) call ["load", [_memberUid]];
private _actorPatch = _self call ["applyActorOrganization", [_memberUid, _x getOrDefault ["actorOrganization", "default"], _memberActor]];
if (_actorPatch isEqualTo createHashMap) then {
["WARNING", format ["Failed to restore actor organization for %1 after org disband.", _memberUid]] call EFUNC(common,log);
@@ -471,7 +468,9 @@ GVAR(OrgBaseStore) = compileFinal createHashMapFromArray [
["success", false],
["message", ""],
["patch", createHashMap],
- ["memberUids", []]
+ ["memberUids", []],
+ ["persisted", false],
+ ["persistenceMessage", ""]
];
if (_requesterUid isEqualTo "" || { _memberUid isEqualTo "" } || { _amount <= 0 }) exitWith {
@@ -479,10 +478,7 @@ GVAR(OrgBaseStore) = compileFinal createHashMapFromArray [
_result
};
- private _requesterActor = EGVAR(actor,Registry) getOrDefault [_requesterUid, createHashMap];
- private _orgID = _requesterActor getOrDefault ["organization", "default"];
- if (_orgID isEqualTo "") then { _orgID = "default"; };
-
+ private _orgID = EGVAR(actor,ActorStore) call ["getOrganization", [_requesterUid]];
private _requesterPlayer = [_requesterUid] call EFUNC(common,getPlayer);
private _requesterIsDefaultOrgCeo = (
_requesterPlayer isNotEqualTo objNull
@@ -509,7 +505,7 @@ GVAR(OrgBaseStore) = compileFinal createHashMapFromArray [
_result set ["message", _envelope getOrDefault ["message", "Credit line assigned."]];
_result set ["patch", _envelope getOrDefault ["patch", createHashMap]];
_result set ["memberUids", _envelope getOrDefault ["memberUids", []]];
- _result
+ _self call ["persistMutationResult", [_orgID, _result, "Credit line assignment"]]
}],
["repayCreditLine", compileFinal {
params [["_requesterUid", "", [""]], ["_amount", 0, [0]]];
@@ -518,7 +514,9 @@ GVAR(OrgBaseStore) = compileFinal createHashMapFromArray [
["success", false],
["message", ""],
["patch", createHashMap],
- ["memberUids", []]
+ ["memberUids", []],
+ ["persisted", false],
+ ["persistenceMessage", ""]
];
if (_requesterUid isEqualTo "" || { _amount <= 0 }) exitWith {
@@ -526,10 +524,7 @@ GVAR(OrgBaseStore) = compileFinal createHashMapFromArray [
_result
};
- private _requesterActor = EGVAR(actor,Registry) getOrDefault [_requesterUid, createHashMap];
- private _orgID = _requesterActor getOrDefault ["organization", "default"];
- if (_orgID isEqualTo "") then { _orgID = "default"; };
-
+ private _orgID = EGVAR(actor,ActorStore) call ["getOrganization", [_requesterUid]];
private _context = createHashMapFromArray [
["requesterUid", _requesterUid],
["orgId", _orgID],
@@ -546,13 +541,38 @@ GVAR(OrgBaseStore) = compileFinal createHashMapFromArray [
_result set ["message", _envelope getOrDefault ["message", "Credit repayment posted."]];
_result set ["patch", _envelope getOrDefault ["patch", createHashMap]];
_result set ["memberUids", _envelope getOrDefault ["memberUids", []]];
- _result
+ _self call ["persistMutationResult", [_orgID, _result, "Credit repayment"]]
}],
["buildPortalPayload", compileFinal {
params [["_uid", "", [""]]];
GVAR(OrgPayloadBuilder) call ["buildPortalPayload", [_uid]]
}],
+ ["persistMutationResult", compileFinal {
+ params [
+ ["_orgID", "", [""]],
+ ["_result", createHashMap, [createHashMap]],
+ ["_actionLabel", "Organization update", [""]]
+ ];
+
+ if (_orgID isEqualTo "" || { _result isEqualTo createHashMap }) exitWith { _result };
+
+ if !(_result getOrDefault ["success", false]) exitWith { _result };
+
+ _result set ["persisted", false];
+ _result set ["persistenceMessage", ""];
+
+ private _savedOrg = _self call ["saveById", [_orgID]];
+ if (_savedOrg isEqualTo createHashMap) exitWith {
+ private _message = format ["%1 applied, but durable save failed for organization %2.", _actionLabel, _orgID];
+ ["ERROR", _message] call EFUNC(common,log);
+ _result set ["persistenceMessage", _message];
+ _result
+ };
+
+ _result set ["persisted", true];
+ _result
+ }],
["chargeCheckout", compileFinal {
params [["_requesterUid", "", [""]], ["_requesterPlayer", objNull, [objNull]], ["_source", "org_funds", [""]], ["_amount", 0, [0]], ["_commit", false, [false]]];
@@ -560,13 +580,12 @@ GVAR(OrgBaseStore) = compileFinal createHashMapFromArray [
["success", false],
["message", "Unable to process organization payment."],
["patch", createHashMap],
- ["memberUids", []]
+ ["memberUids", []],
+ ["persisted", false],
+ ["persistenceMessage", ""]
];
- private _requesterActor = EGVAR(actor,Registry) getOrDefault [_requesterUid, createHashMap];
- private _orgID = _requesterActor getOrDefault ["organization", "default"];
- if (_orgID isEqualTo "") then { _orgID = "default"; };
-
+ private _orgID = EGVAR(actor,ActorStore) call ["getOrganization", [_requesterUid]];
private _requesterIsDefaultOrgCeo = (
_requesterPlayer isNotEqualTo objNull
&& { _orgID isEqualTo "default" }
@@ -589,7 +608,7 @@ GVAR(OrgBaseStore) = compileFinal createHashMapFromArray [
_result set ["message", _envelope getOrDefault ["message", ""]];
_result set ["patch", _envelope getOrDefault ["patch", createHashMap]];
_result set ["memberUids", _envelope getOrDefault ["memberUids", []]];
- _result
+ _self call ["persistMutationResult", [_orgID, _result, "Organization checkout charge"]]
}],
["saveById", compileFinal {
params [["_orgID", "", [""]]];
@@ -605,7 +624,9 @@ GVAR(OrgBaseStore) = compileFinal createHashMapFromArray [
["success", false],
["message", "Unable to update organization assets."],
["patch", createHashMap],
- ["memberUids", []]
+ ["memberUids", []],
+ ["persisted", false],
+ ["persistenceMessage", ""]
];
if (_assets isEqualTo []) exitWith {
@@ -616,8 +637,7 @@ GVAR(OrgBaseStore) = compileFinal createHashMapFromArray [
private _resolvedOrgID = _orgID;
if (_resolvedOrgID isEqualTo "") then {
- private _requesterActor = EGVAR(actor,Registry) getOrDefault [_requesterUid, createHashMap];
- _resolvedOrgID = _requesterActor getOrDefault ["organization", "default"];
+ _resolvedOrgID = EGVAR(actor,ActorStore) call ["getOrganization", [_requesterUid]];
};
if (_resolvedOrgID isEqualTo "") then { _resolvedOrgID = "default"; };
@@ -644,7 +664,7 @@ GVAR(OrgBaseStore) = compileFinal createHashMapFromArray [
_result set ["message", _envelope getOrDefault ["message", ""]];
_result set ["patch", _envelope getOrDefault ["patch", createHashMap]];
_result set ["memberUids", _envelope getOrDefault ["memberUids", []]];
- _result
+ _self call ["persistMutationResult", [_resolvedOrgID, _result, "Organization asset update"]]
}],
["addFleetVehicles", compileFinal {
params [["_requesterUid", "", [""]], ["_vehicles", [], [[]]], ["_commit", false, [false]], ["_orgID", "", [""]]];
@@ -653,7 +673,9 @@ GVAR(OrgBaseStore) = compileFinal createHashMapFromArray [
["success", false],
["message", "Unable to update organization fleet."],
["patch", createHashMap],
- ["memberUids", []]
+ ["memberUids", []],
+ ["persisted", false],
+ ["persistenceMessage", ""]
];
if (_vehicles isEqualTo []) exitWith {
@@ -664,8 +686,7 @@ GVAR(OrgBaseStore) = compileFinal createHashMapFromArray [
private _resolvedOrgID = _orgID;
if (_resolvedOrgID isEqualTo "") then {
- private _requesterActor = EGVAR(actor,Registry) getOrDefault [_requesterUid, createHashMap];
- _resolvedOrgID = _requesterActor getOrDefault ["organization", "default"];
+ _resolvedOrgID = EGVAR(actor,ActorStore) call ["getOrganization", [_requesterUid]];
};
if (_resolvedOrgID isEqualTo "") then { _resolvedOrgID = "default"; };
@@ -691,7 +712,7 @@ GVAR(OrgBaseStore) = compileFinal createHashMapFromArray [
_result set ["message", _envelope getOrDefault ["message", ""]];
_result set ["patch", _envelope getOrDefault ["patch", createHashMap]];
_result set ["memberUids", _envelope getOrDefault ["memberUids", []]];
- _result
+ _self call ["persistMutationResult", [_resolvedOrgID, _result, "Organization fleet update"]]
}],
["loadById", compileFinal {
params [["_orgID", "", [""]]];
@@ -715,10 +736,9 @@ GVAR(OrgBaseStore) = compileFinal createHashMapFromArray [
_result
};
- private _actor = EGVAR(actor,Registry) getOrDefault [_uid, createHashMap];
- private _existingOrgID = _actor getOrDefault ["organization", ""];
-
- private _orgID = _actor getOrDefault ["phone_number", ""];
+ private _actor = EGVAR(actor,ActorStore) call ["load", [_uid]];
+ private _existingOrgID = EGVAR(actor,ActorStore) call ["getOrganization", [_uid, ""]];
+ private _orgID = EGVAR(actor,ActorStore) call ["getPhoneNumber", [_uid]];
if (_orgID isEqualTo "") exitWith {
_result set ["message", "Player phone number was not available for organization registration."];
_result
@@ -754,12 +774,8 @@ GVAR(OrgBaseStore) = compileFinal createHashMapFromArray [
params [["_uid", "", [""]]];
private _player = [_uid] call EFUNC(common,getPlayer);
- private _actor = EGVAR(actor,Registry) get _uid;
- private _orgID = _actor get "organization";
- if (_orgID isEqualTo "") then {
- _orgID = "default";
- };
-
+ private _actor = EGVAR(actor,ActorStore) call ["load", [_uid]];
+ private _orgID = EGVAR(actor,ActorStore) call ["getOrganization", [_uid]];
private _finalOrg = _self call ["loadById", [_orgID]];
if (_finalOrg isEqualTo createHashMap) then {
["WARNING", format ["No existing org found for %1, using default org.", _uid]] call EFUNC(common,log);
diff --git a/arma/server/addons/org/functions/fnc_initPayloadBuilder.sqf b/arma/server/addons/org/functions/fnc_initPayloadBuilder.sqf
index 19fe960..3e91122 100644
--- a/arma/server/addons/org/functions/fnc_initPayloadBuilder.sqf
+++ b/arma/server/addons/org/functions/fnc_initPayloadBuilder.sqf
@@ -160,10 +160,8 @@ GVAR(OrgPayloadBuilder) = createHashMapObject [[
private _player = [_uid] call EFUNC(common,getPlayer);
if (isNull _player) exitWith { createHashMap };
- private _actor = EGVAR(actor,Registry) getOrDefault [_uid, createHashMap];
- private _orgID = _actor getOrDefault ["organization", "default"];
- if (_orgID isEqualTo "") then { _orgID = "default"; };
-
+ private _actor = EGVAR(actor,ActorStore) call ["load", [_uid]];
+ private _orgID = EGVAR(actor,ActorStore) call ["getOrganization", [_uid]];
private _org = _self call ["resolveOrgForUid", [_uid]];
if (_org isEqualTo createHashMap) exitWith { createHashMap };
diff --git a/arma/server/addons/store/functions/fnc_initStoreStore.sqf b/arma/server/addons/store/functions/fnc_initStoreStore.sqf
index 61fb751..b77f94f 100644
--- a/arma/server/addons/store/functions/fnc_initStoreStore.sqf
+++ b/arma/server/addons/store/functions/fnc_initStoreStore.sqf
@@ -4,7 +4,7 @@
* File: fnc_initStoreStore.sqf
* Author: IDSolutions
* Date: 2026-03-12
- * Last Update: 2026-03-14
+ * Last Update: 2026-04-04
* Public: No
*
* Description:
@@ -50,10 +50,7 @@ GVAR(StoreBaseStore) = compileFinal createHashMapFromArray [
_bankBalance = _bankAccount getOrDefault ["bank", 0];
};
- private _actor = EGVAR(actor,Registry) getOrDefault [_uid, createHashMap];
- private _orgId = _actor getOrDefault ["organization", "default"];
- if (_orgId isEqualTo "") then { _orgId = "default"; };
-
+ private _orgId = EGVAR(actor,ActorStore) call ["getOrganization", [_uid]];
private _org = EGVAR(org,OrgStore) call ["loadById", [_orgId]];
if (_org isEqualTo createHashMap) then {
_org = EGVAR(org,OrgStore) call ["loadById", ["default"]];
@@ -160,7 +157,10 @@ GVAR(StoreBaseStore) = compileFinal createHashMapFromArray [
["vehicleGranted", []],
["bankPatch", createHashMap],
["orgPatch", createHashMap],
- ["orgTargetUids", []]
+ ["orgTargetUids", []],
+ ["persistenceSucceeded", false],
+ ["persistenceFailures", []],
+ ["persistenceMessage", ""]
]
}],
["formatCurrency", compileFinal {
@@ -255,6 +255,69 @@ GVAR(StoreBaseStore) = compileFinal createHashMapFromArray [
true
}],
+ ["persistCheckoutState", compileFinal {
+ params [
+ ["_uid", "", [""]],
+ ["_orgID", "", [""]],
+ ["_backendResult", createHashMap, [createHashMap]]
+ ];
+
+ private _result = createHashMapFromArray [
+ ["success", true],
+ ["failures", []],
+ ["message", ""]
+ ];
+
+ if (_uid isEqualTo "" || { _backendResult isEqualTo createHashMap }) exitWith {
+ _result set ["success", false];
+ _result set ["failures", ["checkout"]];
+ _result set ["message", "Checkout persistence context was invalid."];
+ _result
+ };
+
+ private _persistenceFailures = [];
+
+ if ((keys (_backendResult getOrDefault ["lockerPatch", createHashMap])) isNotEqualTo []) then {
+ if ((EGVAR(locker,LockerStore) call ["save", [_uid]]) isEqualTo createHashMap) then {
+ _persistenceFailures pushBack "locker";
+ };
+ };
+
+ if ((keys (_backendResult getOrDefault ["vaPatch", createHashMap])) isNotEqualTo []) then {
+ if ((EGVAR(locker,VAStore) call ["save", [_uid]]) isEqualTo createHashMap) then {
+ _persistenceFailures pushBack "virtual_arsenal";
+ };
+ };
+
+ if ((keys (_backendResult getOrDefault ["vgaragePatch", createHashMap])) isNotEqualTo []) then {
+ if ((EGVAR(garage,VGarageStore) call ["save", [_uid]]) isEqualTo createHashMap) then {
+ _persistenceFailures pushBack "virtual_garage";
+ };
+ };
+
+ if ((keys (_backendResult getOrDefault ["bankPatch", createHashMap])) isNotEqualTo []) then {
+ if ((EGVAR(bank,BankStore) call ["save", [_uid]]) isEqualTo createHashMap) then {
+ _persistenceFailures pushBack "bank";
+ };
+ };
+
+ if (_orgID isNotEqualTo "" && { (keys (_backendResult getOrDefault ["orgPatch", createHashMap])) isNotEqualTo [] }) then {
+ if ((EGVAR(org,OrgStore) call ["saveById", [_orgID]]) isEqualTo createHashMap) then {
+ _persistenceFailures pushBack "organization";
+ };
+ };
+
+ if (_persistenceFailures isNotEqualTo []) then {
+ _result set ["success", false];
+ _result set ["failures", _persistenceFailures];
+ _result set ["message", format [
+ "Checkout completed, but durable save failed for: %1.",
+ _persistenceFailures joinString ", "
+ ]];
+ };
+
+ _result
+ }],
["checkout", compileFinal {
params [["_uid", "", [""]], ["_player", objNull, [objNull]], ["_payloadJson", "", [""]]];
@@ -310,6 +373,14 @@ GVAR(StoreBaseStore) = compileFinal createHashMapFromArray [
};
_self call ["syncCheckoutResult", [_player, _backendResult]];
+ private _persistenceResult = _self call [
+ "persistCheckoutState",
+ [
+ _uid,
+ _checkoutContext getOrDefault ["orgId", ""],
+ _backendResult
+ ]
+ ];
_result set ["success", true];
_result set ["message", _backendResult getOrDefault ["message", format [
@@ -320,6 +391,16 @@ GVAR(StoreBaseStore) = compileFinal createHashMapFromArray [
]]];
_result set ["lockerGranted", _backendResult getOrDefault ["lockerGranted", []]];
_result set ["vehicleGranted", _backendResult getOrDefault ["vehicleGranted", []]];
+ _result set ["persistenceSucceeded", _persistenceResult getOrDefault ["success", false]];
+ _result set ["persistenceFailures", _persistenceResult getOrDefault ["failures", []]];
+ _result set ["persistenceMessage", _persistenceResult getOrDefault ["message", ""]];
+
+ if !(_persistenceResult getOrDefault ["success", false]) then {
+ private _warning = _persistenceResult getOrDefault ["message", "Checkout completed with persistence failures."];
+ ["ERROR", format ["Store checkout for %1 completed with persistence failures: %2", _uid, (_persistenceResult getOrDefault ["failures", []]) joinString ", "]] call EFUNC(common,log);
+ _result set ["message", format ["%1 %2", _result get "message", _warning]];
+ };
+
_result
}]
];
diff --git a/arma/server/addons/task/README.md b/arma/server/addons/task/README.md
index 3ea76e3..ef67658 100644
--- a/arma/server/addons/task/README.md
+++ b/arma/server/addons/task/README.md
@@ -3,6 +3,10 @@
## Overview
The task addon is a server-owned mission/task system for Forge. It manages task execution, task-owned state, participant tracking, contribution-based player earnings, and org-owned rewards.
+Task operational state is mission-scoped. The extension-backed task catalog,
+ownership, status, and defuse state are reset on task store startup, so the
+system intentionally starts clean after each server or mission restart.
+
## Responsibilities
- spawn and monitor task flows on the server
- track per-task entities through `TaskStore`
@@ -95,6 +99,7 @@ If you want the accepting player's org to own the task rewards, use `fnc_handler
- the dynamic mission manager in `fnc_missionManager.sqf` is now limited to attack missions only
- it starts server-owned tasks through `fnc_handler.sqf` and binds them to the `default` org
- task lifecycle for the mission manager is tracked through `TaskStore` status entries
+- task backend state is intentionally transient and resets with the active server/mission lifecycle
- task rewards are org-owned, not player-owned
- participant notifications are sent through the notifications module, not through local server UI
diff --git a/arma/server/addons/task/functions/fnc_handleTaskRewards.sqf b/arma/server/addons/task/functions/fnc_handleTaskRewards.sqf
index e0e83bf..1ba8f83 100644
--- a/arma/server/addons/task/functions/fnc_handleTaskRewards.sqf
+++ b/arma/server/addons/task/functions/fnc_handleTaskRewards.sqf
@@ -51,6 +51,7 @@ if (_orgID isEqualTo "") exitWith {
private _success = true;
private _funds = _rewards getOrDefault ["funds", 0];
private _rewardMessages = [];
+private _failureMessages = [];
private _resolveRewardLabel = {
params [["_className", "", [""]]];
@@ -115,8 +116,15 @@ if (_funds > 0) then {
if (_updatedOrg isEqualTo createHashMap) then {
["ERROR", format ["Failed to update organization %1 funds for task %2.", _orgID, _taskID]] call EFUNC(common,log);
_success = false;
+ _failureMessages pushBack "org funds update";
} else {
private _patch = createHashMapFromArray [["funds", _nextFunds]];
+ private _savedOrg = EGVAR(org,OrgStore) call ["saveById", [_orgID]];
+ if (_savedOrg isEqualTo createHashMap) then {
+ ["ERROR", format ["Task %1 updated organization %2 funds, but durable save failed.", _taskID, _orgID]] call EFUNC(common,log);
+ _success = false;
+ _failureMessages pushBack "org funds persistence";
+ };
[_patch] call _syncOrgPatch;
_rewardMessages pushBack format ["$%1 org funds", [_funds] call EFUNC(common,formatNumber)];
@@ -141,8 +149,15 @@ private _grantOrgAssets = {
if !(_grantResult getOrDefault ["success", false]) then {
["ERROR", format ["Failed to award %1 assets for task %2: %3", _category, _taskID, _grantResult getOrDefault ["message", "Unknown error."]]] call EFUNC(common,log);
_success = false;
+ _failureMessages pushBack format ["%1 asset update", _category];
} else {
[_grantResult getOrDefault ["patch", createHashMap]] call _syncOrgPatch;
+ if !(_grantResult getOrDefault ["persisted", false]) then {
+ private _persistenceMessage = _grantResult getOrDefault ["persistenceMessage", format ["%1 assets updated, but durable save failed.", _category]];
+ ["ERROR", format ["Task %1 %2", _taskID, _persistenceMessage]] call EFUNC(common,log);
+ _success = false;
+ _failureMessages pushBack format ["%1 asset persistence", _category];
+ };
private _labels = _items apply { [_x] call _resolveRewardLabel };
_rewardMessages pushBack format ["%1: %2", _category, _labels joinString ", "];
};
@@ -171,8 +186,15 @@ private _grantOrgFleet = {
if !(_fleetResult getOrDefault ["success", false]) then {
["ERROR", format ["Failed to award vehicle rewards for task %2: %1", _fleetResult getOrDefault ["message", "Unknown error."], _taskID]] call EFUNC(common,log);
_success = false;
+ _failureMessages pushBack "fleet update";
} else {
[_fleetResult getOrDefault ["patch", createHashMap]] call _syncOrgPatch;
+ if !(_fleetResult getOrDefault ["persisted", false]) then {
+ private _persistenceMessage = _fleetResult getOrDefault ["persistenceMessage", "Fleet updated, but durable save failed."];
+ ["ERROR", format ["Task %1 %2", _taskID, _persistenceMessage]] call EFUNC(common,log);
+ _success = false;
+ _failureMessages pushBack "fleet persistence";
+ };
private _labels = _vehicles apply { [_x] call _resolveRewardLabel };
_rewardMessages pushBack format ["vehicles: %1", _labels joinString ", "];
};
@@ -219,7 +241,12 @@ if (_success) then {
["INFO", _message] call EFUNC(common,log);
["success", "Tasks", _message] call _notifyMembers;
} else {
- ["warning", "Tasks", format ["Task %1 completed, but one or more org rewards failed to apply.", _taskID]] call _notifyMembers;
+ private _warningMessage = format ["Task %1 completed, but one or more org rewards failed to apply.", _taskID];
+ if (_failureMessages isNotEqualTo []) then {
+ _warningMessage = format ["%1 Failed areas: %2.", _warningMessage, _failureMessages joinString ", "];
+ };
+
+ ["warning", "Tasks", _warningMessage] call _notifyMembers;
};
_success
diff --git a/arma/server/addons/task/functions/fnc_handler.sqf b/arma/server/addons/task/functions/fnc_handler.sqf
index 0349b27..cffe3a1 100644
--- a/arma/server/addons/task/functions/fnc_handler.sqf
+++ b/arma/server/addons/task/functions/fnc_handler.sqf
@@ -27,14 +27,7 @@ if (_minRating > 0) then {
if (_requesterUid isEqualTo "") then {
["WARNING", format ["Task %1 requires minimum reputation %2 but no requester UID was provided, skipping reputation gate.", _taskType, _minRating]] call EFUNC(common,log);
} else {
- private _requesterActor = EGVAR(actor,Registry) getOrDefault [_requesterUid, createHashMap];
- if (_requesterActor isEqualTo createHashMap) then {
- _requesterActor = EGVAR(actor,ActorStore) call ["init", [_requesterUid]];
- };
-
- private _orgID = _requesterActor getOrDefault ["organization", "default"];
- if (_orgID isEqualTo "") then { _orgID = "default"; };
-
+ private _orgID = EGVAR(actor,ActorStore) call ["getOrganization", [_requesterUid]];
private _org = EGVAR(org,OrgStore) call ["loadById", [_orgID]];
private _orgReputation = _org getOrDefault ["reputation", 0];
if (_orgReputation < _minRating) exitWith {
diff --git a/arma/server/addons/task/functions/fnc_initTaskStore.sqf b/arma/server/addons/task/functions/fnc_initTaskStore.sqf
index f0410f9..5de0c4e 100644
--- a/arma/server/addons/task/functions/fnc_initTaskStore.sqf
+++ b/arma/server/addons/task/functions/fnc_initTaskStore.sqf
@@ -5,6 +5,10 @@
* Initializes the task store for task entity tracking, participant
* contribution tracking, and task outcome application.
*
+ * Task metadata is extension-backed but intentionally transient. The
+ * task backend is reset when this store is created so task/catalog/status
+ * state starts clean for each server or mission lifecycle.
+ *
* Arguments:
* None
*
@@ -32,6 +36,8 @@ GVAR(TaskStore) = createHashMapObject [[
["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"];
if (
!_isSuccess
@@ -99,18 +105,14 @@ GVAR(TaskStore) = createHashMapObject [[
private _orgID = "default";
if (_requesterUid isNotEqualTo "") then {
- private _actor = EGVAR(actor,Registry) getOrDefault [_requesterUid, createHashMap];
- if (_actor isEqualTo createHashMap) then {
- _actor = EGVAR(actor,ActorStore) call ["init", [_requesterUid]];
- };
+ private _actor = EGVAR(actor,ActorStore) call ["load", [_requesterUid]];
if (_actor isEqualTo createHashMap) exitWith {
_result set ["message", format ["Failed to load actor for %1.", _requesterUid]];
_result
};
- _orgID = _actor getOrDefault ["organization", ""];
- if (_orgID isEqualTo "") then { _orgID = "default"; };
+ _orgID = EGVAR(actor,ActorStore) call ["getOrganization", [_requesterUid]];
};
private _context = createHashMapFromArray [
@@ -166,6 +168,14 @@ GVAR(TaskStore) = createHashMapObject [[
_entries
}],
+ ["hasTaskCatalogEntry", compileFinal {
+ params [["_taskID", "", [""]]];
+
+ if (_taskID isEqualTo "") exitWith { false };
+
+ private _entry = _self call ["callTaskState", ["task:catalog:get", [_taskID], objNull]];
+ _entry isEqualType createHashMap
+ }],
["acceptTask", compileFinal {
params [["_taskID", "", [""]], ["_requesterUid", "", [""]]];
@@ -180,17 +190,13 @@ GVAR(TaskStore) = createHashMapObject [[
_result
};
- private _actor = EGVAR(actor,Registry) getOrDefault [_requesterUid, createHashMap];
- if (_actor isEqualTo createHashMap) then {
- _actor = EGVAR(actor,ActorStore) call ["init", [_requesterUid]];
- };
+ private _actor = EGVAR(actor,ActorStore) call ["load", [_requesterUid]];
if (_actor isEqualTo createHashMap) exitWith {
_result set ["message", format ["Failed to load actor for %1.", _requesterUid]];
_result
};
- private _orgID = _actor getOrDefault ["organization", ""];
- if (_orgID isEqualTo "") then { _orgID = "default"; };
+ private _orgID = EGVAR(actor,ActorStore) call ["getOrganization", [_requesterUid]];
private _context = createHashMapFromArray [
["requesterUid", _requesterUid],
@@ -418,6 +424,10 @@ GVAR(TaskStore) = createHashMapObject [[
if (_taskID isEqualTo "") exitWith { false };
+ if !(isNil QGVAR(MissionManager)) then {
+ GVAR(MissionManager) call ["completeMission", [_taskID]];
+ };
+
private _participantRegistry = _self getOrDefault ["participantRegistry", createHashMap];
_participantRegistry deleteAt _taskID;
_self set ["participantRegistry", _participantRegistry];
@@ -431,7 +441,11 @@ GVAR(TaskStore) = createHashMapObject [[
private _result = createHashMapFromArray [
["participantUids", []],
["orgIds", []],
- ["contributions", createHashMap]
+ ["contributions", createHashMap],
+ ["success", true],
+ ["mutationFailures", []],
+ ["persistenceFailures", []],
+ ["message", ""]
];
if (_taskID isEqualTo "" || { _delta isEqualTo 0 }) exitWith { _result };
@@ -462,6 +476,8 @@ GVAR(TaskStore) = createHashMapObject [[
private _orgIds = [];
private _contributions = createHashMap;
private _totalContribution = 0;
+ private _mutationFailures = [];
+ private _persistenceFailures = [];
if (_delta > 0) then {
{
@@ -481,12 +497,7 @@ GVAR(TaskStore) = createHashMapObject [[
{
private _uid = _x;
- private _actor = EGVAR(actor,Registry) getOrDefault [_uid, createHashMap];
- if (_actor isEqualTo createHashMap) then {
- _actor = EGVAR(actor,ActorStore) call ["init", [_uid]];
- };
-
- private _orgID = _actor getOrDefault ["organization", ""];
+ private _orgID = EGVAR(actor,ActorStore) call ["getOrganization", [_uid, ""]];
if (_orgID isNotEqualTo "") then {
_orgIds pushBackUnique _orgID;
};
@@ -517,6 +528,10 @@ GVAR(TaskStore) = createHashMapObject [[
if (_patch isEqualTo createHashMap) then { continue; };
EGVAR(bank,BankMessenger) call ["sendAccountSync", [_uid, _patch]];
+ if ((EGVAR(bank,BankStore) call ["save", [_uid]]) isEqualTo createHashMap) then {
+ _persistenceFailures pushBackUnique format ["bank:%1", _uid];
+ ["ERROR", format ["Task %1 updated bank earnings for %2, but durable save failed.", _taskID, _uid]] call EFUNC(common,log);
+ };
};
};
} forEach _participantUids;
@@ -547,8 +562,13 @@ GVAR(TaskStore) = createHashMapObject [[
} forEach _memberUids;
_orgIds = [_ownerOrgID];
+ if ((EGVAR(org,OrgStore) call ["saveById", [_ownerOrgID]]) isEqualTo createHashMap) then {
+ _persistenceFailures pushBackUnique format ["organization:%1", _ownerOrgID];
+ ["ERROR", format ["Task %1 updated reputation for organization %2, but durable save failed.", _taskID, _ownerOrgID]] call EFUNC(common,log);
+ };
} else {
["ERROR", format ["Failed to update organization %1 reputation for task %2.", _ownerOrgID, _taskID]] call EFUNC(common,log);
+ _mutationFailures pushBackUnique format ["organization:%1", _ownerOrgID];
};
};
};
@@ -556,6 +576,19 @@ GVAR(TaskStore) = createHashMapObject [[
_result set ["participantUids", _participantUids];
_result set ["orgIds", _orgIds];
_result set ["contributions", _contributions];
+ _result set ["success", (_mutationFailures isEqualTo []) && { _persistenceFailures isEqualTo [] }];
+ _result set ["mutationFailures", _mutationFailures];
+ _result set ["persistenceFailures", _persistenceFailures];
+ if (_mutationFailures isNotEqualTo [] || { _persistenceFailures isNotEqualTo [] }) then {
+ private _messageParts = [];
+ if (_mutationFailures isNotEqualTo []) then {
+ _messageParts pushBack format ["mutation failures: %1", _mutationFailures joinString ", "];
+ };
+ if (_persistenceFailures isNotEqualTo []) then {
+ _messageParts pushBack format ["persistence failures: %1", _persistenceFailures joinString ", "];
+ };
+ _result set ["message", _messageParts joinString "; "];
+ };
_result
}]
]];
diff --git a/arma/server/addons/task/functions/fnc_missionManager.sqf b/arma/server/addons/task/functions/fnc_missionManager.sqf
index a1aec02..e471b27 100644
--- a/arma/server/addons/task/functions/fnc_missionManager.sqf
+++ b/arma/server/addons/task/functions/fnc_missionManager.sqf
@@ -38,8 +38,25 @@ GVAR(MissionManagerBaseClass) = compileFinal createHashMapFromArray [
["getMaxConcurrentMissions", compileFinal {
private _maxConcurrent = _self getOrDefault ["maxConcurrentMissions", 1];
if (_maxConcurrent <= 0) then { _maxConcurrent = 1; };
+ private _attackLocationCount = _self call ["getAttackLocationCount", []];
+ if (_attackLocationCount > 0) then {
+ _maxConcurrent = _maxConcurrent min _attackLocationCount;
+ };
_maxConcurrent
}],
+ ["getAttackLocationCount", compileFinal {
+ private _locationsConfig = _self getOrDefault ["locationsConfig", configNull];
+ if (isNull _locationsConfig) exitWith { 0 };
+
+ private _count = 0;
+ {
+ if ("attack" in getArray (_x >> "suitable")) then {
+ _count = _count + 1;
+ };
+ } forEach ("true" configClasses _locationsConfig);
+
+ _count
+ }],
["getLocationReuseCooldown", compileFinal {
private _missionConfig = _self getOrDefault ["missionConfig", configNull];
private _cooldown = getNumber (_missionConfig >> "locationReuseCooldown");
@@ -352,7 +369,8 @@ GVAR(MissionManager) = createHashMapObject [GVAR(MissionManagerBaseClass)];
[{
{
private _status = GVAR(TaskStore) call ["getTaskStatus", [_x]];
- if (_status in ["succeeded", "failed"]) then {
+ private _hasCatalogEntry = GVAR(TaskStore) call ["hasTaskCatalogEntry", [_x]];
+ if (_status in ["succeeded", "failed"] || { _status isEqualTo "" && { !_hasCatalogEntry } }) then {
GVAR(MissionManager) call ["completeMission", [_x]];
GVAR(TaskStore) call ["clearTaskStatus", [_x]];
};
diff --git a/arma/server/extension/src/actor.rs b/arma/server/extension/src/actor.rs
index 53f3a68..7bbdf68 100644
--- a/arma/server/extension/src/actor.rs
+++ b/arma/server/extension/src/actor.rs
@@ -54,6 +54,7 @@ pub fn group() -> Group {
Group::new()
.command("init", init_hot_actor)
.command("get", get_hot_actor)
+ .command("keys", list_hot_actor_keys)
.command("override", override_hot_actor)
.command("save", save_hot_actor)
.command("remove", remove_hot_actor),
@@ -91,6 +92,16 @@ pub(crate) fn get_hot_actor(call_context: CallContext, key: String) -> String {
}
}
+pub(crate) fn list_hot_actor_keys() -> String {
+ match HOT_ACTOR_SERVICE.list_actor_keys() {
+ Ok(keys) => match serde_json::to_string(&keys) {
+ Ok(json) => json,
+ Err(error) => format!("Error: Failed to serialize actor hot-state keys: {}", error),
+ },
+ Err(error) => format!("Error: {}", error),
+ }
+}
+
pub(crate) fn override_hot_actor(
call_context: CallContext,
key: String,
diff --git a/arma/server/extension/src/cad.rs b/arma/server/extension/src/cad.rs
index 5984e99..d6c4223 100644
--- a/arma/server/extension/src/cad.rs
+++ b/arma/server/extension/src/cad.rs
@@ -3,6 +3,10 @@
//! The extension owns the in-memory CAD state store, while the shared service
//! layer handles mutation rules and hydrate shaping. This keeps the extension
//! surface thin and aligned with the workspace architecture.
+//!
+//! CAD state is intentionally transient operational state. It follows the
+//! active server or mission lifecycle and is not treated as durable player or
+//! organization persistence.
use arma_rs::Group;
use forge_repositories::InMemoryCadRepository;
diff --git a/arma/server/extension/src/task.rs b/arma/server/extension/src/task.rs
index 233d3dc..fbe2e40 100644
--- a/arma/server/extension/src/task.rs
+++ b/arma/server/extension/src/task.rs
@@ -2,6 +2,9 @@
//!
//! The extension owns portable task metadata while SQF keeps Arma-only runtime
//! state such as entity references and participant tracking.
+//!
+//! This state is intentionally transient and is reset during server task-store
+//! initialization so tasks start clean for each server or mission lifecycle.
use arma_rs::Group;
use forge_repositories::InMemoryTaskRepository;
diff --git a/arma/server/extension/src/transport.rs b/arma/server/extension/src/transport.rs
index 0232ba4..6e3b32c 100644
--- a/arma/server/extension/src/transport.rs
+++ b/arma/server/extension/src/transport.rs
@@ -185,6 +185,10 @@ fn route_command(
expect_arg_count(function_name, &arguments, 1)?;
Ok(actor::get_hot_actor(call_context, arguments[0].clone()))
}
+ "actor:hot:keys" => {
+ expect_arg_count(function_name, &arguments, 0)?;
+ Ok(actor::list_hot_actor_keys())
+ }
"actor:hot:override" => {
expect_arg_count(function_name, &arguments, 2)?;
Ok(actor::override_hot_actor(
diff --git a/lib/models/src/actor.rs b/lib/models/src/actor.rs
index b6e7c2b..203eed1 100644
--- a/lib/models/src/actor.rs
+++ b/lib/models/src/actor.rs
@@ -61,7 +61,7 @@ impl Actor {
state: "HEALTHY".to_string(),
holster: true,
rank: None,
- organization: "".to_string(),
+ organization: "default".to_string(),
};
actor.validate()?;
@@ -171,7 +171,7 @@ impl FromArma for Actor {
})?;
if actor.organization.trim().is_empty() {
- actor.organization = String::new();
+ actor.organization = "default".to_string();
}
Ok(actor)
diff --git a/lib/models/src/v_locker.rs b/lib/models/src/v_locker.rs
index 2f68ffc..f67de35 100644
--- a/lib/models/src/v_locker.rs
+++ b/lib/models/src/v_locker.rs
@@ -35,7 +35,7 @@ impl VLocker {
"ItemMap".to_string(),
"ItemRadio".to_string(),
"ItemWatch".to_string(),
- "U_IG_Guerrilla_6_1".to_string(),
+ "U_BG_Guerrilla_6_1".to_string(),
"V_TacVest_oli".to_string(),
],
weapons: vec!["arifle_MX_F".to_string(), "hgun_P07_F".to_string()],
diff --git a/lib/repositories/src/actor.rs b/lib/repositories/src/actor.rs
index cca7867..72107e1 100644
--- a/lib/repositories/src/actor.rs
+++ b/lib/repositories/src/actor.rs
@@ -34,6 +34,7 @@ pub trait ActorRepository: Send + Sync {
pub trait ActorHotRepository: Send + Sync {
fn get(&self, id: &str) -> Result