From a4df237ee123d15111ebec9ef0256a55553ee275 Mon Sep 17 00:00:00 2001 From: thegamecracks <61257169+thegamecracks@users.noreply.github.com> Date: Mon, 3 Aug 2026 11:35:25 -0400 Subject: [PATCH 01/10] fix: register fired handlers on all clients simultaneously Clients sometimes didn't receive their FiredMan / HandleDamage event handlers when joining-in-progress or respawning. This addresses what I suspect to be a race condition with the CBA class event handler not tracking projectiles from players. This is my theory of the order of operations when a client joins: 1. Player unit spawns on server locality 2. Player unit transfers locality to client 3. CBA fires the class EH If (2) and (3) were switched around, the class EH may see the player unit as local before it's transferred, and would be unable to remote execute fired handlers on the client. After locality is transferred, the Local EH removes the server's EHs from the unit, but does not tell the client to set up its own fired handlers. As a result, the player unit is left with no EH to track their projectiles. If my theory is correct, a solution here is to make every client set up the same handlers on every unit. FiredMan and HandleDamage have locality documented here: - https://community.bistudio.com/wiki/Arma_3:_Event_Handlers#FiredMan - https://community.bistudio.com/wiki/Arma_3:_Event_Handlers#HandleDamage For FiredMan, the event can sometimes trigger on remote units if within proximity to the player's camera, but otherwise only fires reliably where the unit is local. For HandleDamage, the event handler should never on remote units. In both events, I added guard conditions to both out of caution to ensure the EHs only trigger on the respective client. An alternative solution could be to keep the original server-side class EH, but tweak Local EH to remote execute adding/removing handlers whenever locality shifts. However, the Local EH is expected not fire on the server when locality transfers between two clients, such as when the group leader changes: https://community.bistudio.com/wiki/Arma_3:_Event_Handlers#Local_(Entity) > The event handler only triggers on the computers that are directly involved > in change of locality. So if EH is added to every computer on network, > it will only trigger on 2 computers, on the computer that receives ownership > of the object ... and on the computer from which ownership is transferred ... As such, I believe it is more reliable to have all clients register their event handlers and let it fire on whomever the unit is local to. This theory does have one contradiction from a separate wiki page: https://community.bistudio.com/wiki/Arma_3:_Mission_Event_Handlers#OnUserSelectedPlayer > [OnUserSelectedPlayer] is the earliest the player object is known when > player joins the server, but it is not local to the user yet, so there > is a wait time depending on network connection. When player respawns, > **the unit created on the client** and so it might take a while before > server has valid player object. Why was the EH registration flaky even when respawning repeatedly, if the unit is supposedly created on the client to begin with? I don't have an answer for this... --- addons/recorder/fnc_eh_fired_server.sqf | 93 +++++-------------------- 1 file changed, 16 insertions(+), 77 deletions(-) diff --git a/addons/recorder/fnc_eh_fired_server.sqf b/addons/recorder/fnc_eh_fired_server.sqf index ef28eb2..32e3300 100644 --- a/addons/recorder/fnc_eh_fired_server.sqf +++ b/addons/recorder/fnc_eh_fired_server.sqf @@ -28,16 +28,18 @@ GVAR(trackedProjectiles) = createHashMap; GVAR(trackedPlacedObjects) = createHashMap; -// Now we'll do the server setup. -// Wrap everything in a CBA Class Event Handler so when the server initializes any soldier, it'll set up the Local EH. The Local EH is global (ironically) when applied to a unit so it'll do what we need across the entire session and trigger the relevant machines on locality change. -["CAManBase", "init", { - params ["_entity"]; +// Wrap everything in a CBA Class Event Handler and broadcast it so when any soldier is initialized, all clients will set up EHs on them. This avoids the need to track locality changes from server-to-client or client-to-client (e.g. group leader changes). +{ + ["CAManBase", "init", { + params ["_entity"]; + + // Local entities should always have event handlers added directly — remoteExec to owner 0 + // (not-yet-networked entities) causes the object reference to deserialize as null. - // When object is inited, add the EH to the owner machine. - // For local entities (server-owned AI), add directly — remoteExec to owner 0 - // (not-yet-networked entities) causes the object reference to deserialize as null. - if (local _entity) then { private _id = _entity addEventHandler ["FiredMan", { + // FiredMan can sometimes fire on remote units which is not desired here. All clients have their own EHs so we don't need to handle remote units. + params ["_unit"]; + if (!local _unit) exitWith {}; private _start = diag_tickTime; _this call FUNC(eh_fired_client); TRACE_1("Ran fired handler",diag_tickTime - _start); @@ -47,83 +49,20 @@ GVAR(trackedPlacedObjects) = createHashMap; // HandleDamage stores the ammo classname on the victim for kill attribution private _hdId = _entity addEventHandler ["HandleDamage", { + // HandleDamage should never fire on remote units, but we'll check anyway to avoid double-send. params ["_unit", "", "", "", "_projectile"]; + if (!local _unit) exitWith {}; if (_projectile isNotEqualTo "" && {_projectile isNotEqualTo (_unit getVariable [QGVARMAIN(lastDamageAmmo), ""])}) then { _unit setVariable [QGVARMAIN(lastDamageAmmo), _projectile, 2]; }; }]; _entity setVariable [QGVARMAIN(handleDamageEHExists), true]; _entity setVariable [QGVARMAIN(handleDamageEH), _hdId]; - } else { - [_entity, { - private _id = _this addEventHandler ["FiredMan", { - private _start = diag_tickTime; - _this call FUNC(eh_fired_client); - TRACE_1("Ran fired handler",diag_tickTime - _start); - }]; - _this setVariable [QGVARMAIN(firedManEHExists), true]; - _this setVariable [QGVARMAIN(firedManEH), _id]; - - private _hdId = _this addEventHandler ["HandleDamage", { - params ["_unit", "", "", "", "_projectile"]; - if (_projectile isNotEqualTo "" && {_projectile isNotEqualTo (_unit getVariable [QGVARMAIN(lastDamageAmmo), ""])}) then { - _unit setVariable [QGVARMAIN(lastDamageAmmo), _projectile, 2]; - }; - }]; - _this setVariable [QGVARMAIN(handleDamageEHExists), true]; - _this setVariable [QGVARMAIN(handleDamageEH), _hdId]; - }] remoteExec ["call", owner _entity]; - }; - - - // Again, we will add a single Local EH for the unit on the server, but it has global effect so this is sufficient. - _entity addEventHandler ["Local", { - // This code will be run on both the machine giving up ownership and the machine receiving ownership. - params ["_entity", "_isLocal"]; - - // If the unit is NO LONGER local, remove the EH and the CBA EH. - // We need to see if it exists already. - private _firedManEHExists = _entity getVariable [QGVARMAIN(firedManEHExists), false]; - private _handleDamageEHExists = _entity getVariable [QGVARMAIN(handleDamageEHExists), false]; - - // If the unit is NO LONGER local, and the EH exists, remove it. - if (!_isLocal && _firedManEHExists) then { - _entity removeEventHandler ["FiredMan", _entity getVariable QGVARMAIN(firedManEH)]; - _entity setVariable [QGVARMAIN(firedManEHExists), false]; - _entity setVariable [QGVARMAIN(firedManEH), nil]; - }; - if (!_isLocal && _handleDamageEHExists) then { - _entity removeEventHandler ["HandleDamage", _entity getVariable QGVARMAIN(handleDamageEH)]; - _entity setVariable [QGVARMAIN(handleDamageEHExists), false]; - _entity setVariable [QGVARMAIN(handleDamageEH), nil]; - }; - - // If the unit is NOW local and the EH doesn't exist, add it. - if (_isLocal && !_firedManEHExists) then { - private _id = _entity addEventHandler ["FiredMan", { - TRACE_2("FiredMan EH fired",clientOwner,_this); - private _start = diag_tickTime; - _this call FUNC(eh_fired_client); - TRACE_1("Ran fired handler",diag_tickTime - _start); - }]; - _entity setVariable [QGVARMAIN(firedManEHExists), true]; - _entity setVariable [QGVARMAIN(firedManEH), _id]; - }; - if (_isLocal && !_handleDamageEHExists) then { - private _hdId = _entity addEventHandler ["HandleDamage", { - params ["_unit", "", "", "", "_projectile"]; - if (_projectile isNotEqualTo "" && {_projectile isNotEqualTo (_unit getVariable [QGVARMAIN(lastDamageAmmo), ""])}) then { - _unit setVariable [QGVARMAIN(lastDamageAmmo), _projectile, 2]; - }; - }]; - _entity setVariable [QGVARMAIN(handleDamageEHExists), true]; - _entity setVariable [QGVARMAIN(handleDamageEH), _hdId]; - }; - }]; -// for the class event handler, -// allow inheritance, don't exclude anything, and apply retroactively -}, true, [], true] call CBA_fnc_addClassEventHandler; + // for the class event handler, + // allow inheritance, don't exclude anything, and apply retroactively + }, true, [], true] call CBA_fnc_addClassEventHandler; +} remoteExec ["call", 0, true]; // Finally, we'll add a CBA Event Handler to take in the pre-processed fired data here on the server and send it to the extension. From 79b2de6fb7dd4ee313a2fbdb7b1f6b2647454546 Mon Sep 17 00:00:00 2001 From: thegamecracks <61257169+thegamecracks@users.noreply.github.com> Date: Mon, 3 Aug 2026 13:40:21 -0400 Subject: [PATCH 02/10] fix: remove CBA dependency for adding fired handlers --- addons/recorder/fnc_eh_fired_server.sqf | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/addons/recorder/fnc_eh_fired_server.sqf b/addons/recorder/fnc_eh_fired_server.sqf index 32e3300..63d2d78 100644 --- a/addons/recorder/fnc_eh_fired_server.sqf +++ b/addons/recorder/fnc_eh_fired_server.sqf @@ -28,9 +28,9 @@ GVAR(trackedProjectiles) = createHashMap; GVAR(trackedPlacedObjects) = createHashMap; -// Wrap everything in a CBA Class Event Handler and broadcast it so when any soldier is initialized, all clients will set up EHs on them. This avoids the need to track locality changes from server-to-client or client-to-client (e.g. group leader changes). +// Globally broadcast and initialize fired events for all units and future units. This avoids the need to track locality changes from server-to-client or client-to-client (e.g. group leader changes). { - ["CAManBase", "init", { + private _fnc_addHandlers = { params ["_entity"]; // Local entities should always have event handlers added directly — remoteExec to owner 0 @@ -58,10 +58,19 @@ GVAR(trackedPlacedObjects) = createHashMap; }]; _entity setVariable [QGVARMAIN(handleDamageEHExists), true]; _entity setVariable [QGVARMAIN(handleDamageEH), _hdId]; + }; + + // Initialize on existing units first, then handle all subsequent units. + {_x call _fnc_addHandlers} forEach allUnits; + addMissionEventHandler ["EntityCreated", { + params ["_entity"]; + _thisArgs params ["_fnc_addHandlers"]; + + if (_entity isKindOf "CAManBase") then { + _entity call _fnc_addHandlers; + }; + }, [_fnc_addHandlers]]; - // for the class event handler, - // allow inheritance, don't exclude anything, and apply retroactively - }, true, [], true] call CBA_fnc_addClassEventHandler; } remoteExec ["call", 0, true]; From f363a03817bd60168fd90831dae22b6a5c807041 Mon Sep 17 00:00:00 2001 From: thegamecracks <61257169+thegamecracks@users.noreply.github.com> Date: Mon, 3 Aug 2026 13:43:54 -0400 Subject: [PATCH 03/10] fix: skip FiredMan EH for clients without CBA --- addons/recorder/fnc_eh_fired_server.sqf | 27 ++++++++++++++++--------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/addons/recorder/fnc_eh_fired_server.sqf b/addons/recorder/fnc_eh_fired_server.sqf index 63d2d78..241b058 100644 --- a/addons/recorder/fnc_eh_fired_server.sqf +++ b/addons/recorder/fnc_eh_fired_server.sqf @@ -36,16 +36,19 @@ GVAR(trackedPlacedObjects) = createHashMap; // Local entities should always have event handlers added directly — remoteExec to owner 0 // (not-yet-networked entities) causes the object reference to deserialize as null. - private _id = _entity addEventHandler ["FiredMan", { - // FiredMan can sometimes fire on remote units which is not desired here. All clients have their own EHs so we don't need to handle remote units. - params ["_unit"]; - if (!local _unit) exitWith {}; - private _start = diag_tickTime; - _this call FUNC(eh_fired_client); - TRACE_1("Ran fired handler",diag_tickTime - _start); - }]; - _entity setVariable [QGVARMAIN(firedManEHExists), true]; - _entity setVariable [QGVARMAIN(firedManEH), _id]; + // eh_fired_client depends on CBA for sending projectile events back to the server. If a client doesn't have CBA, this FiredMan EH won't work. + if (isClass (configFile >> "CfgPatches" >> "cba_xeh")) then { + private _id = _entity addEventHandler ["FiredMan", { + // FiredMan can sometimes fire on remote units which is not desired here. All clients have their own EHs so we don't need to handle remote units. + params ["_unit"]; + if (!local _unit) exitWith {}; + private _start = diag_tickTime; + _this call FUNC(eh_fired_client); + TRACE_1("Ran fired handler",diag_tickTime - _start); + }]; + _entity setVariable [QGVARMAIN(firedManEHExists), true]; + _entity setVariable [QGVARMAIN(firedManEH), _id]; + }; // HandleDamage stores the ammo classname on the victim for kill attribution private _hdId = _entity addEventHandler ["HandleDamage", { @@ -60,6 +63,10 @@ GVAR(trackedPlacedObjects) = createHashMap; _entity setVariable [QGVARMAIN(handleDamageEH), _hdId]; }; + if (!isClass (configFile >> "CfgPatches" >> "cba_xeh")) exitWith { + WARNING("CBA is not loaded. Your projectiles will not be tracked in recordings!"); + }; + // Initialize on existing units first, then handle all subsequent units. {_x call _fnc_addHandlers} forEach allUnits; addMissionEventHandler ["EntityCreated", { From 6703557c5fee980dba232d2696d1274c5139a296 Mon Sep 17 00:00:00 2001 From: thegamecracks <61257169+thegamecracks@users.noreply.github.com> Date: Mon, 3 Aug 2026 13:48:25 -0400 Subject: [PATCH 04/10] docs: update design comment for fired events --- addons/recorder/fnc_eh_fired_server.sqf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/recorder/fnc_eh_fired_server.sqf b/addons/recorder/fnc_eh_fired_server.sqf index 241b058..8826b31 100644 --- a/addons/recorder/fnc_eh_fired_server.sqf +++ b/addons/recorder/fnc_eh_fired_server.sqf @@ -4,8 +4,8 @@ // First, we'll remoteExec three functions to all clients: // eh_fired_client, eh_fired_clientBullet, and eh_fired_clientProjectile. -// We'll use the Local EH to detect changes of unit locality. Add the EH for the soldier unit on the new owner, and remove it on the old. This EH only triggers on two machines so it limits the overall impact of doing so and validates duplicate records are not sent to the server. -// https://community.bistudio.com/wiki/Arma_3:_Event_Handlers#Local +// Afterwards, we'll ask every client to register event handlers on all units + future units with the EntityCreated mission EH. Object EHs like FiredMan and HandleDamage generally fire where the unit is local, which limits its overall performance impact and validates duplicate records are not sent to the server. +// https://community.bistudio.com/wiki/Arma_3:_Mission_Event_Handlers#EntityCreated From 94007fb177639dc98698169bc8d65f9fd6c11093 Mon Sep 17 00:00:00 2001 From: thegamecracks <61257169+thegamecracks@users.noreply.github.com> Date: Mon, 3 Aug 2026 13:49:23 -0400 Subject: [PATCH 05/10] docs: shift locality comments closer to locality guards --- addons/recorder/fnc_eh_fired_server.sqf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/recorder/fnc_eh_fired_server.sqf b/addons/recorder/fnc_eh_fired_server.sqf index 8826b31..7ff4100 100644 --- a/addons/recorder/fnc_eh_fired_server.sqf +++ b/addons/recorder/fnc_eh_fired_server.sqf @@ -39,8 +39,8 @@ GVAR(trackedPlacedObjects) = createHashMap; // eh_fired_client depends on CBA for sending projectile events back to the server. If a client doesn't have CBA, this FiredMan EH won't work. if (isClass (configFile >> "CfgPatches" >> "cba_xeh")) then { private _id = _entity addEventHandler ["FiredMan", { - // FiredMan can sometimes fire on remote units which is not desired here. All clients have their own EHs so we don't need to handle remote units. params ["_unit"]; + // FiredMan can sometimes fire on remote units which is not desired here. All clients have their own EHs so we don't need to handle remote units. if (!local _unit) exitWith {}; private _start = diag_tickTime; _this call FUNC(eh_fired_client); @@ -52,8 +52,8 @@ GVAR(trackedPlacedObjects) = createHashMap; // HandleDamage stores the ammo classname on the victim for kill attribution private _hdId = _entity addEventHandler ["HandleDamage", { - // HandleDamage should never fire on remote units, but we'll check anyway to avoid double-send. params ["_unit", "", "", "", "_projectile"]; + // HandleDamage should never fire on remote units, but we'll check anyway to avoid double-send. if (!local _unit) exitWith {}; if (_projectile isNotEqualTo "" && {_projectile isNotEqualTo (_unit getVariable [QGVARMAIN(lastDamageAmmo), ""])}) then { _unit setVariable [QGVARMAIN(lastDamageAmmo), _projectile, 2]; From 38b030a6405b9b95edf64488659e461d1d4dfc40 Mon Sep 17 00:00:00 2001 From: thegamecracks <61257169+thegamecracks@users.noreply.github.com> Date: Mon, 3 Aug 2026 14:06:47 -0400 Subject: [PATCH 06/10] fix: bump requiredVersion to 2.10 This is in line with README.md which already documents the required version as 2.10. Given that Arma is already at 2.20, maybe it should be bumped to that? --- addons/extension/config.cpp | 2 +- addons/main/config.cpp | 2 +- addons/recorder/config.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/extension/config.cpp b/addons/extension/config.cpp index 6090adc..196efcc 100644 --- a/addons/extension/config.cpp +++ b/addons/extension/config.cpp @@ -5,7 +5,7 @@ class CfgPatches { class ADDON { - requiredVersion = 2.06; + requiredVersion = 2.10; name = COMPONENT_NAME; author = "Dell, Zealot, Kurt, IndigoFox, Fank"; authors[] = {"Dell", "Zealot", "Kurt", "IndigoFox", "Fank"}; diff --git a/addons/main/config.cpp b/addons/main/config.cpp index 290a473..6a3b9fe 100644 --- a/addons/main/config.cpp +++ b/addons/main/config.cpp @@ -5,7 +5,7 @@ class CfgPatches { class ADDON { - requiredVersion = 2.06; + requiredVersion = 2.10; name = COMPONENT_NAME; author = "Dell, Zealot, Kurt, IndigoFox, Fank"; authors[] = {"Dell", "Zealot", "Kurt", "IndigoFox", "Fank"}; diff --git a/addons/recorder/config.cpp b/addons/recorder/config.cpp index cb52534..f02a2f4 100644 --- a/addons/recorder/config.cpp +++ b/addons/recorder/config.cpp @@ -5,7 +5,7 @@ class CfgPatches { class ADDON { - requiredVersion = 2.06; + requiredVersion = 2.10; name = COMPONENT_NAME; author = "Dell, Zealot, Kurt, IndigoFox, Fank"; authors[] = {"Dell", "Zealot", "Kurt", "IndigoFox", "Fank"}; From 8f28e71c45e8e8949751beb4a49fc5840cedc6b6 Mon Sep 17 00:00:00 2001 From: thegamecracks <61257169+thegamecracks@users.noreply.github.com> Date: Mon, 3 Aug 2026 15:49:17 -0400 Subject: [PATCH 07/10] fix: whoops, don't exit early if CBA guard fails --- addons/recorder/fnc_eh_fired_server.sqf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/recorder/fnc_eh_fired_server.sqf b/addons/recorder/fnc_eh_fired_server.sqf index 7ff4100..2bb85ab 100644 --- a/addons/recorder/fnc_eh_fired_server.sqf +++ b/addons/recorder/fnc_eh_fired_server.sqf @@ -63,7 +63,7 @@ GVAR(trackedPlacedObjects) = createHashMap; _entity setVariable [QGVARMAIN(handleDamageEH), _hdId]; }; - if (!isClass (configFile >> "CfgPatches" >> "cba_xeh")) exitWith { + if (!isClass (configFile >> "CfgPatches" >> "cba_xeh")) then { WARNING("CBA is not loaded. Your projectiles will not be tracked in recordings!"); }; From bf390c51f6feb6a2c5e2a94cdc0e282ef2bda7f5 Mon Sep 17 00:00:00 2001 From: thegamecracks <61257169+thegamecracks@users.noreply.github.com> Date: Mon, 3 Aug 2026 16:06:23 -0400 Subject: [PATCH 08/10] docs: adjust note about deserializing non-network objects --- addons/recorder/fnc_eh_fired_server.sqf | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/addons/recorder/fnc_eh_fired_server.sqf b/addons/recorder/fnc_eh_fired_server.sqf index 2bb85ab..5275511 100644 --- a/addons/recorder/fnc_eh_fired_server.sqf +++ b/addons/recorder/fnc_eh_fired_server.sqf @@ -29,12 +29,11 @@ GVAR(trackedProjectiles) = createHashMap; GVAR(trackedPlacedObjects) = createHashMap; // Globally broadcast and initialize fired events for all units and future units. This avoids the need to track locality changes from server-to-client or client-to-client (e.g. group leader changes). +// Note that handlers are added on each executing machine directly. The old remoteExec-to-owner path was removed because not-yet-networked entities (owner 0) deserialize as objNull. { private _fnc_addHandlers = { params ["_entity"]; - // Local entities should always have event handlers added directly — remoteExec to owner 0 - // (not-yet-networked entities) causes the object reference to deserialize as null. // eh_fired_client depends on CBA for sending projectile events back to the server. If a client doesn't have CBA, this FiredMan EH won't work. if (isClass (configFile >> "CfgPatches" >> "cba_xeh")) then { From 1046f6d4fa8a34df84ec733be3ee2d71eb136c4f Mon Sep 17 00:00:00 2001 From: thegamecracks <61257169+thegamecracks@users.noreply.github.com> Date: Mon, 3 Aug 2026 17:47:29 -0400 Subject: [PATCH 09/10] fix: prevent initializing duplicate fired handlers EntityCreated fires on respawning units which preserve their event handlers. Adding the handler without checking would cause projectile events to incorrectly fire multiple times. This makes two changes: 1. Check for existing handlers before adding them 2. Wait one frame before adding handlers on remote units The second point is to address this note in the wiki: https://community.bistudio.com/wiki/Arma_3:_Mission_Event_Handlers#EntityCreated > This event is called before variable namespace is copied to remote respawning entity, > keep this in mind so your setVariables are not overwritten in the next frame. > EntityRespawned always fires after variable namespace is copied to new entity > regardless of locality. --- addons/recorder/fnc_eh_fired_server.sqf | 36 ++++++++++++++++--------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/addons/recorder/fnc_eh_fired_server.sqf b/addons/recorder/fnc_eh_fired_server.sqf index 5275511..479d78a 100644 --- a/addons/recorder/fnc_eh_fired_server.sqf +++ b/addons/recorder/fnc_eh_fired_server.sqf @@ -34,9 +34,12 @@ GVAR(trackedPlacedObjects) = createHashMap; private _fnc_addHandlers = { params ["_entity"]; - + // Event handlers are preserved across respawning units, so we must check to avoid duplicate handlers. // eh_fired_client depends on CBA for sending projectile events back to the server. If a client doesn't have CBA, this FiredMan EH won't work. - if (isClass (configFile >> "CfgPatches" >> "cba_xeh")) then { + if ( + isClass (configFile >> "CfgPatches" >> "cba_xeh") && + isNil {_entity getVariable QGVARMAIN(firedManEHExists)} + ) then { private _id = _entity addEventHandler ["FiredMan", { params ["_unit"]; // FiredMan can sometimes fire on remote units which is not desired here. All clients have their own EHs so we don't need to handle remote units. @@ -50,16 +53,18 @@ GVAR(trackedPlacedObjects) = createHashMap; }; // HandleDamage stores the ammo classname on the victim for kill attribution - private _hdId = _entity addEventHandler ["HandleDamage", { - params ["_unit", "", "", "", "_projectile"]; - // HandleDamage should never fire on remote units, but we'll check anyway to avoid double-send. - if (!local _unit) exitWith {}; - if (_projectile isNotEqualTo "" && {_projectile isNotEqualTo (_unit getVariable [QGVARMAIN(lastDamageAmmo), ""])}) then { - _unit setVariable [QGVARMAIN(lastDamageAmmo), _projectile, 2]; - }; - }]; - _entity setVariable [QGVARMAIN(handleDamageEHExists), true]; - _entity setVariable [QGVARMAIN(handleDamageEH), _hdId]; + if (isNil {_entity getVariable QGVARMAIN(handleDamageEHExists)}) then { + private _hdId = _entity addEventHandler ["HandleDamage", { + params ["_unit", "", "", "", "_projectile"]; + // HandleDamage should never fire on remote units, but we'll check anyway to avoid double-send. + if (!local _unit) exitWith {}; + if (_projectile isNotEqualTo "" && {_projectile isNotEqualTo (_unit getVariable [QGVARMAIN(lastDamageAmmo), ""])}) then { + _unit setVariable [QGVARMAIN(lastDamageAmmo), _projectile, 2]; + }; + }]; + _entity setVariable [QGVARMAIN(handleDamageEHExists), true]; + _entity setVariable [QGVARMAIN(handleDamageEH), _hdId]; + }; }; if (!isClass (configFile >> "CfgPatches" >> "cba_xeh")) then { @@ -70,9 +75,14 @@ GVAR(trackedPlacedObjects) = createHashMap; {_x call _fnc_addHandlers} forEach allUnits; addMissionEventHandler ["EntityCreated", { params ["_entity"]; + if !(_entity isKindOf "CAManBase") exitWith {}; _thisArgs params ["_fnc_addHandlers"]; + if (local _entity) exitWith _fnc_addHandlers; - if (_entity isKindOf "CAManBase") then { + // EntityCreated fires on remote respawning units before variables are synced. We need to wait one frame before adding handlers. CBA_fnc_execNextFrame would be preferred if CBA was available on all clients. + [_entity, _fnc_addHandlers] spawn { + params ["_entity", "_fnc_addHandlers"]; + sleep 0.001; _entity call _fnc_addHandlers; }; }, [_fnc_addHandlers]]; From 84a4f91504363ebeb2dc5bcac56a6ab5b746e7b4 Mon Sep 17 00:00:00 2001 From: thegamecracks <61257169+thegamecracks@users.noreply.github.com> Date: Mon, 3 Aug 2026 18:05:42 -0400 Subject: [PATCH 10/10] fix: use explicit argument for _fnc_addHandlers --- addons/recorder/fnc_eh_fired_server.sqf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/recorder/fnc_eh_fired_server.sqf b/addons/recorder/fnc_eh_fired_server.sqf index 479d78a..d7894e1 100644 --- a/addons/recorder/fnc_eh_fired_server.sqf +++ b/addons/recorder/fnc_eh_fired_server.sqf @@ -77,7 +77,7 @@ GVAR(trackedPlacedObjects) = createHashMap; params ["_entity"]; if !(_entity isKindOf "CAManBase") exitWith {}; _thisArgs params ["_fnc_addHandlers"]; - if (local _entity) exitWith _fnc_addHandlers; + if (local _entity) exitWith {_entity call _fnc_addHandlers}; // EntityCreated fires on remote respawning units before variables are synced. We need to wait one frame before adding handlers. CBA_fnc_execNextFrame would be preferred if CBA was available on all clients. [_entity, _fnc_addHandlers] spawn {