From 8a90c1d184875b6c77c08d297cab16d946c88fb4 Mon Sep 17 00:00:00 2001 From: Florian Kinder Date: Sat, 11 Apr 2026 21:34:16 +0200 Subject: [PATCH 1/3] feat(extension): handle :MISSION:SAVED: async callback --- addons/extension/fnc_initSession.sqf | 43 ++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/addons/extension/fnc_initSession.sqf b/addons/extension/fnc_initSession.sqf index a7d7abd..8ffd600 100644 --- a/addons/extension/fnc_initSession.sqf +++ b/addons/extension/fnc_initSession.sqf @@ -154,6 +154,49 @@ addMissionEventHandler ["ExtensionCallback", { INFO("Mission registered. Starting data send."); GVAR(sessionReady) = true; }; + + if (_function isEqualTo ":MISSION:SAVED:") exitWith { + // Payload shapes: + // ["ok", path] + // ["partial", path, error] + // ["error", error] + private _status = _data param [0, ""]; + private _detail = _data param [1, ""]; + private _extra = _data param [2, ""]; + + switch (_status) do { + case "ok": { + INFO_1("Mission save complete — path: %1",_detail); + GVAR(lastSaveResult) = ["ok", _detail]; + [ + format["OCAP saved %1 successfully", briefingName], + 2, + [0, 0.8, 0, 1] + ] remoteExec ["CBA_fnc_notify", [0, -2] select isDedicated]; + }; + case "partial": { + WARNING_2("Mission save complete but upload failed — path: %1 error: %2",_detail,_extra); + GVAR(lastSaveResult) = ["partial", _detail, _extra]; + [ + format["OCAP saved locally (%1) but upload failed: %2", _detail, _extra], + 2, + [1, 0.8, 0, 1] + ] remoteExec ["CBA_fnc_notify", [0, -2] select isDedicated]; + }; + case "error": { + ERROR_MSG_1("Mission save failed: %1",_detail); + GVAR(lastSaveResult) = ["error", _detail]; + [ + format["OCAP save failed: %1", _detail], + 2, + [1, 0, 0, 1] + ] remoteExec ["CBA_fnc_notify", [0, -2] select isDedicated]; + }; + default { + WARNING_1("Unknown :MISSION:SAVED: status: %1",_status); + }; + }; + }; }]; From 07c22cee58c96eff52552e611e090fea3834e63a Mon Sep 17 00:00:00 2001 From: Florian Kinder Date: Sat, 11 Apr 2026 21:34:35 +0200 Subject: [PATCH 2/3] fix(recorder): remove misleading sync save logging in fnc_exportData --- addons/recorder/fnc_exportData.sqf | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/addons/recorder/fnc_exportData.sqf b/addons/recorder/fnc_exportData.sqf index a4654d3..1ebe1b7 100644 --- a/addons/recorder/fnc_exportData.sqf +++ b/addons/recorder/fnc_exportData.sqf @@ -123,14 +123,17 @@ private _endMessage = if (isNil "_message") then {if (_winSide == "") then {"Mis private _saveTag = if (!isNil "_tag") then {_tag} else {EGVAR(settings,saveTag)}; INFO_3("Saving recording — mission: %1 | frames: %2 | tag: %3",GVAR(missionName),_endFrameNumber,_saveTag); -private _saveStart = diag_tickTime; -[":MISSION:SAVE:", []] call EFUNC(extension,sendData); -INFO_2("Recording saved — took %1 ms | mission: %2",round ((diag_tickTime - _saveStart) * 1000),GVAR(missionName)); -OCAPEXTLOG(ARR4("Saved recording of mission",GVAR(missionName),"with tag",_saveTag)); +// Save is now asynchronous — the extension returns immediately and will fire +// a :MISSION:SAVED: ExtensionCallback when the write + upload finishes. +// The final success/failure notification is driven from that callback in +// fnc_initSession.sqf. +INFO_2("Mission save queued — mission: %1 | frames: %2",GVAR(missionName),_endFrameNumber); +[":MISSION:SAVE:", []] call EFUNC(extension,sendData); +OCAPEXTLOG(ARR4("Queued recording of mission",GVAR(missionName),"with tag",_saveTag)); -// notify players that the recording was saved with a 2 second delay to ensure the "stopped recording" entries populate first -[format["OCAP saved %1 frames successfully", _endFrameNumber], 1, [1, 1, 1, 1]] remoteExec ["CBA_fnc_notify", [0, -2] select isDedicated]; +// Interim "saving..." toast; the final result comes from :MISSION:SAVED: +[format["OCAP saving %1 (%2 frames) — upload will follow", briefingName, _endFrameNumber], 1, [1, 1, 1, 1]] remoteExec ["CBA_fnc_notify", [0, -2] select isDedicated]; [[GVAR(missionName), GVAR(captureFrameNo)], { params ["_missionName", "_endFrame"]; From e0cfa1652c8462e31118eb2226c672dd9910b6d7 Mon Sep 17 00:00:00 2001 From: Florian Kinder Date: Sat, 11 Apr 2026 22:02:32 +0200 Subject: [PATCH 3/3] review(addon): type-validate :MISSION:SAVED: payload and defer final diary --- addons/extension/fnc_initSession.sqf | 35 +++++++++++++++++++++++++--- addons/recorder/fnc_exportData.sqf | 5 +++- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/addons/extension/fnc_initSession.sqf b/addons/extension/fnc_initSession.sqf index 8ffd600..793f841 100644 --- a/addons/extension/fnc_initSession.sqf +++ b/addons/extension/fnc_initSession.sqf @@ -160,9 +160,26 @@ addMissionEventHandler ["ExtensionCallback", { // ["ok", path] // ["partial", path, error] // ["error", error] - private _status = _data param [0, ""]; - private _detail = _data param [1, ""]; - private _extra = _data param [2, ""]; + // Type-check each field: if the extension ever sends something + // unexpected, fall back to an empty string rather than crashing + // the callback handler with a type error in the switch below. + private _status = _data param [0, "", [""]]; + private _detail = _data param [1, "", [""]]; + private _extra = _data param [2, "", [""]]; + + // finalDiary appends a final status entry to the OCAPInfo diary + // subject so the interim "being saved" record from fnc_exportData.sqf + // is followed by the authoritative outcome. + private _finalDiary = { + params ["_diaryHtml"]; + [[_diaryHtml], { + params ["_html"]; + player createDiaryRecord [ + "OCAPInfo", + ["Status", _html] + ]; + }] remoteExec ["call", [0, -2] select isDedicated, true]; + }; switch (_status) do { case "ok": { @@ -173,6 +190,10 @@ addMissionEventHandler ["ExtensionCallback", { 2, [0, 0.8, 0, 1] ] remoteExec ["CBA_fnc_notify", [0, -2] select isDedicated]; + [format[ + "OCAP capture of %1 has been exported and uploaded successfully.", + briefingName + ]] call _finalDiary; }; case "partial": { WARNING_2("Mission save complete but upload failed — path: %1 error: %2",_detail,_extra); @@ -182,6 +203,10 @@ addMissionEventHandler ["ExtensionCallback", { 2, [1, 0.8, 0, 1] ] remoteExec ["CBA_fnc_notify", [0, -2] select isDedicated]; + [format[ + "OCAP capture of %1 was saved locally to %2, but the upload to the web server failed:
%3", + briefingName, _detail, _extra + ]] call _finalDiary; }; case "error": { ERROR_MSG_1("Mission save failed: %1",_detail); @@ -191,6 +216,10 @@ addMissionEventHandler ["ExtensionCallback", { 2, [1, 0, 0, 1] ] remoteExec ["CBA_fnc_notify", [0, -2] select isDedicated]; + [format[ + "OCAP save of %1 failed:
%2", + briefingName, _detail + ]] call _finalDiary; }; default { WARNING_1("Unknown :MISSION:SAVED: status: %1",_status); diff --git a/addons/recorder/fnc_exportData.sqf b/addons/recorder/fnc_exportData.sqf index 1ebe1b7..c6f910c 100644 --- a/addons/recorder/fnc_exportData.sqf +++ b/addons/recorder/fnc_exportData.sqf @@ -134,6 +134,9 @@ OCAPEXTLOG(ARR4("Queued recording of mission",GVAR(missionName),"with tag",_save // Interim "saving..." toast; the final result comes from :MISSION:SAVED: [format["OCAP saving %1 (%2 frames) — upload will follow", briefingName, _endFrameNumber], 1, [1, 1, 1, 1]] remoteExec ["CBA_fnc_notify", [0, -2] select isDedicated]; +// Interim diary record. The final "exported successfully" / "upload failed" +// record is appended from the :MISSION:SAVED: ExtensionCallback handler in +// fnc_initSession.sqf once the async save completes. [[GVAR(missionName), GVAR(captureFrameNo)], { params ["_missionName", "_endFrame"]; @@ -146,7 +149,7 @@ OCAPEXTLOG(ARR4("Queued recording of mission",GVAR(missionName),"with tag",_save [ "Status", format[ - "OCAP capture of %1 has been exported with %2 frames saved.

Upload results have been logged.", + "OCAP capture of %1 with %2 frames is being saved and uploaded.

Final status will be reported when the upload completes.", _missionName, _endFrame ]