From a903cbd67629c1f355acf4514b7e0beda94a65bc Mon Sep 17 00:00:00 2001 From: thegamecracks <61257169+thegamecracks@users.noreply.github.com> Date: Mon, 3 Aug 2026 15:06:42 -0400 Subject: [PATCH 1/5] fix: admins missing UI controls when JIP joining Clients connecting to the server with their steam ID in the administrator list, or logging in during the lobby, don't receive the admin UI controls when joining a mission in progress. Seemingly in this situation, the PlayerConnected mission EH fires before getUserInfo has a player object to return, resulting in the log: > [OCAP] (recorder) WARNING: getUserInfo unit (index 10) is null for PID 1431421350 (UID: 7656...) When backing out to the lobby and rejoining, i.e. not fully disconnecting from the server, the EH receives the player object and adds the controls as normal. To solve this, an OnUserSelectedPlayer EH is added to wait until the player object exists and transfers locality: https://community.bistudio.com/wiki/Arma_3:_Mission_Event_Handlers#OnUserSelectedPlayer > This 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. --- addons/recorder/XEH_prep.sqf | 1 + addons/recorder/fnc_addEventMission.sqf | 9 ++++ addons/recorder/fnc_adminUIcontrol.sqf | 2 +- addons/recorder/fnc_eh_connected.sqf | 5 --- .../recorder/fnc_eh_onUserSelectedPlayer.sqf | 41 +++++++++++++++++++ 5 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 addons/recorder/fnc_eh_onUserSelectedPlayer.sqf diff --git a/addons/recorder/XEH_prep.sqf b/addons/recorder/XEH_prep.sqf index a967ea6..b9d7e5c 100644 --- a/addons/recorder/XEH_prep.sqf +++ b/addons/recorder/XEH_prep.sqf @@ -19,6 +19,7 @@ PREP(addUnitEventHandlers); PREP(eh_connected); PREP(eh_disconnected); PREP(eh_onUserAdminStateChanged); +PREP(eh_onUserSelectedPlayer); PREP(adminUIcontrol); PREP(eh_firedMan); diff --git a/addons/recorder/fnc_addEventMission.sqf b/addons/recorder/fnc_addEventMission.sqf index 6f85cfd..66934c6 100644 --- a/addons/recorder/fnc_addEventMission.sqf +++ b/addons/recorder/fnc_addEventMission.sqf @@ -53,6 +53,15 @@ if (isNil QEGVAR(EH,OnUserAdminStateChanged)) then { OCAPEXTLOG(["Initialized OnUserAdminStateChanged EH"]); }; +if (isNil QEGVAR(EH,OnUserSelectedPlayer)) then { + // Event Handler: OCAP_EH_OnUserSelectedPlayer + // Handle for the "OnUserSelectedPlayer" mission event handler. Fired when a player object is selected for user. Calls . + EGVAR(EH,OnUserSelectedPlayer) = addMissionEventHandler ["OnUserSelectedPlayer", { + _this call FUNC(eh_onUserSelectedPlayer); + }]; + OCAPEXTLOG(["Initialized OnUserSelectedPlayer EH"]); +}; + if (isNil QEGVAR(EH,EntityKilled)) then { // Event Handler: OCAP_EH_EntityKilled // Handle for the "EntityKilled" mission event handler. Fired when an entity is killed. Calls . diff --git a/addons/recorder/fnc_adminUIcontrol.sqf b/addons/recorder/fnc_adminUIcontrol.sqf index 2fb5774..6f3885f 100644 --- a/addons/recorder/fnc_adminUIcontrol.sqf +++ b/addons/recorder/fnc_adminUIcontrol.sqf @@ -6,8 +6,8 @@ Description: Runs checks to determine if a player should have the administrative diary entry added or removed upon joining the mission or logging in/out as admin. - - at mission start to determine if a player is in - to add/remove when a player logs in or out as admin on the server + - at mission start to determine if a player is in Parameters: _PID - PlayerID indicating unique network client on the server [String] diff --git a/addons/recorder/fnc_eh_connected.sqf b/addons/recorder/fnc_eh_connected.sqf index a0e8210..9cab6d3 100644 --- a/addons/recorder/fnc_eh_connected.sqf +++ b/addons/recorder/fnc_eh_connected.sqf @@ -7,8 +7,6 @@ This function uses the event handler to log "connected" events to the timeline. - It also calls to apply the admin UI if the player is in . - Parameters: See the wiki for details. @@ -40,6 +38,3 @@ if (_owner isEqualTo 2) exitWith {}; ["playerUid", _uid] ]] call CBA_fnc_encodeJSON ]] call EFUNC(extension,sendData); - -// trigger admin control check for all connecting players -[_idstr, "connect"] call FUNC(adminUIcontrol); diff --git a/addons/recorder/fnc_eh_onUserSelectedPlayer.sqf b/addons/recorder/fnc_eh_onUserSelectedPlayer.sqf new file mode 100644 index 0000000..555fb9f --- /dev/null +++ b/addons/recorder/fnc_eh_onUserSelectedPlayer.sqf @@ -0,0 +1,41 @@ +/* ---------------------------------------------------------------------------- + FILE: fnc_eh_onUserSelectedPlayer.sqf + + FUNCTION: OCAP_recorder_fnc_eh_onUserSelectedPlayer + + Description: + Uses to detect when someone joins the server. + + Calls to apply the admin UI if the player is in . + + Parameters: + _networkId - The network ID of the player who has logged in or out of the server [String] + _playerObject - player object to be controlled by the user [Object] + + Returns: + Nothing + + Examples: + > call FUNC(eh_onUserSelectedPlayer); + + Public: + No + + Author: + IndigoFox +---------------------------------------------------------------------------- */ +#include "script_component.hpp" + +params ["_networkId", "_playerObject"]; + +// In rare cases, _playerObject may be objNull despite Arma 3 v2.18 allowing the event to be postponed. +if (isNull _playerObject) exitWith { + diag_log text format ["[OCAP] (recorder) WARNING: connecting player object is null for PID: %1", _networkId]; +}; + +_playerObject addEventHandler ["Local", { + params ["_playerObject"]; + _playerObject removeEventHandler [_thisEvent, _thisEventHandler]; + private _networkId = getPlayerID _playerObject; + [_networkId, "connect"] call FUNC(adminUIcontrol); +}]; From eb549870865623000c3bbfa24bd3bb1eea19ad20 Mon Sep 17 00:00:00 2001 From: thegamecracks <61257169+thegamecracks@users.noreply.github.com> Date: Mon, 3 Aug 2026 15:14:42 -0400 Subject: [PATCH 2/5] fix: admins missing UI controls when non-JIP Unlike PlayerConnected EH, OnUserSelectedPlayer does not fire for non-JIP players, so the previous commit ironically broke non-JIP admins. OCAP_recorder_fnc_init documents an edge case where OCAP initialization may occur too late for the PlayerConnected EH to pick up non-JIP players, and so it calls adminUIcontrol there. However, `getUserInfo` expects a DirectPlayer ID from `getPlayerID`, not a machine network ID as would be returned by `str owner _x`. This fixes it to use the correct command. --- addons/recorder/fnc_eh_onUserSelectedPlayer.sqf | 1 + addons/recorder/fnc_init.sqf | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/recorder/fnc_eh_onUserSelectedPlayer.sqf b/addons/recorder/fnc_eh_onUserSelectedPlayer.sqf index 555fb9f..0c7ebf6 100644 --- a/addons/recorder/fnc_eh_onUserSelectedPlayer.sqf +++ b/addons/recorder/fnc_eh_onUserSelectedPlayer.sqf @@ -28,6 +28,7 @@ params ["_networkId", "_playerObject"]; +// Unlike PlayerConnected EH, this event does not fire for non-JIP players. Admins in this case are handled by . // In rare cases, _playerObject may be objNull despite Arma 3 v2.18 allowing the event to be postponed. if (isNull _playerObject) exitWith { diag_log text format ["[OCAP] (recorder) WARNING: connecting player object is null for PID: %1", _networkId]; diff --git a/addons/recorder/fnc_init.sqf b/addons/recorder/fnc_init.sqf index 78f6686..44a9dce 100644 --- a/addons/recorder/fnc_init.sqf +++ b/addons/recorder/fnc_init.sqf @@ -136,7 +136,7 @@ call FUNC(telemetryLoop); // where players connected before OCAP initialized don't get diary entries) // Wait for getUserInfo to be populated before calling, as it may not be ready during postInit { - private _pid = str owner _x; + private _pid = getPlayerID _x; [{ private _info = getUserInfo _this; !isNil "_info" && {_info isEqualType [] && {count _info >= 11}} From c259a9e2db403de4170ec99b58c7724f6ac61b6c Mon Sep 17 00:00:00 2001 From: thegamecracks <61257169+thegamecracks@users.noreply.github.com> Date: Tue, 4 Aug 2026 07:04:19 -0400 Subject: [PATCH 3/5] docs: OnUserSelectedPlayer fires before postInit --- addons/recorder/fnc_eh_onUserSelectedPlayer.sqf | 2 +- addons/recorder/fnc_init.sqf | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/addons/recorder/fnc_eh_onUserSelectedPlayer.sqf b/addons/recorder/fnc_eh_onUserSelectedPlayer.sqf index 0c7ebf6..0dfec8f 100644 --- a/addons/recorder/fnc_eh_onUserSelectedPlayer.sqf +++ b/addons/recorder/fnc_eh_onUserSelectedPlayer.sqf @@ -28,7 +28,7 @@ params ["_networkId", "_playerObject"]; -// Unlike PlayerConnected EH, this event does not fire for non-JIP players. Admins in this case are handled by . +// For non-JIP players, OnUserSelectedPlayer fires between preInit and postInit. Since we're initializing in postInit, this function will be too late to handle non-JIP players. Admins in this case are handled by . // In rare cases, _playerObject may be objNull despite Arma 3 v2.18 allowing the event to be postponed. if (isNull _playerObject) exitWith { diag_log text format ["[OCAP] (recorder) WARNING: connecting player object is null for PID: %1", _networkId]; diff --git a/addons/recorder/fnc_init.sqf b/addons/recorder/fnc_init.sqf index 44a9dce..30e12bd 100644 --- a/addons/recorder/fnc_init.sqf +++ b/addons/recorder/fnc_init.sqf @@ -132,8 +132,7 @@ call FUNC(eh_fired_server); call FUNC(telemetryLoop); [] spawn FUNC(getStaticObjects); -// Check already-connected players for admin controls (fixes race condition -// where players connected before OCAP initialized don't get diary entries) +// Check non-JIP players for admin controls, as postInit is too late for to fire. // Wait for getUserInfo to be populated before calling, as it may not be ready during postInit { private _pid = getPlayerID _x; From 0cc7e1a63918d2812f36b9cacd5603b0dd572931 Mon Sep 17 00:00:00 2001 From: thegamecracks <61257169+thegamecracks@users.noreply.github.com> Date: Tue, 4 Aug 2026 07:10:36 -0400 Subject: [PATCH 4/5] feat: wait 30s more to handle objNull edge case --- .../recorder/fnc_eh_onUserSelectedPlayer.sqf | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/addons/recorder/fnc_eh_onUserSelectedPlayer.sqf b/addons/recorder/fnc_eh_onUserSelectedPlayer.sqf index 0dfec8f..3db63f5 100644 --- a/addons/recorder/fnc_eh_onUserSelectedPlayer.sqf +++ b/addons/recorder/fnc_eh_onUserSelectedPlayer.sqf @@ -29,14 +29,22 @@ params ["_networkId", "_playerObject"]; // For non-JIP players, OnUserSelectedPlayer fires between preInit and postInit. Since we're initializing in postInit, this function will be too late to handle non-JIP players. Admins in this case are handled by . -// In rare cases, _playerObject may be objNull despite Arma 3 v2.18 allowing the event to be postponed. -if (isNull _playerObject) exitWith { - diag_log text format ["[OCAP] (recorder) WARNING: connecting player object is null for PID: %1", _networkId]; +if (!isNull _playerObject) exitWith { + _playerObject addEventHandler ["Local", { + params ["_playerObject"]; + _playerObject removeEventHandler [_thisEvent, _thisEventHandler]; + + private _networkId = getPlayerID _playerObject; + [_networkId, "connect"] call FUNC(adminUIcontrol); + }]; }; -_playerObject addEventHandler ["Local", { - params ["_playerObject"]; - _playerObject removeEventHandler [_thisEvent, _thisEventHandler]; - private _networkId = getPlayerID _playerObject; - [_networkId, "connect"] call FUNC(adminUIcontrol); -}]; +// In rare cases, _playerObject may be objNull despite Arma 3 v2.18 allowing the event to be postponed. +// Make a last ditch attempt to wait for the player object to be set (similar to ). +[{ + !isNull (getUserInfo _this param [10, objNull, [objNull]]) +}, { + [_this, "connect"] call FUNC(adminUIcontrol); +}, _networkId, 30, { + diag_log text format ["[OCAP] (recorder) WARNING: connecting player object is null for PID: %1", _networkId]; +}] call CBA_fnc_waitUntilAndExecute; From 624ea2ecb669bfac22b9b2bfdaa3960c1c61f79d Mon Sep 17 00:00:00 2001 From: thegamecracks <61257169+thegamecracks@users.noreply.github.com> Date: Tue, 4 Aug 2026 07:20:00 -0400 Subject: [PATCH 5/5] fix: incorrect variable name in timeout code --- addons/recorder/fnc_eh_onUserSelectedPlayer.sqf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/recorder/fnc_eh_onUserSelectedPlayer.sqf b/addons/recorder/fnc_eh_onUserSelectedPlayer.sqf index 3db63f5..533299d 100644 --- a/addons/recorder/fnc_eh_onUserSelectedPlayer.sqf +++ b/addons/recorder/fnc_eh_onUserSelectedPlayer.sqf @@ -46,5 +46,5 @@ if (!isNull _playerObject) exitWith { }, { [_this, "connect"] call FUNC(adminUIcontrol); }, _networkId, 30, { - diag_log text format ["[OCAP] (recorder) WARNING: connecting player object is null for PID: %1", _networkId]; + diag_log text format ["[OCAP] (recorder) WARNING: connecting player object is null for PID: %1", _this]; }] call CBA_fnc_waitUntilAndExecute;