- Replace task-only refresh flow with hydrate/assignment/group events - Add contracts, groups, and activity tabs to the client sidepanel - Introduce server-side CAD store and request handlers
499 lines
18 KiB
Plaintext
499 lines
18 KiB
Plaintext
#include "..\script_component.hpp"
|
|
|
|
/*
|
|
* File: fnc_initCadStore.sqf
|
|
* Author: IDSolutions
|
|
* Date: 2026-03-29
|
|
* Public: Yes
|
|
*
|
|
* Description:
|
|
* Initializes the CAD store for group tracking, assignment state,
|
|
* activity history, and CAD hydrate payloads.
|
|
*
|
|
* Arguments:
|
|
* None
|
|
*
|
|
* Return Value:
|
|
* CAD store object [HASHMAP OBJECT]
|
|
*
|
|
* Example:
|
|
* call forge_server_cad_fnc_initCadStore
|
|
*/
|
|
|
|
#pragma hemtt ignore_variables ["_self"]
|
|
GVAR(CadStoreBaseClass) = compileFinal createHashMapFromArray [
|
|
["#type", "CadStoreBaseClass"],
|
|
["#create", compileFinal {
|
|
_self set ["groupRegistry", createHashMap];
|
|
_self set ["assignmentRegistry", createHashMap];
|
|
_self set ["activityRegistry", []];
|
|
_self set ["validStatuses", [
|
|
"available",
|
|
"en_route",
|
|
"on_task",
|
|
"holding",
|
|
"danger",
|
|
"refit",
|
|
"offline"
|
|
]];
|
|
["INFO", "CAD Store Initialized!"] call EFUNC(common,log);
|
|
}],
|
|
["appendActivity", compileFinal {
|
|
params [
|
|
["_type", "", [""]],
|
|
["_message", "", [""]],
|
|
["_taskID", "", [""]],
|
|
["_groupID", "", [""]],
|
|
["_actorUid", "", [""]]
|
|
];
|
|
|
|
if (_type isEqualTo "" || { _message isEqualTo "" }) exitWith { false };
|
|
|
|
private _activityRegistry = +(_self getOrDefault ["activityRegistry", []]);
|
|
_activityRegistry pushBack createHashMapFromArray [
|
|
["type", _type],
|
|
["message", _message],
|
|
["timestamp", serverTime],
|
|
["taskId", _taskID],
|
|
["groupId", _groupID],
|
|
["actorUid", _actorUid]
|
|
];
|
|
|
|
if ((count _activityRegistry) > 50) then {
|
|
_activityRegistry deleteRange [0, (count _activityRegistry) - 50];
|
|
};
|
|
|
|
_self set ["activityRegistry", _activityRegistry];
|
|
true
|
|
}],
|
|
["resolveGroupId", compileFinal {
|
|
params [["_group", grpNull, [grpNull]]];
|
|
|
|
if (isNull _group) exitWith { "" };
|
|
|
|
private _leader = leader _group;
|
|
private _leaderUid = if (isNull _leader) then { "" } else { getPlayerUID _leader };
|
|
if (_leaderUid isNotEqualTo "") exitWith { format ["group:%1", _leaderUid] };
|
|
|
|
private _groupLabel = groupId _group;
|
|
if (_groupLabel isNotEqualTo "") exitWith { format ["group:%1", _groupLabel] };
|
|
|
|
str _group
|
|
}],
|
|
["canDispatch", compileFinal {
|
|
params [["_uid", "", [""]]];
|
|
|
|
if (_uid isEqualTo "") exitWith { false };
|
|
|
|
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", "default"];
|
|
private _org = EGVAR(org,Registry) getOrDefault [_orgID, createHashMap];
|
|
if (_org isEqualTo createHashMap) then {
|
|
_org = EGVAR(org,OrgStore) call ["loadById", [_orgID]];
|
|
};
|
|
|
|
if (_org getOrDefault ["owner", ""] isEqualTo _uid) exitWith { true };
|
|
|
|
private _player = [_uid] call EFUNC(common,getPlayer);
|
|
if (_player isEqualTo objNull) exitWith { false };
|
|
|
|
(_orgID isEqualTo "default") && { vehicleVarName _player isEqualTo "ceo" }
|
|
}],
|
|
["getCurrentTaskIdForGroup", compileFinal {
|
|
params [["_groupID", "", [""]]];
|
|
|
|
if (_groupID isEqualTo "") exitWith { "" };
|
|
|
|
private _assignmentRegistry = _self getOrDefault ["assignmentRegistry", createHashMap];
|
|
private _taskID = "";
|
|
{
|
|
if ((_y getOrDefault ["groupId", ""]) isNotEqualTo _groupID) then { continue; };
|
|
if !((_y getOrDefault ["state", ""]) in ["assigned", "acknowledged"]) then { continue; };
|
|
if ((EGVAR(task,TaskStore) call ["getTaskStatus", [_x]]) isNotEqualTo "active") then { continue; };
|
|
|
|
_taskID = _x;
|
|
} forEach _assignmentRegistry;
|
|
|
|
_taskID
|
|
}],
|
|
["syncGroups", compileFinal {
|
|
private _previousRegistry = _self getOrDefault ["groupRegistry", createHashMap];
|
|
private _nextRegistry = createHashMap;
|
|
|
|
{
|
|
if (side _x isNotEqualTo west) then { continue; };
|
|
|
|
private _members = (units _x) select { isPlayer _x };
|
|
if (_members isEqualTo []) then { continue; };
|
|
|
|
private _leader = leader _x;
|
|
if (isNull _leader || { !isPlayer _leader }) then {
|
|
_leader = _members # 0;
|
|
};
|
|
|
|
private _groupID = _self call ["resolveGroupId", [_x]];
|
|
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 _existingRecord = +(_previousRegistry getOrDefault [_groupID, createHashMap]);
|
|
private _memberUids = [];
|
|
{
|
|
private _memberUid = getPlayerUID _x;
|
|
if (_memberUid isNotEqualTo "") then {
|
|
_memberUids pushBack _memberUid;
|
|
};
|
|
} forEach _members;
|
|
|
|
private _record = createHashMapFromArray [
|
|
["groupId", _groupID],
|
|
["callsign", [groupId _x, _groupID] select ((groupId _x) isEqualTo "")],
|
|
["leaderUid", _leaderUid],
|
|
["leaderName", name _leader],
|
|
["memberUids", _memberUids],
|
|
["orgId", _orgID],
|
|
["role", _existingRecord getOrDefault ["role", "infantry"]],
|
|
["status", _existingRecord getOrDefault ["status", "available"]],
|
|
["position", getPosATL _leader],
|
|
["currentTaskId", _self call ["getCurrentTaskIdForGroup", [_groupID]]],
|
|
["lastUpdate", serverTime]
|
|
];
|
|
|
|
_nextRegistry set [_groupID, _record];
|
|
} forEach allGroups;
|
|
|
|
_self set ["groupRegistry", _nextRegistry];
|
|
_nextRegistry
|
|
}],
|
|
["getGroupRecord", compileFinal {
|
|
params [["_groupID", "", [""]]];
|
|
|
|
if (_groupID isEqualTo "") exitWith { createHashMap };
|
|
|
|
private _groupRegistry = _self call ["syncGroups", []];
|
|
+(_groupRegistry getOrDefault [_groupID, createHashMap])
|
|
}],
|
|
["getPlayerGroupId", compileFinal {
|
|
params [["_uid", "", [""]]];
|
|
|
|
if (_uid isEqualTo "") exitWith { "" };
|
|
|
|
private _player = [_uid] call EFUNC(common,getPlayer);
|
|
if (_player isEqualTo objNull) exitWith { "" };
|
|
|
|
_self call ["resolveGroupId", [group _player]]
|
|
}],
|
|
["isGroupLeader", compileFinal {
|
|
params [["_uid", "", [""]], ["_groupID", "", [""]]];
|
|
|
|
if (_uid isEqualTo "" || { _groupID isEqualTo "" }) exitWith { false };
|
|
|
|
private _groupRecord = _self call ["getGroupRecord", [_groupID]];
|
|
(_groupRecord getOrDefault ["leaderUid", ""]) isEqualTo _uid
|
|
}],
|
|
["pruneAssignments", compileFinal {
|
|
private _assignmentRegistry = _self getOrDefault ["assignmentRegistry", createHashMap];
|
|
private _keysToRemove = [];
|
|
|
|
{
|
|
private _status = EGVAR(task,TaskStore) call ["getTaskStatus", [_x]];
|
|
if !(_status in ["active", ""]) then {
|
|
_keysToRemove pushBack _x;
|
|
};
|
|
} forEach _assignmentRegistry;
|
|
|
|
{
|
|
_assignmentRegistry deleteAt _x;
|
|
} forEach _keysToRemove;
|
|
|
|
_self set ["assignmentRegistry", _assignmentRegistry];
|
|
count _keysToRemove
|
|
}],
|
|
["buildContracts", compileFinal {
|
|
_self call ["pruneAssignments", []];
|
|
|
|
private _assignmentRegistry = _self getOrDefault ["assignmentRegistry", createHashMap];
|
|
private _contracts = [];
|
|
|
|
{
|
|
private _taskID = _x getOrDefault ["taskID", ""];
|
|
if (_taskID isEqualTo "") then { continue; };
|
|
|
|
private _assignment = _assignmentRegistry getOrDefault [_taskID, createHashMap];
|
|
private _entry = +_x;
|
|
_entry set ["taskId", _taskID];
|
|
_entry set ["assignedGroupId", _assignment getOrDefault ["groupId", ""]];
|
|
_entry set ["assignmentState", [_assignment getOrDefault ["state", ""], "unassigned"] select (_assignment isEqualTo createHashMap)];
|
|
_contracts pushBack _entry;
|
|
} forEach (EGVAR(task,TaskStore) call ["getActiveTaskCatalog", []]);
|
|
|
|
_contracts
|
|
}],
|
|
["buildGroups", compileFinal {
|
|
private _groupRegistry = _self call ["syncGroups", []];
|
|
private _groups = [];
|
|
|
|
{
|
|
_groups pushBack +_y;
|
|
} forEach _groupRegistry;
|
|
|
|
_groups
|
|
}],
|
|
["buildHydratePayload", compileFinal {
|
|
params [["_uid", "", [""]]];
|
|
|
|
private _activity = +(_self getOrDefault ["activityRegistry", []]);
|
|
private _actor = EGVAR(actor,Registry) getOrDefault [_uid, createHashMap];
|
|
if (_actor isEqualTo createHashMap && { _uid isNotEqualTo "" }) then {
|
|
_actor = EGVAR(actor,ActorStore) call ["init", [_uid]];
|
|
};
|
|
|
|
createHashMapFromArray [
|
|
["groups", _self call ["buildGroups", []]],
|
|
["contracts", _self call ["buildContracts", []]],
|
|
["assignments", values (_self getOrDefault ["assignmentRegistry", createHashMap])],
|
|
["activity", _activity],
|
|
["session", createHashMapFromArray [
|
|
["uid", _uid],
|
|
["orgId", _actor getOrDefault ["organization", "default"]],
|
|
["isDispatcher", _self call ["canDispatch", [_uid]]],
|
|
["groupId", _self call ["getPlayerGroupId", [_uid]]],
|
|
["isLeader", _self call ["isGroupLeader", [_uid, _self call ["getPlayerGroupId", [_uid]]]]]
|
|
]]
|
|
]
|
|
}],
|
|
["notifyPlayer", compileFinal {
|
|
params [
|
|
["_uid", "", [""]],
|
|
["_type", "info", [""]],
|
|
["_title", "CAD", [""]],
|
|
["_message", "", [""]]
|
|
];
|
|
|
|
if (_uid isEqualTo "" || { _message isEqualTo "" }) exitWith { false };
|
|
|
|
private _player = [_uid] call EFUNC(common,getPlayer);
|
|
if (_player isEqualTo objNull) exitWith { false };
|
|
|
|
[CRPC(notifications,recieveNotification), [_type, _title, _message], _player] call CFUNC(targetEvent);
|
|
true
|
|
}],
|
|
["assignTaskToGroup", compileFinal {
|
|
params [
|
|
["_requesterUid", "", [""]],
|
|
["_taskID", "", [""]],
|
|
["_groupID", "", [""]],
|
|
["_note", "", [""]]
|
|
];
|
|
|
|
private _result = createHashMapFromArray [
|
|
["success", false],
|
|
["message", "Unable to assign task."],
|
|
["assignment", createHashMap]
|
|
];
|
|
|
|
if !(_self call ["canDispatch", [_requesterUid]]) exitWith {
|
|
_result set ["message", "You are not authorized to assign contracts."];
|
|
_result
|
|
};
|
|
|
|
if ((EGVAR(task,TaskStore) call ["getTaskStatus", [_taskID]]) isNotEqualTo "active") exitWith {
|
|
_result set ["message", "Task is no longer active."];
|
|
_result
|
|
};
|
|
|
|
private _groupRecord = _self call ["getGroupRecord", [_groupID]];
|
|
if (_groupRecord isEqualTo createHashMap) exitWith {
|
|
_result set ["message", "Selected group is unavailable."];
|
|
_result
|
|
};
|
|
|
|
private _leaderUid = _groupRecord getOrDefault ["leaderUid", ""];
|
|
if (_leaderUid isEqualTo "") exitWith {
|
|
_result set ["message", "Selected group has no online leader."];
|
|
_result
|
|
};
|
|
|
|
private _requesterPlayer = [_requesterUid] call EFUNC(common,getPlayer);
|
|
private _assignmentRegistry = _self getOrDefault ["assignmentRegistry", createHashMap];
|
|
private _assignment = createHashMapFromArray [
|
|
["taskId", _taskID],
|
|
["groupId", _groupID],
|
|
["assignedByUid", _requesterUid],
|
|
["assignedByName", ["Dispatcher", name _requesterPlayer] select (_requesterPlayer isNotEqualTo objNull)],
|
|
["assignedAt", serverTime],
|
|
["state", "assigned"],
|
|
["note", _note]
|
|
];
|
|
|
|
_assignmentRegistry set [_taskID, _assignment];
|
|
_self set ["assignmentRegistry", _assignmentRegistry];
|
|
|
|
_self call ["appendActivity", [
|
|
"task_assigned",
|
|
format ["%1 assigned %2 to %3.", _assignment get "assignedByName", _taskID, _groupRecord getOrDefault ["callsign", _groupID]],
|
|
_taskID,
|
|
_groupID,
|
|
_requesterUid
|
|
]];
|
|
|
|
_self call ["notifyPlayer", [
|
|
_leaderUid,
|
|
"info",
|
|
"Tasks",
|
|
format ["Contract assigned: %1. Open CAD to review and acknowledge.", _taskID]
|
|
]];
|
|
|
|
_result set ["success", true];
|
|
_result set ["message", "Task assigned."];
|
|
_result set ["assignment", _assignment];
|
|
_result
|
|
}],
|
|
["acknowledgeTask", compileFinal {
|
|
params [["_requesterUid", "", [""]], ["_taskID", "", [""]]];
|
|
|
|
private _result = createHashMapFromArray [
|
|
["success", false],
|
|
["message", "Unable to acknowledge task."],
|
|
["assignment", createHashMap]
|
|
];
|
|
|
|
private _assignmentRegistry = _self getOrDefault ["assignmentRegistry", createHashMap];
|
|
private _assignment = +(_assignmentRegistry getOrDefault [_taskID, createHashMap]);
|
|
if (_assignment isEqualTo createHashMap) exitWith {
|
|
_result set ["message", "Task is not assigned."];
|
|
_result
|
|
};
|
|
|
|
private _groupID = _assignment getOrDefault ["groupId", ""];
|
|
if !(_self call ["isGroupLeader", [_requesterUid, _groupID]]) exitWith {
|
|
_result set ["message", "Only the assigned group leader can acknowledge this task."];
|
|
_result
|
|
};
|
|
|
|
private _bindResult = EGVAR(task,TaskStore) call ["bindTaskOwnership", [_taskID, _requesterUid]];
|
|
if !(_bindResult getOrDefault ["success", false]) exitWith {
|
|
_result set ["message", _bindResult getOrDefault ["message", "Failed to bind task ownership."]];
|
|
_result
|
|
};
|
|
|
|
_assignment set ["state", "acknowledged"];
|
|
_assignment set ["acknowledgedAt", serverTime];
|
|
_assignmentRegistry set [_taskID, _assignment];
|
|
_self set ["assignmentRegistry", _assignmentRegistry];
|
|
|
|
_self call ["appendActivity", [
|
|
"task_acknowledged",
|
|
format ["%1 acknowledged %2.", _requesterUid, _taskID],
|
|
_taskID,
|
|
_groupID,
|
|
_requesterUid
|
|
]];
|
|
|
|
_result set ["success", true];
|
|
_result set ["message", "Task acknowledged."];
|
|
_result set ["assignment", _assignment];
|
|
_result
|
|
}],
|
|
["declineTask", compileFinal {
|
|
params [["_requesterUid", "", [""]], ["_taskID", "", [""]]];
|
|
|
|
private _result = createHashMapFromArray [
|
|
["success", false],
|
|
["message", "Unable to decline task."],
|
|
["assignment", createHashMap]
|
|
];
|
|
|
|
private _assignmentRegistry = _self getOrDefault ["assignmentRegistry", createHashMap];
|
|
private _assignment = +(_assignmentRegistry getOrDefault [_taskID, createHashMap]);
|
|
if (_assignment isEqualTo createHashMap) exitWith {
|
|
_result set ["message", "Task is not assigned."];
|
|
_result
|
|
};
|
|
|
|
private _groupID = _assignment getOrDefault ["groupId", ""];
|
|
if !(_self call ["isGroupLeader", [_requesterUid, _groupID]]) exitWith {
|
|
_result set ["message", "Only the assigned group leader can decline this task."];
|
|
_result
|
|
};
|
|
|
|
_assignment set ["state", "declined"];
|
|
_assignment set ["declinedAt", serverTime];
|
|
_assignmentRegistry set [_taskID, _assignment];
|
|
_self set ["assignmentRegistry", _assignmentRegistry];
|
|
|
|
_self call ["appendActivity", [
|
|
"task_declined",
|
|
format ["%1 declined %2.", _requesterUid, _taskID],
|
|
_taskID,
|
|
_groupID,
|
|
_requesterUid
|
|
]];
|
|
|
|
_result set ["success", true];
|
|
_result set ["message", "Task declined."];
|
|
_result set ["assignment", _assignment];
|
|
_result
|
|
}],
|
|
["updateGroupStatus", compileFinal {
|
|
params [["_requesterUid", "", [""]], ["_groupID", "", [""]], ["_status", "", [""]]];
|
|
|
|
private _result = createHashMapFromArray [
|
|
["success", false],
|
|
["message", "Unable to update group status."],
|
|
["group", createHashMap]
|
|
];
|
|
|
|
private _finalStatus = toLowerANSI _status;
|
|
if !(_finalStatus in (_self getOrDefault ["validStatuses", []])) exitWith {
|
|
_result set ["message", "Invalid group status."];
|
|
_result
|
|
};
|
|
|
|
private _isAuthorized = (_self call ["isGroupLeader", [_requesterUid, _groupID]]) || { _self call ["canDispatch", [_requesterUid]] };
|
|
if !_isAuthorized exitWith {
|
|
_result set ["message", "You are not authorized to update that group."];
|
|
_result
|
|
};
|
|
|
|
private _groupRegistry = _self call ["syncGroups", []];
|
|
private _groupRecord = +(_groupRegistry getOrDefault [_groupID, createHashMap]);
|
|
if (_groupRecord isEqualTo createHashMap) exitWith {
|
|
_result set ["message", "Group could not be resolved."];
|
|
_result
|
|
};
|
|
|
|
_groupRecord set ["status", _finalStatus];
|
|
_groupRecord set ["lastUpdate", serverTime];
|
|
_groupRegistry set [_groupID, _groupRecord];
|
|
_self set ["groupRegistry", _groupRegistry];
|
|
|
|
_self call ["appendActivity", [
|
|
"group_status",
|
|
format ["%1 updated %2 to %3.", _requesterUid, _groupRecord getOrDefault ["callsign", _groupID], _finalStatus],
|
|
"",
|
|
_groupID,
|
|
_requesterUid
|
|
]];
|
|
|
|
_result set ["success", true];
|
|
_result set ["message", "Group status updated."];
|
|
_result set ["group", _groupRecord];
|
|
_result
|
|
}]
|
|
];
|
|
|
|
GVAR(CadStore) = createHashMapObject [GVAR(CadStoreBaseClass)];
|
|
GVAR(CadStore)
|