From 1994d60007d4106009b646c947c60f7fbea364cd Mon Sep 17 00:00:00 2001 From: Florian Kinder Date: Sun, 22 Feb 2026 19:23:47 +0100 Subject: [PATCH 1/3] feat: replace mine projectile events with placed object lifecycle Mines and explosives now get dedicated :NEW:PLACED: and :PLACED:EVENT: commands instead of being sent as :PROJECTILE: events. Placed objects receive unique OCAP IDs and track Explode/Deleted events for detonation and removal, avoiding the double-send issue with the projectile path. --- addons/recorder/fnc_eh_fired_client.sqf | 86 ++++++++++++++++++++----- addons/recorder/fnc_eh_fired_server.sqf | 14 ++++ 2 files changed, 84 insertions(+), 16 deletions(-) diff --git a/addons/recorder/fnc_eh_fired_client.sqf b/addons/recorder/fnc_eh_fired_client.sqf index f4faa75..55cd71a 100644 --- a/addons/recorder/fnc_eh_fired_client.sqf +++ b/addons/recorder/fnc_eh_fired_client.sqf @@ -109,25 +109,79 @@ private _data = [ _projectile setVariable [QGVARMAIN(projectileData), _data]; -// carryover variables to submunitions -if ((_data select 17) isEqualTo "shotSubmunitions") then { - _projectile addEventHandler ["SubmunitionCreated", { - params ["_projectile", "_submunitionProjectile"]; - private _data = +(_projectile getVariable QGVARMAIN(projectileData)); - _data set [17, getText(configOf _submunitionProjectile >> "simulation")]; // actual sim type - _data set [18, true]; // isSub = true - (_data select 14) pushBack [ - diag_tickTime, - EGVAR(recorder,captureFrameNo), - (getPosASL _submunitionProjectile) joinString "," +// Handle placed objects (mines, explosives) — separate lifecycle from projectiles +if (_weapon == "put") then { + // Assign unique OCAP ID to placed object (same counter as soldiers/vehicles) + private _placedId = GVAR(nextId); + GVAR(nextId) = GVAR(nextId) + 1; + _projectile setVariable [QGVARMAIN(placedId), _placedId]; + _projectile setVariable [QGVARMAIN(detonated), false]; + + // Build :NEW:PLACED: data + private _placedData = [ + EGVAR(recorder,captureFrameNo), // 0: captureFrameNo + _placedId, // 1: placedId + typeOf _projectile, // 2: className + _data select 11, // 3: displayName (magazineDisplay) + (getPosASL _projectile) joinString ",", // 4: position + _firerOcapId, // 5: firerOcapId + str (side group _firer), // 6: side + _weapon, // 7: weapon + _data select 19 // 8: magazineIcon + ]; + + [QGVARMAIN(handlePlacedData), [_placedData]] call CBA_fnc_serverEvent; + + // Attach simplified EHs for placed object lifecycle + _projectile addEventHandler ["Explode", { + params ["_projectile", "_pos", "_velocity"]; + if (_projectile getVariable [QGVARMAIN(detonated), true]) exitWith {}; + _projectile setVariable [QGVARMAIN(detonated), true]; + private _placedId = _projectile getVariable [QGVARMAIN(placedId), -1]; + private _eventData = [ + EGVAR(recorder,captureFrameNo), // 0: captureFrameNo + _placedId, // 1: placedId + "detonated", // 2: eventType + _pos joinString "," // 3: position ]; - _submunitionProjectile setVariable [QGVARMAIN(projectileData), _data]; - // add the rest of EHs to submunition - [_submunitionProjectile] call FUNC(eh_fired_clientBullet); + [QGVARMAIN(handlePlacedEvent), [_eventData]] call CBA_fnc_serverEvent; + }]; + + _projectile addEventHandler ["Deleted", { + params ["_projectile"]; + // Only send "deleted" if not already detonated (avoid double-send) + if (_projectile getVariable [QGVARMAIN(detonated), true]) exitWith {}; + _projectile setVariable [QGVARMAIN(detonated), true]; + private _placedId = _projectile getVariable [QGVARMAIN(placedId), -1]; + private _eventData = [ + EGVAR(recorder,captureFrameNo), // 0: captureFrameNo + _placedId, // 1: placedId + "deleted", // 2: eventType + (getPosASL _projectile) joinString "," // 3: position + ]; + [QGVARMAIN(handlePlacedEvent), [_eventData]] call CBA_fnc_serverEvent; }]; } else { - // add the rest of EHs to projectile - [_projectile] call FUNC(eh_fired_clientBullet); + // carryover variables to submunitions + if ((_data select 17) isEqualTo "shotSubmunitions") then { + _projectile addEventHandler ["SubmunitionCreated", { + params ["_projectile", "_submunitionProjectile"]; + private _data = +(_projectile getVariable QGVARMAIN(projectileData)); + _data set [17, getText(configOf _submunitionProjectile >> "simulation")]; // actual sim type + _data set [18, true]; // isSub = true + (_data select 14) pushBack [ + diag_tickTime, + EGVAR(recorder,captureFrameNo), + (getPosASL _submunitionProjectile) joinString "," + ]; + _submunitionProjectile setVariable [QGVARMAIN(projectileData), _data]; + // add the rest of EHs to submunition + [_submunitionProjectile] call FUNC(eh_fired_clientBullet); + }]; + } else { + // add the rest of EHs to projectile + [_projectile] call FUNC(eh_fired_clientBullet); + }; }; true; diff --git a/addons/recorder/fnc_eh_fired_server.sqf b/addons/recorder/fnc_eh_fired_server.sqf index c19505b..eee6538 100644 --- a/addons/recorder/fnc_eh_fired_server.sqf +++ b/addons/recorder/fnc_eh_fired_server.sqf @@ -130,3 +130,17 @@ [":PROJECTILE:", _this] call EFUNC(extension,sendData); }; }] call CBA_fnc_addEventHandler; + +// Handle placed object creation events (mines, explosives) +[QGVARMAIN(handlePlacedData), { + params ["_data"]; + TRACE_1("Sending placed object data to extension",_data); + [":NEW:PLACED:", _data] call EFUNC(extension,sendData); +}] call CBA_fnc_addEventHandler; + +// Handle placed object lifecycle events (detonation, deletion) +[QGVARMAIN(handlePlacedEvent), { + params ["_data"]; + TRACE_1("Sending placed event data to extension",_data); + [":PLACED:EVENT:", _data] call EFUNC(extension,sendData); +}] call CBA_fnc_addEventHandler; From 83220d5e436b9c04482387d1a8d6af06663e8a18 Mon Sep 17 00:00:00 2001 From: Florian Kinder Date: Sun, 22 Feb 2026 20:25:45 +0100 Subject: [PATCH 2/3] fix: assign placed object IDs on server where nextId exists The fired handler runs on the projectile owner (client/HC/server), but GVAR(nextId) is only initialized on the server. On clients it was undefined, producing "any" for placedId and cascading to -1 in Explode/Deleted event handlers. Move ID assignment to the server-side handlePlacedData CBA handler and broadcast the assigned ID back onto the projectile object. --- addons/recorder/fnc_eh_fired_client.sqf | 10 +++------- addons/recorder/fnc_eh_fired_server.sqf | 9 +++++++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/addons/recorder/fnc_eh_fired_client.sqf b/addons/recorder/fnc_eh_fired_client.sqf index 55cd71a..9cf7098 100644 --- a/addons/recorder/fnc_eh_fired_client.sqf +++ b/addons/recorder/fnc_eh_fired_client.sqf @@ -111,16 +111,12 @@ _projectile setVariable [QGVARMAIN(projectileData), _data]; // Handle placed objects (mines, explosives) — separate lifecycle from projectiles if (_weapon == "put") then { - // Assign unique OCAP ID to placed object (same counter as soldiers/vehicles) - private _placedId = GVAR(nextId); - GVAR(nextId) = GVAR(nextId) + 1; - _projectile setVariable [QGVARMAIN(placedId), _placedId]; _projectile setVariable [QGVARMAIN(detonated), false]; - // Build :NEW:PLACED: data + // Build :NEW:PLACED: data — placedId assigned server-side (GVAR(nextId) only exists there) private _placedData = [ EGVAR(recorder,captureFrameNo), // 0: captureFrameNo - _placedId, // 1: placedId + -1, // 1: placedId (assigned by server) typeOf _projectile, // 2: className _data select 11, // 3: displayName (magazineDisplay) (getPosASL _projectile) joinString ",", // 4: position @@ -130,7 +126,7 @@ if (_weapon == "put") then { _data select 19 // 8: magazineIcon ]; - [QGVARMAIN(handlePlacedData), [_placedData]] call CBA_fnc_serverEvent; + [QGVARMAIN(handlePlacedData), [_placedData, _projectile]] call CBA_fnc_serverEvent; // Attach simplified EHs for placed object lifecycle _projectile addEventHandler ["Explode", { diff --git a/addons/recorder/fnc_eh_fired_server.sqf b/addons/recorder/fnc_eh_fired_server.sqf index eee6538..b45727c 100644 --- a/addons/recorder/fnc_eh_fired_server.sqf +++ b/addons/recorder/fnc_eh_fired_server.sqf @@ -132,9 +132,14 @@ }] call CBA_fnc_addEventHandler; // Handle placed object creation events (mines, explosives) +// ID assignment happens here because GVAR(nextId) only exists on the server [QGVARMAIN(handlePlacedData), { - params ["_data"]; - TRACE_1("Sending placed object data to extension",_data); + params ["_data", "_projectile"]; + private _placedId = GVAR(nextId); + GVAR(nextId) = GVAR(nextId) + 1; + _data set [1, _placedId]; + _projectile setVariable [QGVARMAIN(placedId), _placedId, true]; + TRACE_2("Sending placed object data to extension",_placedId,_data); [":NEW:PLACED:", _data] call EFUNC(extension,sendData); }] call CBA_fnc_addEventHandler; From 503710aba2bcfc26fd38e9638806cd4de21236b0 Mon Sep 17 00:00:00 2001 From: Florian Kinder Date: Sun, 22 Feb 2026 22:44:10 +0100 Subject: [PATCH 3/3] feat: add HitExplosion tracking to placed objects Record which entities are hit by mine/explosive detonations. Sends a "hit" placed event per victim, filtered by _hitThings to avoid false positives from near-misses. --- addons/recorder/fnc_eh_fired_client.sqf | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/addons/recorder/fnc_eh_fired_client.sqf b/addons/recorder/fnc_eh_fired_client.sqf index 9cf7098..46fee20 100644 --- a/addons/recorder/fnc_eh_fired_client.sqf +++ b/addons/recorder/fnc_eh_fired_client.sqf @@ -129,6 +129,23 @@ if (_weapon == "put") then { [QGVARMAIN(handlePlacedData), [_placedData, _projectile]] call CBA_fnc_serverEvent; // Attach simplified EHs for placed object lifecycle + _projectile addEventHandler ["HitExplosion", { + params ["_projectile", "_hitEntity", "_projectileOwner", "_hitThings"]; + if (isNull _hitEntity) exitWith {}; + if (count _hitThings isEqualTo 0) exitWith {}; + private _hitOcapId = _hitEntity getVariable [QGVARMAIN(id), -1]; + if (_hitOcapId isEqualTo -1) exitWith {}; + private _placedId = _projectile getVariable [QGVARMAIN(placedId), -1]; + private _eventData = [ + EGVAR(recorder,captureFrameNo), // 0: captureFrameNo + _placedId, // 1: placedId + "hit", // 2: eventType + (getPosASL _hitEntity) joinString ",", // 3: position (victim pos) + _hitOcapId // 4: hitEntityOcapId + ]; + [QGVARMAIN(handlePlacedEvent), [_eventData]] call CBA_fnc_serverEvent; + }]; + _projectile addEventHandler ["Explode", { params ["_projectile", "_pos", "_velocity"]; if (_projectile getVariable [QGVARMAIN(detonated), true]) exitWith {};