Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions addons/recorder/XEH_preInit.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,34 @@ GVAR(allSettings) = [
false // requires restart to apply
],

/*
CBA Setting: OCAP_settings_excludeMarkerTypeFromRecord
Description:
Array of marker type classnames. Any marker whose type matches one of these is excluded from recording. Use single quotes! Default: []

Setting Name:
Marker Types To Exclude

Value Type:
Stringified Array

Example:
> "['Empty']"
*/
[
QEGVAR(settings,excludeMarkerTypeFromRecord),
"EDITBOX", // setting type
[
"Marker Types to Exclude", // Pretty name shown inside the ingame settings menu. Can be stringtable entry.
"Array of marker type classnames. Any marker with one of these types is excluded from recording. Use single quotes! Default: []"
],
[COMPONENT_NAME, "Exclusions"], // Pretty name of the category where the setting can be found. Can be stringtable entry.
"[]", // default string value
true, // "_isGlobal" flag. Set this to true to always have this setting synchronized between all clients in multiplayer
{}, // function that will be executed once on mission start and every time the setting is changed.
false // requires restart to apply
],

/*
CBA Setting: OCAP_settings_excludeVarNameFromRecord
Description:
Expand Down
30 changes: 29 additions & 1 deletion addons/recorder/fnc_handleMarkers.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Description:

Due to the nature of locality and single-view playback, markers of the same name which exist in different states on different clients may display odd behavior during playback.

Marker exclusions as configured in <OCAP_settings_excludeMarkerFromRecord> are handled client-side to avoid unnecessary network traffic.
Marker exclusions as configured in <OCAP_settings_excludeMarkerFromRecord> and <OCAP_settings_excludeMarkerTypeFromRecord> are handled client-side to avoid unnecessary network traffic.

This will also wait until the mission proceeds past the briefing screen, then gather all existing markers and send them to the server for entry onto the Editor/Briefing Markers layer during playback.

Expand Down Expand Up @@ -41,6 +41,14 @@ Author:
GVAR(trackedMarkers) = []; // Markers which we save into replay
GVAR(trackedMarkerStates) = createHashMap;

// VARIABLE: OCAP_recorder_excludeMarkerTypeList
// Parsed list of marker type classnames excluded from recording, as configured in <OCAP_settings_excludeMarkerTypeFromRecord>.
GVAR(excludeMarkerTypeList) = if (!isNil QEGVAR(settings,excludeMarkerTypeFromRecord)) then {
parseSimpleArray EGVAR(settings,excludeMarkerTypeFromRecord)
} else {
[]
};

// VARIABLE: OCAP_listener_markers
// Contains handle for <OCAP_handleMarker> CBA event handler.

Expand All @@ -66,6 +74,14 @@ EGVAR(listener,markers) = [QGVARMAIN(handleMarker), {
};
};

// A marker whose type is excluded (for example "Empty" zeus location markers)
// is not recorded, so no delete event for it needs to be sent either.
if (_type in GVAR(excludeMarkerTypeList)) exitWith {
if (GVARMAIN(isDebug)) then {
OCAPEXTLOG(ARR4("MARKER:CREATE: Marker",_mrk_name,"excluded by type",_type));
};
};

if (GVARMAIN(isDebug)) then {
format["CREATE:MARKER: Valid CREATED process of %1, sending to extension", _mrk_name] SYSCHAT;
OCAPEXTLOG(ARR3("CREATE:MARKER: Valid CREATED process of",_mrk_name,", sending to extension"));
Expand Down Expand Up @@ -204,6 +220,10 @@ EGVAR(listener,markers) = [QGVARMAIN(handleMarker), {
_isExcluded = true;
};
} forEach GVAR(excludeMarkerList);
// check for an excluded marker type, e.g. the "Empty" type of zeus location markers
if ((markerType _marker) in GVAR(excludeMarkerTypeList)) then {
_isExcluded = true;
};
if (_isExcluded) exitWith {};

private _event = _this;
Expand Down Expand Up @@ -255,6 +275,10 @@ EGVAR(listener,markers) = [QGVARMAIN(handleMarker), {
_isExcluded = true;
};
} forEach GVAR(excludeMarkerList);
// check for an excluded marker type, e.g. the "Empty" type of zeus location markers
if ((markerType _marker) in GVAR(excludeMarkerTypeList)) then {
_isExcluded = true;
};
if (_isExcluded) exitWith {};

private _pos = ATLToASL (markerPos [_marker, true]);
Expand Down Expand Up @@ -327,6 +351,10 @@ EGVAR(listener,markers) = [QGVARMAIN(handleMarker), {
_isExcluded = true;
};
} forEach GVAR(excludeMarkerList);
// check for an excluded marker type, e.g. the "Empty" type of zeus location markers
if ((markerType _marker) in GVAR(excludeMarkerTypeList)) then {
_isExcluded = true;
};
if (_isExcluded) then {continue};


Expand Down
6 changes: 6 additions & 0 deletions addons/recorder/fnc_init.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ GVAR(excludeMarkerList) = if (!isNil QEGVAR(settings,excludeMarkerFromRecord)) t
[]
};

GVAR(excludeMarkerTypeList) = if (!isNil QEGVAR(settings,excludeMarkerTypeFromRecord)) then {
parseSimpleArray EGVAR(settings,excludeMarkerTypeFromRecord)
} else {
[]
};

// Lowercased once here so lookups in the capture loop can be case-insensitive,
// matching how class/kind exclusions behave.
GVAR(excludeVarNameList) = if (!isNil QEGVAR(settings,excludeVarNameFromRecord)) then {
Expand Down
40 changes: 40 additions & 0 deletions tests/recorder_regressions.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,46 @@ try {
(OCAP_test_wait select 2) call (OCAP_test_wait select 1);
(OCAP_test_wait select 2) call (OCAP_test_wait select 4);
[OCAP_test_commands isEqualTo [], "export then start with reused ids emits no fictitious servicing, disassembly or queued assembly"] call _assert;

// Marker type exclusion (OCAP2/OCAP#99): a marker of an excluded type is
// never recorded; a marker of another type still is. The type list is read
// at event time, so assigning it directly is enough.
OCAP_recorder_excludeMarkerTypeList = ["Empty"];
OCAP_test_marker_commands = [];
private _origMarkerSend = OCAP_extension_fnc_sendData;
OCAP_extension_fnc_sendData = {
params ["_command", ["_args", []]];
private _result = call _origMarkerSend;
if (_command in [":MARKER:CREATE:", ":MARKER:DELETE:"]) then {
OCAP_test_marker_commands pushBack +_this;
};
_result
};
createMarker ["Empty", [500, 500, 0], "mk_type_excluded"];
createMarker ["mil_dot", [510, 510, 0], "mk_type_kept"];
// Marker creations are forwarded after a two second delay.
private _markerDeadline = diag_tickTime + 15;
waitUntil {
sleep 0.1;
({"mk_type_kept" in str _x} count OCAP_test_marker_commands) > 0 || {diag_tickTime > _markerDeadline};
};
[({"mk_type_kept" in str _x} count OCAP_test_marker_commands) == 1, "marker of a non-excluded type is recorded"] call _assert;
[({"mk_type_excluded" in str _x} count OCAP_test_marker_commands) == 0, "marker of an excluded type is not recorded"] call _assert;
[("mk_type_kept" in OCAP_recorder_trackedMarkers) && {!("mk_type_excluded" in OCAP_recorder_trackedMarkers)}, "tracked marker list reflects the type exclusion"] call _assert;
// Deletions carry no type information; the server drops deletions of markers
// it never tracked, so both deletions are accepted as no-ops where needed.
deleteMarker "mk_type_kept";
deleteMarker "mk_type_excluded";
_markerDeadline = diag_tickTime + 5;
waitUntil {
sleep 0.05;
({"mk_type_kept" in str _x} count OCAP_test_marker_commands) > 1 || {diag_tickTime > _markerDeadline};
};
[({"mk_type_kept" in str _x} count OCAP_test_marker_commands) == 2, "deletion of a tracked marker is recorded"] call _assert;
[!("mk_type_excluded" in OCAP_recorder_trackedMarkers), "deleted excluded marker stays untracked"] call _assert;
OCAP_extension_fnc_sendData = _origMarkerSend;
OCAP_recorder_excludeMarkerTypeList = [];
OCAP_test_marker_commands = [];
} catch {
_error = _exception;
};
Expand Down
Loading