Add task lifecycle event bus integration
- Add a common in-process event bus - Emit task lifecycle events from task store and instances - Register CAD listeners to invalidate task state
This commit is contained in:
parent
e6eceac4ec
commit
732433f848
@ -5,3 +5,4 @@ PREP(initGroupRepository);
|
||||
PREP(initPermissionService);
|
||||
PREP(initPersistenceService);
|
||||
PREP(initRequestRepository);
|
||||
PREP(registerTaskEventListeners);
|
||||
|
||||
@ -5,6 +5,7 @@ PREP_RECOMPILE_START;
|
||||
PREP_RECOMPILE_END;
|
||||
|
||||
call FUNC(initCadStore);
|
||||
call FUNC(registerTaskEventListeners);
|
||||
|
||||
[QGVAR(requestHydrateCad), {
|
||||
params [["_uid", "", [""]]];
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* File: fnc_registerTaskEventListeners.sqf
|
||||
* Author: IDSolutions
|
||||
* Date: 2026-05-14
|
||||
* Public: No
|
||||
*
|
||||
* Description:
|
||||
* Registers CAD listeners for framework task lifecycle events.
|
||||
*
|
||||
* Arguments:
|
||||
* None
|
||||
*
|
||||
* Return Value:
|
||||
* Listener tokens [ARRAY]
|
||||
*
|
||||
* Example:
|
||||
* call forge_server_cad_fnc_registerTaskEventListeners
|
||||
*/
|
||||
|
||||
if (isNil QEGVAR(common,EventBus)) then { call EFUNC(common,eventBus); };
|
||||
if !(isNil QGVAR(TaskEventListenerTokens)) exitWith { GVAR(TaskEventListenerTokens) };
|
||||
|
||||
private _invalidateCadState = {
|
||||
params ["_event"];
|
||||
|
||||
["INFO", format [
|
||||
"CAD task event received: %1 taskID=%2 taskType=%3 status=%4",
|
||||
_event getOrDefault ["event", ""],
|
||||
_event getOrDefault ["taskID", ""],
|
||||
_event getOrDefault ["taskType", ""],
|
||||
_event getOrDefault ["status", ""]
|
||||
]] call EFUNC(common,log);
|
||||
|
||||
[CRPC(cad,invalidateCadState), []] call CFUNC(globalEvent);
|
||||
};
|
||||
|
||||
GVAR(TaskEventListenerTokens) = [
|
||||
EGVAR(common,EventBus) call ["on", ["task.created", _invalidateCadState, "cad.task.invalidate"]],
|
||||
EGVAR(common,EventBus) call ["on", ["task.started", _invalidateCadState, "cad.task.invalidate"]],
|
||||
EGVAR(common,EventBus) call ["on", ["task.completed", _invalidateCadState, "cad.task.invalidate"]],
|
||||
EGVAR(common,EventBus) call ["on", ["task.failed", _invalidateCadState, "cad.task.invalidate"]],
|
||||
EGVAR(common,EventBus) call ["on", ["task.cleared", _invalidateCadState, "cad.task.invalidate"]]
|
||||
];
|
||||
|
||||
GVAR(TaskEventListenerTokens)
|
||||
@ -11,6 +11,8 @@ the specific domain addons or the Rust extension.
|
||||
## Main Components
|
||||
- `fnc_baseStore.sqf` provides shared hash-map object behavior such as JSON
|
||||
conversion.
|
||||
- `fnc_eventBus.sqf` provides a framework-wide in-process event bus for
|
||||
cross-addon notifications.
|
||||
- `fnc_log.sqf` standardizes server log messages.
|
||||
- `fnc_getPlayer.sqf` resolves online players by UID.
|
||||
- `fnc_formatNumber.sqf` formats numeric values for notifications and UI text.
|
||||
@ -21,3 +23,27 @@ the specific domain addons or the Rust extension.
|
||||
## Notes
|
||||
Keep this addon free of domain-specific behavior. If a helper needs actor,
|
||||
bank, org, task, store, or CAD state, it belongs in that addon instead.
|
||||
|
||||
## Event Bus
|
||||
The event bus is initialized as `forge_server_common_EventBus` during store
|
||||
bootstrap. It is synchronous and in-process: listeners run immediately when an
|
||||
event is emitted.
|
||||
|
||||
```sqf
|
||||
private _token = EGVAR(common,EventBus) call ["on", [
|
||||
"task.completed",
|
||||
{
|
||||
params ["_event"];
|
||||
["INFO", format ["Task completed: %1", _event getOrDefault ["taskID", ""]]] call EFUNC(common,log);
|
||||
},
|
||||
"example"
|
||||
]];
|
||||
|
||||
EGVAR(common,EventBus) call ["emit", [
|
||||
"task.completed",
|
||||
createHashMapFromArray [["taskID", "task_001"]],
|
||||
createHashMapFromArray [["source", "task"]]
|
||||
]];
|
||||
|
||||
EGVAR(common,EventBus) call ["off", [_token]];
|
||||
```
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
PREP(baseStore);
|
||||
PREP(eventBus);
|
||||
PREP(formatNumber);
|
||||
PREP(getPlayer);
|
||||
PREP(generateHash);
|
||||
|
||||
167
arma/server/addons/common/functions/fnc_eventBus.sqf
Normal file
167
arma/server/addons/common/functions/fnc_eventBus.sqf
Normal file
@ -0,0 +1,167 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* File: fnc_eventBus.sqf
|
||||
* Author: IDSolutions
|
||||
* Date: 2026-05-14
|
||||
* Public: No
|
||||
*
|
||||
* Description:
|
||||
* Initializes the framework-wide in-process event bus.
|
||||
*
|
||||
* Arguments:
|
||||
* None
|
||||
*
|
||||
* Return Value:
|
||||
* Event bus object [HASHMAP OBJECT]
|
||||
*
|
||||
* Example:
|
||||
* call forge_server_common_fnc_eventBus
|
||||
*/
|
||||
|
||||
#pragma hemtt ignore_variables ["_self"]
|
||||
GVAR(EventBusBase) = compileFinal createHashMapFromArray [
|
||||
["#type", "EventBus"],
|
||||
["#create", compileFinal {
|
||||
_self set ["handlers", createHashMap];
|
||||
_self set ["nextToken", 0];
|
||||
|
||||
["INFO", "Common EventBus Initialized!"] call EFUNC(common,log);
|
||||
}],
|
||||
["on", compileFinal {
|
||||
params [["_eventName", "", [""]], ["_handler", {}, [{}]], ["_owner", "", [""]]];
|
||||
|
||||
if (_eventName isEqualTo "") exitWith { "" };
|
||||
|
||||
private _handlers = _self getOrDefault ["handlers", createHashMap];
|
||||
private _eventHandlers = +(_handlers getOrDefault [_eventName, []]);
|
||||
private _nextToken = (_self getOrDefault ["nextToken", 0]) + 1;
|
||||
private _token = format ["%1:%2", _eventName, _nextToken];
|
||||
|
||||
_eventHandlers pushBack createHashMapFromArray [
|
||||
["token", _token],
|
||||
["owner", _owner],
|
||||
["handler", _handler]
|
||||
];
|
||||
|
||||
_handlers set [_eventName, _eventHandlers];
|
||||
_self set ["handlers", _handlers];
|
||||
_self set ["nextToken", _nextToken];
|
||||
|
||||
_token
|
||||
}],
|
||||
["off", compileFinal {
|
||||
params [["_token", "", [""]]];
|
||||
|
||||
if (_token isEqualTo "") exitWith { false };
|
||||
|
||||
private _handlers = _self getOrDefault ["handlers", createHashMap];
|
||||
private _removed = false;
|
||||
|
||||
{
|
||||
private _eventHandlers = +(_handlers getOrDefault [_x, []]);
|
||||
private _remainingHandlers = _eventHandlers select {
|
||||
(_x getOrDefault ["token", ""]) isNotEqualTo _token
|
||||
};
|
||||
|
||||
if ((count _remainingHandlers) isNotEqualTo (count _eventHandlers)) then {
|
||||
_removed = true;
|
||||
if (_remainingHandlers isEqualTo []) then {
|
||||
_handlers deleteAt _x;
|
||||
} else {
|
||||
_handlers set [_x, _remainingHandlers];
|
||||
};
|
||||
};
|
||||
} forEach (keys _handlers);
|
||||
|
||||
_self set ["handlers", _handlers];
|
||||
_removed
|
||||
}],
|
||||
["emit", compileFinal {
|
||||
params [["_eventName", "", [""]], ["_payload", createHashMap], ["_options", createHashMap]];
|
||||
|
||||
private _result = createHashMapFromArray [
|
||||
["event", _eventName],
|
||||
["listenerCount", 0],
|
||||
["invoked", 0],
|
||||
["failed", 0]
|
||||
];
|
||||
|
||||
if (_eventName isEqualTo "") exitWith { _result };
|
||||
|
||||
if !(_payload isEqualType createHashMap) then {
|
||||
_payload = createHashMapFromArray [["value", _payload]];
|
||||
};
|
||||
if !(_options isEqualType createHashMap) then {
|
||||
_options = createHashMap;
|
||||
};
|
||||
|
||||
private _eventPayload = +_payload;
|
||||
_eventPayload set ["event", _eventName];
|
||||
_eventPayload set ["source", _eventPayload getOrDefault ["source", _options getOrDefault ["source", "unknown"]]];
|
||||
_eventPayload set ["timestamp", _eventPayload getOrDefault ["timestamp", serverTime]];
|
||||
|
||||
private _handlers = _self getOrDefault ["handlers", createHashMap];
|
||||
private _eventHandlers = +(_handlers getOrDefault [_eventName, []]);
|
||||
_result set ["listenerCount", count _eventHandlers];
|
||||
|
||||
{
|
||||
private _handler = _x getOrDefault ["handler", {}];
|
||||
private _token = _x getOrDefault ["token", ""];
|
||||
private _owner = _x getOrDefault ["owner", ""];
|
||||
|
||||
try {
|
||||
[_eventPayload] call _handler;
|
||||
_result set ["invoked", (_result getOrDefault ["invoked", 0]) + 1];
|
||||
} catch {
|
||||
_result set ["failed", (_result getOrDefault ["failed", 0]) + 1];
|
||||
["ERROR", format ["EventBus handler failed. Event=%1 Token=%2 Owner=%3 Error=%4", _eventName, _token, _owner, _exception]] call EFUNC(common,log);
|
||||
};
|
||||
} forEach _eventHandlers;
|
||||
|
||||
_result
|
||||
}],
|
||||
["clear", compileFinal {
|
||||
params [["_eventName", "", [""]]];
|
||||
|
||||
private _handlers = _self getOrDefault ["handlers", createHashMap];
|
||||
|
||||
if (_eventName isEqualTo "") then {
|
||||
_self set ["handlers", createHashMap];
|
||||
} else {
|
||||
_handlers deleteAt _eventName;
|
||||
_self set ["handlers", _handlers];
|
||||
};
|
||||
|
||||
true
|
||||
}],
|
||||
["listenerCount", compileFinal {
|
||||
params [["_eventName", "", [""]]];
|
||||
|
||||
private _handlers = _self getOrDefault ["handlers", createHashMap];
|
||||
|
||||
if (_eventName isEqualTo "") exitWith {
|
||||
private _total = 0;
|
||||
{ _total = _total + (count _y); } forEach _handlers;
|
||||
_total
|
||||
};
|
||||
|
||||
count (_handlers getOrDefault [_eventName, []])
|
||||
}],
|
||||
["listeners", compileFinal {
|
||||
params [["_eventName", "", [""]]];
|
||||
|
||||
private _handlers = _self getOrDefault ["handlers", createHashMap];
|
||||
|
||||
if (_eventName isNotEqualTo "") exitWith { +(_handlers getOrDefault [_eventName, []]) };
|
||||
|
||||
private _counts = createHashMap;
|
||||
{ _counts set [_x, count _y]; } forEach _handlers;
|
||||
|
||||
_counts
|
||||
}]
|
||||
];
|
||||
|
||||
GVAR(EventBus) = createHashMapObject [GVAR(EventBusBase)];
|
||||
|
||||
GVAR(EventBus)
|
||||
@ -18,6 +18,7 @@
|
||||
|
||||
// Base
|
||||
if (isNil QEGVAR(common,BaseStore)) then { call EFUNC(common,baseStore); };
|
||||
if (isNil QEGVAR(common,EventBus)) then { call EFUNC(common,eventBus); };
|
||||
|
||||
// Actor
|
||||
if (isNil QEGVAR(actor,ActorStore)) then { call EFUNC(actor,initActorStore); };
|
||||
|
||||
@ -1,5 +1,29 @@
|
||||
#include "script_component.hpp"
|
||||
|
||||
if (isNil QEGVAR(common,EventBus)) then { call EFUNC(common,eventBus); };
|
||||
if (isNil QGVAR(TaskLifecycleEventLogTokens)) then {
|
||||
private _logTaskLifecycleEvent = {
|
||||
params ["_event"];
|
||||
|
||||
["INFO", format [
|
||||
"Task lifecycle event: %1 taskID=%2 taskType=%3 status=%4 participants=%5",
|
||||
_event getOrDefault ["event", ""],
|
||||
_event getOrDefault ["taskID", ""],
|
||||
_event getOrDefault ["taskType", ""],
|
||||
_event getOrDefault ["status", ""],
|
||||
_event getOrDefault ["participants", []]
|
||||
]] call EFUNC(common,log);
|
||||
};
|
||||
|
||||
GVAR(TaskLifecycleEventLogTokens) = [
|
||||
EGVAR(common,EventBus) call ["on", ["task.created", _logTaskLifecycleEvent, "task.lifecycle.log"]],
|
||||
EGVAR(common,EventBus) call ["on", ["task.started", _logTaskLifecycleEvent, "task.lifecycle.log"]],
|
||||
EGVAR(common,EventBus) call ["on", ["task.completed", _logTaskLifecycleEvent, "task.lifecycle.log"]],
|
||||
EGVAR(common,EventBus) call ["on", ["task.failed", _logTaskLifecycleEvent, "task.lifecycle.log"]],
|
||||
EGVAR(common,EventBus) call ["on", ["task.cleared", _logTaskLifecycleEvent, "task.lifecycle.log"]]
|
||||
];
|
||||
};
|
||||
|
||||
["ace_explosives_defuse", {
|
||||
private _taskID = "";
|
||||
private _explosive = objNull;
|
||||
|
||||
@ -8,4 +8,22 @@ private _category = [QUOTE(MOD_NAME), LLSTRING(displayName)];
|
||||
|
||||
#include "initSettings.inc.sqf"
|
||||
|
||||
[] call FUNC(TaskInstanceBaseClass);
|
||||
[] call FUNC(EntityControllerBaseClass);
|
||||
[] call FUNC(AttackTaskBaseClass);
|
||||
[] call FUNC(HostageTaskBaseClass);
|
||||
[] call FUNC(HostageEntityController);
|
||||
[] call FUNC(TargetEntityController);
|
||||
[] call FUNC(ShooterEntityController);
|
||||
[] call FUNC(HVTEntityController);
|
||||
[] call FUNC(CargoEntityController);
|
||||
[] call FUNC(ProtectedEntityController);
|
||||
[] call FUNC(IEDEntityController);
|
||||
[] call FUNC(DefenseEnemyController);
|
||||
[] call FUNC(DefuseTaskBaseClass);
|
||||
[] call FUNC(DestroyTaskBaseClass);
|
||||
[] call FUNC(DeliveryTaskBaseClass);
|
||||
[] call FUNC(HVTTaskBaseClass);
|
||||
[] call FUNC(DefendTaskBaseClass);
|
||||
|
||||
call FUNC(initTaskStore);
|
||||
|
||||
@ -2,152 +2,77 @@
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Registers an attack task
|
||||
* Registers an attack task.
|
||||
*
|
||||
* This public function is now a compatibility adapter around
|
||||
* AttackTaskBaseClass. Keep the argument list stable for Eden modules,
|
||||
* startTask, and external scripts while the object-style task prototypes
|
||||
* become the live implementation.
|
||||
*
|
||||
* Arguments:
|
||||
* 0: ID of the task <STRING>
|
||||
* 1: Amount of targets escaped to fail the task <NUMBER>
|
||||
* 2: Amount of targets eliminated to complete the task <NUMBER>
|
||||
* 3: Amount of funds the company recieves if the task is successful <NUMBER> (default: 0)
|
||||
* 4: Amount of rating the company and player lose if the task is failed <NUMBER> (default: 0)
|
||||
* 5: Amount of rating the company and player recieve if the task is successful <NUMBER> (default: 0)
|
||||
* 6: Should the mission end (MissionSuccess) if the task is successful <BOOL> (default: false)
|
||||
* 7: Should the mission end (MissionFailed) if the task is failed <BOOL> (default: false)
|
||||
* 8: Amount of time before target(s) escape <NUMBER> (default: 0, 0 = no limit)
|
||||
* 9: Equipment rewards <ARRAY> (default: [])
|
||||
* 10: Supply rewards <ARRAY> (default: [])
|
||||
* 11: Weapon rewards <ARRAY> (default: [])
|
||||
* 12: Vehicle rewards <ARRAY> (default: [])
|
||||
* 13: Special rewards <ARRAY> (default: [])
|
||||
* 3: Amount of funds the company receives if the task is successful <NUMBER>
|
||||
* 4: Amount of rating the company and player lose if the task is failed <NUMBER>
|
||||
* 5: Amount of rating the company and player receive if the task is successful <NUMBER>
|
||||
* 6: Should the mission end if the task is successful <BOOL>
|
||||
* 7: Should the mission end if the task is failed <BOOL>
|
||||
* 8: Amount of time before target(s) escape <NUMBER>
|
||||
* 9: Equipment rewards <ARRAY>
|
||||
* 10: Supply rewards <ARRAY>
|
||||
* 11: Weapon rewards <ARRAY>
|
||||
* 12: Vehicle rewards <ARRAY>
|
||||
* 13: Special rewards <ARRAY>
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Example:
|
||||
* ["task_name", 1, 2, 1500000, -75, 375, false, false] spawn forge_server_task_fnc_attack;
|
||||
* ["task_name", 1, 2, 1500000, -75, 375, false, false, 45] spawn forge_server_task_fnc_attack;
|
||||
*
|
||||
* Public: Yes
|
||||
*/
|
||||
|
||||
params [
|
||||
["_taskID", "", [""]],
|
||||
["_limitFail", -1, [0]],
|
||||
["_limitSuccess", -1, [0]],
|
||||
["_companyFunds", 0, [0]],
|
||||
["_ratingFail", 0, [0]],
|
||||
["_ratingSuccess", 0, [0]],
|
||||
["_endSuccess", false, [false]],
|
||||
["_endFail", false, [false]],
|
||||
["_timeLimit", 0, [0]],
|
||||
["_equipmentRewards", [], [[]]],
|
||||
["_supplyRewards", [], [[]]],
|
||||
["_weaponRewards", [], [[]]],
|
||||
["_vehicleRewards", [], [[]]],
|
||||
["_specialRewards", [], [[]]]
|
||||
["_taskID", "", [""]],
|
||||
["_limitFail", -1, [0]],
|
||||
["_limitSuccess", -1, [0]],
|
||||
["_companyFunds", 0, [0]],
|
||||
["_ratingFail", 0, [0]],
|
||||
["_ratingSuccess", 0, [0]],
|
||||
["_endSuccess", false, [false]],
|
||||
["_endFail", false, [false]],
|
||||
["_timeLimit", 0, [0]],
|
||||
["_equipmentRewards", [], [[]]],
|
||||
["_supplyRewards", [], [[]]],
|
||||
["_weaponRewards", [], [[]]],
|
||||
["_vehicleRewards", [], [[]]],
|
||||
["_specialRewards", [], [[]]]
|
||||
];
|
||||
|
||||
private _result = 0;
|
||||
private _targets = [];
|
||||
private _taskParams = createHashMapFromArray [
|
||||
["limitFail", _limitFail],
|
||||
["limitSuccess", _limitSuccess],
|
||||
["funds", _companyFunds],
|
||||
["ratingFail", _ratingFail],
|
||||
["ratingSuccess", _ratingSuccess],
|
||||
["endSuccess", _endSuccess],
|
||||
["endFail", _endFail],
|
||||
["timeLimit", _timeLimit],
|
||||
["useTaskStore", true]
|
||||
];
|
||||
|
||||
waitUntil {
|
||||
sleep 1;
|
||||
_targets = GVAR(TaskStore) call ["getTaskEntities", ["targets", _taskID]];
|
||||
GVAR(TaskStore) call ["trackParticipants", [_taskID, _targets, "", 300]];
|
||||
count _targets > 0
|
||||
};
|
||||
if (_equipmentRewards isNotEqualTo []) then { _taskParams set ["equipment", _equipmentRewards]; };
|
||||
if (_supplyRewards isNotEqualTo []) then { _taskParams set ["supplies", _supplyRewards]; };
|
||||
if (_weaponRewards isNotEqualTo []) then { _taskParams set ["weapons", _weaponRewards]; };
|
||||
if (_vehicleRewards isNotEqualTo []) then { _taskParams set ["vehicles", _vehicleRewards]; };
|
||||
if (_specialRewards isNotEqualTo []) then { _taskParams set ["special", _specialRewards]; };
|
||||
|
||||
_targets = GVAR(TaskStore) call ["getTaskEntities", ["targets", _taskID]];
|
||||
|
||||
if (_timeLimit isNotEqualTo 0) then {
|
||||
private _catalogEntry = GVAR(TaskStore) call ["getTaskCatalogEntry", [_taskID]];
|
||||
["INFO", format [
|
||||
"Attack task %1 initial state before acceptance wait. Accepted=%2, RequesterUid='%3', Source='%4', TimeLimit=%5s",
|
||||
private _task = createHashMapObject [
|
||||
GVAR(AttackTaskBaseClass),
|
||||
[
|
||||
_taskID,
|
||||
_catalogEntry getOrDefault ["accepted", false],
|
||||
_catalogEntry getOrDefault ["requesterUid", ""],
|
||||
_catalogEntry getOrDefault ["source", ""],
|
||||
_timeLimit
|
||||
]] call EFUNC(common,log);
|
||||
createHashMapFromArray [["targets", GVAR(TaskStore) call ["getTaskEntities", ["targets", _taskID]]]],
|
||||
_taskParams
|
||||
]
|
||||
];
|
||||
|
||||
["INFO", format ["Attack task %1 waiting for acceptance before starting %2s time limit.", _taskID, _timeLimit]] call EFUNC(common,log);
|
||||
waitUntil {
|
||||
sleep 1;
|
||||
GVAR(TaskStore) call ["isTaskAccepted", [_taskID]]
|
||||
};
|
||||
|
||||
["INFO", format ["Attack task %1 accepted. Starting %2s time limit.", _taskID, _timeLimit]] call EFUNC(common,log);
|
||||
};
|
||||
|
||||
private _startTime = if (_timeLimit isNotEqualTo 0) then { floor(time) } else { nil };
|
||||
|
||||
waitUntil {
|
||||
sleep 1;
|
||||
GVAR(TaskStore) call ["trackParticipants", [_taskID, _targets, "", 300]];
|
||||
|
||||
private _targetsKilled = ({ !alive _x } count _targets);
|
||||
|
||||
if (_timeLimit isNotEqualTo 0) then {
|
||||
private _timeExpired = (floor time - _startTime >= _timeLimit);
|
||||
|
||||
if (_targetsKilled < _limitSuccess && _timeExpired) then {
|
||||
["WARNING", format [
|
||||
"Attack task %1 failed by timeout. TargetsKilled=%2, Required=%3, TimeLimit=%4s",
|
||||
_taskID,
|
||||
_targetsKilled,
|
||||
_limitSuccess,
|
||||
_timeLimit
|
||||
]] call EFUNC(common,log);
|
||||
_result = 1;
|
||||
};
|
||||
|
||||
(_result == 1) or (_targetsKilled >= _limitSuccess)
|
||||
} else {
|
||||
(_targetsKilled >= _limitSuccess)
|
||||
};
|
||||
};
|
||||
|
||||
if (_result == 1) then {
|
||||
{ deleteVehicle _x } forEach _targets;
|
||||
|
||||
[_taskID, "FAILED"] call BFUNC(taskSetState);
|
||||
GVAR(TaskStore) call ["setTaskStatus", [_taskID, "failed"]];
|
||||
|
||||
sleep 1;
|
||||
|
||||
GVAR(TaskStore) call ["notifyParticipants", [_taskID, "warning", "Tasks", format ["Task failed: %1 reputation", _ratingFail]]];
|
||||
GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingFail]];
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
|
||||
if (_endFail) then { "EveryoneLost" call BFUNC(endMissionServer); };
|
||||
} else {
|
||||
["INFO", format [
|
||||
"Attack task %1 succeeded. TargetsRequired=%2, TargetsKilled=%3",
|
||||
_taskID,
|
||||
_limitSuccess,
|
||||
{ !alive _x } count _targets
|
||||
]] call EFUNC(common,log);
|
||||
|
||||
{ deleteVehicle _x } forEach _targets;
|
||||
|
||||
private _rewards = createHashMap;
|
||||
_rewards set ["funds", _companyFunds];
|
||||
|
||||
if (_equipmentRewards isNotEqualTo []) then { _rewards set ["equipment", _equipmentRewards]; };
|
||||
if (_supplyRewards isNotEqualTo []) then { _rewards set ["supplies", _supplyRewards]; };
|
||||
if (_weaponRewards isNotEqualTo []) then { _rewards set ["weapons", _weaponRewards]; };
|
||||
if (_vehicleRewards isNotEqualTo []) then { _rewards set ["vehicles", _vehicleRewards]; };
|
||||
if (_specialRewards isNotEqualTo []) then { _rewards set ["special", _specialRewards]; };
|
||||
|
||||
[_taskID, _rewards] call FUNC(handleTaskRewards);
|
||||
[_taskID, "SUCCEEDED"] call BFUNC(taskSetState);
|
||||
GVAR(TaskStore) call ["setTaskStatus", [_taskID, "succeeded"]];
|
||||
|
||||
sleep 1;
|
||||
|
||||
GVAR(TaskStore) call ["notifyParticipants", [_taskID, "success", "Tasks", format ["Task completed: %1 reputation, $%2 funds", _ratingSuccess, [_companyFunds] call EFUNC(common,formatNumber)]]];
|
||||
GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingSuccess]];
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
|
||||
if (_endSuccess) then { "EveryoneWon" call BFUNC(endMissionServer); };
|
||||
};
|
||||
_task call ["runLoop", []];
|
||||
|
||||
@ -26,6 +26,7 @@ GVAR(TaskStore) = createHashMapObject [[
|
||||
["#type", "TaskStore"],
|
||||
["#create", compileFinal {
|
||||
_self set ["participantRegistry", createHashMap];
|
||||
_self set ["taskLifecycleRegistry", createHashMap];
|
||||
_self set ["taskEntityRegistries", createHashMapFromArray [
|
||||
["cargo", createHashMap],
|
||||
["hostages", createHashMap],
|
||||
@ -148,6 +149,51 @@ GVAR(TaskStore) = createHashMapObject [[
|
||||
private _envelope = _self call ["callTaskStateEnvelope", ["task:ownership:release", [_taskID]]];
|
||||
_envelope getOrDefault ["success", false]
|
||||
}],
|
||||
["buildTaskLifecycleEventPayload", compileFinal {
|
||||
params [["_taskID", "", [""]], ["_status", "", [""]], ["_extra", createHashMap]];
|
||||
|
||||
if !(_extra isEqualType createHashMap) then {
|
||||
_extra = createHashMap;
|
||||
};
|
||||
|
||||
private _catalogEntry = _self call ["getTaskCatalogEntry", [_taskID]];
|
||||
private _lifecycleRegistry = _self getOrDefault ["taskLifecycleRegistry", createHashMap];
|
||||
private _lifecycle = +(_lifecycleRegistry getOrDefault [_taskID, createHashMap]);
|
||||
private _startedAt = _lifecycle getOrDefault ["startedAt", -1];
|
||||
private _finishedAt = _lifecycle getOrDefault ["finishedAt", -1];
|
||||
|
||||
createHashMapFromArray [
|
||||
["taskID", _taskID],
|
||||
["taskType", _catalogEntry getOrDefault ["type", ""]],
|
||||
["title", _catalogEntry getOrDefault ["title", _taskID]],
|
||||
["description", _catalogEntry getOrDefault ["description", ""]],
|
||||
["position", +(_catalogEntry getOrDefault ["position", []])],
|
||||
["status", _status],
|
||||
["source", _catalogEntry getOrDefault ["source", "task"]],
|
||||
["requesterUid", _catalogEntry getOrDefault ["requesterUid", ""]],
|
||||
["orgID", _catalogEntry getOrDefault ["orgID", "default"]],
|
||||
["startedAt", _startedAt],
|
||||
["finishedAt", _finishedAt],
|
||||
["duration", if (_startedAt >= 0 && { _finishedAt >= 0 }) then { _finishedAt - _startedAt } else { -1 }],
|
||||
["failureReason", _extra getOrDefault ["failureReason", ""]],
|
||||
["participants", _self call ["getTaskParticipantUids", [_taskID]]],
|
||||
["rewardData", +(_extra getOrDefault ["rewardData", createHashMap])],
|
||||
["resultSnapshot", +(_extra getOrDefault ["resultSnapshot", createHashMap])],
|
||||
["catalogEntry", +_catalogEntry]
|
||||
]
|
||||
}],
|
||||
["emitTaskLifecycleEvent", compileFinal {
|
||||
params [["_eventName", "", [""]], ["_taskID", "", [""]], ["_status", "", [""]], ["_extra", createHashMap]];
|
||||
|
||||
if (_eventName isEqualTo "" || { _taskID isEqualTo "" }) exitWith { createHashMap };
|
||||
if (isNil QEGVAR(common,EventBus)) exitWith { createHashMap };
|
||||
|
||||
EGVAR(common,EventBus) call ["emit", [
|
||||
_eventName,
|
||||
_self call ["buildTaskLifecycleEventPayload", [_taskID, _status, _extra]],
|
||||
createHashMapFromArray [["source", "task"]]
|
||||
]]
|
||||
}],
|
||||
["registerTaskCatalogEntry", compileFinal {
|
||||
params [["_taskID", "", [""]], ["_entry", createHashMap, [createHashMap]]];
|
||||
|
||||
@ -160,7 +206,19 @@ GVAR(TaskStore) = createHashMapObject [[
|
||||
[_taskID, toJSON _entry]
|
||||
]
|
||||
];
|
||||
_envelope getOrDefault ["success", false]
|
||||
private _registered = _envelope getOrDefault ["success", false];
|
||||
|
||||
if (_registered) then {
|
||||
private _lifecycleRegistry = _self getOrDefault ["taskLifecycleRegistry", createHashMap];
|
||||
private _lifecycle = +(_lifecycleRegistry getOrDefault [_taskID, createHashMap]);
|
||||
_lifecycle set ["createdAt", serverTime];
|
||||
_lifecycleRegistry set [_taskID, _lifecycle];
|
||||
_self set ["taskLifecycleRegistry", _lifecycleRegistry];
|
||||
|
||||
_self call ["emitTaskLifecycleEvent", ["task.created", _taskID, "created", createHashMap]];
|
||||
};
|
||||
|
||||
_registered
|
||||
}],
|
||||
["getActiveTaskCatalog", compileFinal {
|
||||
private _entries = _self call ["callTaskState", ["task:catalog:active", [], []]];
|
||||
@ -250,7 +308,37 @@ GVAR(TaskStore) = createHashMapObject [[
|
||||
|
||||
if (_taskID isEqualTo "" || { _status isEqualTo "" }) exitWith { false };
|
||||
|
||||
[(_self call ["callTaskState", ["task:status:set", [_taskID, _status], false]])] params [["_statusResult", false, [false]]];
|
||||
private _envelope = _self call ["callTaskStateEnvelope", ["task:status:set", [_taskID, _status]]];
|
||||
private _statusResult = _envelope getOrDefault ["success", false];
|
||||
|
||||
if (_statusResult) then {
|
||||
private _normalizedStatus = toLowerANSI _status;
|
||||
private _lifecycleRegistry = _self getOrDefault ["taskLifecycleRegistry", createHashMap];
|
||||
private _lifecycle = +(_lifecycleRegistry getOrDefault [_taskID, createHashMap]);
|
||||
private _eventName = "";
|
||||
|
||||
switch (_normalizedStatus) do {
|
||||
case "active": {
|
||||
_lifecycle set ["startedAt", serverTime];
|
||||
_eventName = "task.started";
|
||||
};
|
||||
case "succeeded": {
|
||||
_lifecycle set ["finishedAt", serverTime];
|
||||
_eventName = "task.completed";
|
||||
};
|
||||
case "failed": {
|
||||
_lifecycle set ["finishedAt", serverTime];
|
||||
_eventName = "task.failed";
|
||||
};
|
||||
};
|
||||
|
||||
_lifecycleRegistry set [_taskID, _lifecycle];
|
||||
_self set ["taskLifecycleRegistry", _lifecycleRegistry];
|
||||
|
||||
if (_eventName isNotEqualTo "") then {
|
||||
_self call ["emitTaskLifecycleEvent", [_eventName, _taskID, _normalizedStatus, createHashMap]];
|
||||
};
|
||||
};
|
||||
|
||||
_statusResult
|
||||
}],
|
||||
@ -391,6 +479,21 @@ GVAR(TaskStore) = createHashMapObject [[
|
||||
|
||||
_participantSnapshots
|
||||
}],
|
||||
["getTaskParticipants", compileFinal {
|
||||
params [["_taskID", "", [""]]];
|
||||
|
||||
if (_taskID isEqualTo "") exitWith { createHashMap };
|
||||
|
||||
private _participantRegistry = _self getOrDefault ["participantRegistry", createHashMap];
|
||||
+(_participantRegistry getOrDefault [_taskID, createHashMap])
|
||||
}],
|
||||
["getTaskParticipantUids", compileFinal {
|
||||
params [["_taskID", "", [""]]];
|
||||
|
||||
if (_taskID isEqualTo "") exitWith { [] };
|
||||
|
||||
keys (_self call ["getTaskParticipants", [_taskID]])
|
||||
}],
|
||||
["resolveRewardContext", compileFinal {
|
||||
params [["_taskID", "", [""]]];
|
||||
|
||||
@ -483,14 +586,20 @@ GVAR(TaskStore) = createHashMapObject [[
|
||||
params [["_taskID", "", [""]]];
|
||||
|
||||
if (_taskID isEqualTo "") exitWith { false };
|
||||
|
||||
if !(isNil QGVAR(MissionManager)) then {
|
||||
GVAR(MissionManager) call ["completeMission", [_taskID]];
|
||||
};
|
||||
|
||||
_self call ["emitTaskLifecycleEvent", ["task.cleared", _taskID, "cleared", createHashMap]];
|
||||
|
||||
private _participantRegistry = _self getOrDefault ["participantRegistry", createHashMap];
|
||||
_participantRegistry deleteAt _taskID;
|
||||
_self set ["participantRegistry", _participantRegistry];
|
||||
|
||||
private _lifecycleRegistry = _self getOrDefault ["taskLifecycleRegistry", createHashMap];
|
||||
_lifecycleRegistry deleteAt _taskID;
|
||||
_self set ["taskLifecycleRegistry", _lifecycleRegistry];
|
||||
|
||||
_self call ["callTaskState", ["task:clear", [_taskID], false]];
|
||||
_self call ["clearTaskEntities", [_taskID]];
|
||||
true
|
||||
|
||||
@ -67,6 +67,24 @@ GVAR(AttackTaskBaseClass) = createHashMapFromArray [
|
||||
["#delete", compileFinal {
|
||||
_self call ["unregisterInstance", []];
|
||||
}],
|
||||
["refreshTargetsFromStore", compileFinal {
|
||||
private _taskID = _self getOrDefault ["taskID", ""];
|
||||
if (_taskID isEqualTo "" || { !(_self getOrDefault ["useTaskStore", false]) }) exitWith { false };
|
||||
|
||||
private _targets = GVAR(TaskStore) call ["getTaskEntities", ["targets", _taskID]];
|
||||
_self set ["targets", _targets];
|
||||
|
||||
private _taskParams = _self getOrDefault ["taskParams", createHashMap];
|
||||
private _requiredKills = _taskParams getOrDefault ["limitSuccess", -1];
|
||||
if (_requiredKills < 0) then { _requiredKills = count _targets; };
|
||||
|
||||
private _maxTargetLosses = _taskParams getOrDefault ["limitFail", -1];
|
||||
if (_maxTargetLosses < 0) then { _maxTargetLosses = count _targets; };
|
||||
|
||||
_self set ["requiredKills", _requiredKills];
|
||||
_self set ["maxTargetLosses", _maxTargetLosses];
|
||||
true
|
||||
}],
|
||||
["countKilledTargets", compileFinal {
|
||||
private _targets = _self getOrDefault ["targets", []];
|
||||
{ !alive _x } count _targets
|
||||
@ -94,7 +112,6 @@ GVAR(AttackTaskBaseClass) = createHashMapFromArray [
|
||||
}],
|
||||
["runLoop", compileFinal {
|
||||
private _taskID = _self getOrDefault ["taskID", ""];
|
||||
private _targets = _self getOrDefault ["targets", []];
|
||||
private _timeLimit = _self getOrDefault ["timeLimit", 0];
|
||||
private _rewardData = _self getOrDefault ["rewardData", createHashMap];
|
||||
private _ratingFail = _rewardData getOrDefault ["ratingFail", 0];
|
||||
@ -107,37 +124,69 @@ GVAR(AttackTaskBaseClass) = createHashMapFromArray [
|
||||
if (_useTaskStore) then {
|
||||
waitUntil {
|
||||
sleep 1;
|
||||
_self call ["refreshTargetsFromStore", []];
|
||||
private _targets = _self getOrDefault ["targets", []];
|
||||
GVAR(TaskStore) call ["trackParticipants", [_taskID, _targets, "", 300]];
|
||||
count _targets > 0
|
||||
};
|
||||
} else {
|
||||
waitUntil {
|
||||
sleep 1;
|
||||
count _targets > 0
|
||||
count (_self getOrDefault ["targets", []]) > 0
|
||||
};
|
||||
};
|
||||
|
||||
if (_timeLimit isNotEqualTo 0 && { _useTaskStore }) then {
|
||||
private _catalogEntry = GVAR(TaskStore) call ["getTaskCatalogEntry", [_taskID]];
|
||||
["INFO", format [
|
||||
"Attack task %1 initial state before acceptance wait. Accepted=%2, RequesterUid='%3', Source='%4', TimeLimit=%5s",
|
||||
_taskID,
|
||||
_catalogEntry getOrDefault ["accepted", false],
|
||||
_catalogEntry getOrDefault ["requesterUid", ""],
|
||||
_catalogEntry getOrDefault ["source", ""],
|
||||
_timeLimit
|
||||
]] call EFUNC(common,log);
|
||||
|
||||
["INFO", format ["Attack task %1 waiting for acceptance before starting %2s time limit.", _taskID, _timeLimit]] call EFUNC(common,log);
|
||||
waitUntil {
|
||||
sleep 1;
|
||||
GVAR(TaskStore) call ["isTaskAccepted", [_taskID]]
|
||||
};
|
||||
|
||||
["INFO", format ["Attack task %1 accepted. Starting %2s time limit.", _taskID, _timeLimit]] call EFUNC(common,log);
|
||||
};
|
||||
|
||||
_self call ["markActive", []];
|
||||
|
||||
while { (_self call ["getStatus", []]) isEqualTo "active" } do {
|
||||
private _targets = _self getOrDefault ["targets", []];
|
||||
|
||||
if (_useTaskStore) then {
|
||||
_self call ["refreshTargetsFromStore", []];
|
||||
_targets = _self getOrDefault ["targets", []];
|
||||
GVAR(TaskStore) call ["trackParticipants", [_taskID, _targets, "", 300]];
|
||||
};
|
||||
|
||||
private _snapshot = _self call ["tick", []];
|
||||
|
||||
if (_snapshot getOrDefault ["shouldFail", false]) exitWith {
|
||||
["WARNING", format [
|
||||
"Attack task %1 failed by timeout. TargetsKilled=%2, Required=%3, TimeLimit=%4s",
|
||||
_taskID,
|
||||
_snapshot getOrDefault ["targetsKilled", 0],
|
||||
_snapshot getOrDefault ["requiredKills", 0],
|
||||
_timeLimit
|
||||
]] call EFUNC(common,log);
|
||||
_self call ["markFailed", ["Attack fail conditions met.", _snapshot]];
|
||||
};
|
||||
|
||||
if (_snapshot getOrDefault ["shouldSucceed", false]) exitWith {
|
||||
["INFO", format [
|
||||
"Attack task %1 succeeded. TargetsRequired=%2, TargetsKilled=%3",
|
||||
_taskID,
|
||||
_snapshot getOrDefault ["requiredKills", 0],
|
||||
_snapshot getOrDefault ["targetsKilled", 0]
|
||||
]] call EFUNC(common,log);
|
||||
_self call ["markSucceeded", [_snapshot]];
|
||||
};
|
||||
|
||||
@ -145,6 +194,7 @@ GVAR(AttackTaskBaseClass) = createHashMapFromArray [
|
||||
};
|
||||
|
||||
if ((_self call ["getStatus", []]) isEqualTo "failed") then {
|
||||
private _targets = _self getOrDefault ["targets", []];
|
||||
{ deleteVehicle _x } forEach _targets;
|
||||
|
||||
if (_useTaskStore) then {
|
||||
@ -160,6 +210,7 @@ GVAR(AttackTaskBaseClass) = createHashMapFromArray [
|
||||
|
||||
if (_endFail) then { "EveryoneLost" call BFUNC(endMissionServer); };
|
||||
} else {
|
||||
private _targets = _self getOrDefault ["targets", []];
|
||||
{ deleteVehicle _x } forEach _targets;
|
||||
|
||||
if (_useTaskStore) then {
|
||||
|
||||
@ -108,9 +108,55 @@ GVAR(TaskInstanceBaseClass) = createHashMapFromArray [
|
||||
missionNamespace setVariable [_registryKey, nil];
|
||||
true
|
||||
}],
|
||||
["buildLifecycleEventPayload", compileFinal {
|
||||
private _taskID = _self getOrDefault ["taskID", ""];
|
||||
private _taskType = _self getOrDefault ["taskType", "custom"];
|
||||
private _status = _self getOrDefault ["status", "created"];
|
||||
private _startedAt = _self getOrDefault ["startedAt", -1];
|
||||
private _finishedAt = _self getOrDefault ["finishedAt", -1];
|
||||
private _participantUids = [];
|
||||
|
||||
if (
|
||||
_taskID isNotEqualTo ""
|
||||
&& { _self getOrDefault ["useTaskStore", false] }
|
||||
&& { !(isNil QGVAR(TaskStore)) }
|
||||
) then {
|
||||
_participantUids = GVAR(TaskStore) call ["getTaskParticipantUids", [_taskID]];
|
||||
};
|
||||
|
||||
private _payload = createHashMapFromArray [
|
||||
["taskID", _taskID],
|
||||
["taskType", _taskType],
|
||||
["status", _status],
|
||||
["startedAt", _startedAt],
|
||||
["finishedAt", _finishedAt],
|
||||
["duration", if (_startedAt >= 0 && { _finishedAt >= 0 }) then { _finishedAt - _startedAt } else { -1 }],
|
||||
["failureReason", _self getOrDefault ["failureReason", ""]],
|
||||
["participants", _participantUids],
|
||||
["rewardData", +(_self getOrDefault ["rewardData", createHashMap])],
|
||||
["resultSnapshot", +(_self getOrDefault ["resultSnapshot", createHashMap])]
|
||||
];
|
||||
|
||||
_payload
|
||||
}],
|
||||
["emitLifecycleEvent", compileFinal {
|
||||
params [["_eventName", "", [""]]];
|
||||
|
||||
if (_eventName isEqualTo "") exitWith { createHashMap };
|
||||
if (isNil QEGVAR(common,EventBus)) exitWith { createHashMap };
|
||||
|
||||
EGVAR(common,EventBus) call ["emit", [
|
||||
_eventName,
|
||||
_self call ["buildLifecycleEventPayload", []],
|
||||
createHashMapFromArray [["source", "task"]]
|
||||
]]
|
||||
}],
|
||||
["markActive", compileFinal {
|
||||
_self set ["status", "active"];
|
||||
_self set ["startedAt", serverTime];
|
||||
if !(_self getOrDefault ["useTaskStore", false]) then {
|
||||
_self call ["emitLifecycleEvent", ["task.started"]];
|
||||
};
|
||||
true
|
||||
}],
|
||||
["markSucceeded", compileFinal {
|
||||
@ -119,6 +165,9 @@ GVAR(TaskInstanceBaseClass) = createHashMapFromArray [
|
||||
_self set ["status", "succeeded"];
|
||||
_self set ["finishedAt", serverTime];
|
||||
_self set ["resultSnapshot", _resultSnapshot];
|
||||
if !(_self getOrDefault ["useTaskStore", false]) then {
|
||||
_self call ["emitLifecycleEvent", ["task.completed"]];
|
||||
};
|
||||
true
|
||||
}],
|
||||
["markFailed", compileFinal {
|
||||
@ -128,6 +177,9 @@ GVAR(TaskInstanceBaseClass) = createHashMapFromArray [
|
||||
_self set ["finishedAt", serverTime];
|
||||
_self set ["failureReason", _reason];
|
||||
_self set ["resultSnapshot", _resultSnapshot];
|
||||
if !(_self getOrDefault ["useTaskStore", false]) then {
|
||||
_self call ["emitLifecycleEvent", ["task.failed"]];
|
||||
};
|
||||
true
|
||||
}],
|
||||
["cleanup", compileFinal {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user