From 042ddf0e3449e5299052e3d7abb9fe84b4eea5c9 Mon Sep 17 00:00:00 2001 From: IndigoFox Date: Thu, 13 Apr 2023 00:11:09 -0700 Subject: [PATCH 1/3] add squad params to identity --- .../functions/capture/fn_player_identity.sqf | 51 +++++++++++++++++++ .../functions/capture/fn_unit_inventory.sqf | 3 +- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/@RangerMetrics/addons/RangerMetrics/functions/capture/fn_player_identity.sqf b/@RangerMetrics/addons/RangerMetrics/functions/capture/fn_player_identity.sqf index ecbc2ef..9eef434 100644 --- a/@RangerMetrics/addons/RangerMetrics/functions/capture/fn_player_identity.sqf +++ b/@RangerMetrics/addons/RangerMetrics/functions/capture/fn_player_identity.sqf @@ -3,6 +3,8 @@ if (!RangerMetrics_run) exitWith {}; params ["_playerID", "_ownerId", "_playerUID", "_profileName", "_displayName", "_steamName", "_clientState", "_isHC", "_adminState", "_networkInfo", "_unit", ["_jip", false]]; // _networkInfo params ["_avgPing", "_avgBandwidth", "_desync"]; + + private _fields = [ ["string", "playerID", _playerID], ["string", "ownerId", _ownerId], @@ -14,6 +16,55 @@ private _fields = [ ["bool", "isJip", _jip] ]; +try { + // Get Squad Info of Player + (squadParams _unit) params [ + "_squadInfo", + "_unitInfo", + "_squadId", + "_a3unitsId" + ]; + + // For each section, we'll define the format and save to fields + _squadInfoDataFormat = [ + "squadNick", + "squadName", + "squadEmail", + "squadWeb", + "squadLogo", + "squadTitle" + ]; + + { + _fields pushBack [ + "string", + _squadInfoDataFormat select _forEachIndex, + _squadInfo select _forEachIndex + ]; + } forEach _squadInfoDataFormat; + + _unitInfoDataFormat =[ + "unitUid", + "unitName", + "unitFullName", + "unitICQ", + "unitRemark" + ]; + + { + _fields pushBack [ + "string", + _unitInfoDataFormat select _forEachIndex, + _unitInfo select _forEachIndex + ]; + } forEach _unitInfoDataFormat; +} catch { + // If we fail to get squad info, we'll just skip it + [format["Failed to get squad info for %1", _playerUID]] call RangerMetrics_fnc_log; +}; + + + // Role description private _roleDescription = roleDescription _unit; if (_roleDescription isNotEqualTo "") then { diff --git a/@RangerMetrics/addons/RangerMetrics/functions/capture/fn_unit_inventory.sqf b/@RangerMetrics/addons/RangerMetrics/functions/capture/fn_unit_inventory.sqf index ace0c52..eab781b 100644 --- a/@RangerMetrics/addons/RangerMetrics/functions/capture/fn_unit_inventory.sqf +++ b/@RangerMetrics/addons/RangerMetrics/functions/capture/fn_unit_inventory.sqf @@ -5,7 +5,7 @@ params [ ]; if (isNull _unit) exitWith {false}; -// if (!isPlayer _unit) exitWith {}; +if (!isPlayer _unit) exitWith {}; // do not check more than once every 15 seconds _checkDelay = 15; @@ -47,7 +47,6 @@ if (_unitId isEqualTo -1) exitWith {false}; "unit_loadout", [ ["string", "playerUID", _playerUID], - ["string", "unitId", str _unitId], ["string", "format", "className"] ], _classItemCounts, From 5d30b1931c6f0b8858f6838b409dd017cf873417 Mon Sep 17 00:00:00 2001 From: IndigoFox Date: Fri, 14 Apr 2023 04:54:09 -0700 Subject: [PATCH 2/3] some influx scheme refinement, part 1 --- .../RangerMetrics/influxSchema.plantuml | 104 ++++++++++++------ InfluxDB/bucketsTemplate.json | 52 +++++++++ InfluxDB/exportToTemplate.ps1 | 3 + 3 files changed, 123 insertions(+), 36 deletions(-) create mode 100644 InfluxDB/bucketsTemplate.json create mode 100644 InfluxDB/exportToTemplate.ps1 diff --git a/@RangerMetrics/addons/RangerMetrics/influxSchema.plantuml b/@RangerMetrics/addons/RangerMetrics/influxSchema.plantuml index 11740e5..c9f1a4b 100644 --- a/@RangerMetrics/addons/RangerMetrics/influxSchema.plantuml +++ b/@RangerMetrics/addons/RangerMetrics/influxSchema.plantuml @@ -10,6 +10,7 @@ classDiagram Measurement PlayerDisconnected Measurement OnUserClientStateChanged Measurement OnUserAdminStateChanged + Measurement OnUserKicked Meausrement HandleChatMessage Measurement MPEnded Measurement EntityCreated @@ -19,11 +20,13 @@ classDiagram Measurement MarkerCreated Measurement MarkerDeleted Measurement MarkerUpdated + Measurement Service } server_state --> running_mission class running_mission { capture: ServerPoll, 60s + capture: MissionEH, MPEnded tag string profileName tag string connectedServer field string onLoadName @@ -43,28 +46,34 @@ classDiagram server_state --> server_time class server_time { - tag string profileName - tag string connectedServer - field float diag_tickTime - field int serverTime - field float timeMultiplier - field int accTime + capture: ServerPoll, 3s + tag string profileName + tag string connectedServer + field float diag_tickTime + field int serverTime + field float timeMultiplier + field int accTime } server_state --> running_scripts class running_scripts { - tag string profileName - tag string connectedServer - field int spawn_total - field int execVM_total - field int exec_total - field int execFSM_total - field int pfh_total + capture: ServerPoll, 3s + tag string profileName + tag string connectedServer + field int spawn_total + field int execVM_total + field int exec_total + field int execFSM_total + field int pfh_total } server_state --> entities_local class entities_local { - capture: ServerPoll, 1s (customizable) + capture: ServerPoll, 30s + capture: MissionEH, EntityKilled + capture: MissionEH, EntityCreated + capture: MissionEH, GroupCreated + capture: MissionEH, GroupDeleted tag string profileName tag string connectedServer field int units_alive @@ -74,7 +83,11 @@ classDiagram } server_state --> entities_global class entities_global { - capture: ServerPoll, 1s (customizable) + capture: ServerPoll, 30s + capture: MissionEH, EntityKilled + capture: MissionEH, EntityCreated + capture: MissionEH, GroupCreated + capture: MissionEH, GroupDeleted tag string profileName tag string connectedServer field int units_alive @@ -84,7 +97,11 @@ classDiagram } server_state --> entities_remote class entities_remote { - capture: ServerPoll, 1s (customizable) + capture: ServerPoll, 30s + capture: MissionEH, EntityKilled + capture: MissionEH, EntityCreated + capture: MissionEH, GroupCreated + capture: MissionEH, GroupDeleted tag string profileName tag string connectedServer field int units_alive @@ -95,7 +112,7 @@ classDiagram server_state --> server_performance class server_performance { - capture: ServerPoll, 1s (customizable) + capture: ServerPoll, 1s tag string profileName tag string connectedServer field string fps_avg @@ -128,7 +145,6 @@ classDiagram Measurement mission_config_file Measurement addon_options Measurement mission_parameters - Measurement visual_settings } config_state --> mission_config_file @@ -254,15 +270,6 @@ classDiagram field string missionGroup } - - config_state --> visual_settings - class visual_settings { - tag string profileName - tag string connectedServer - field string getTIParameters - field string objectViewDistance - } - class player_state @@ -272,8 +279,9 @@ classDiagram capture: MissionEH, OnUserDisconnected capture: MissionEH, PlayerConnected capture: MissionEH, PlayerDisconnected - tag string profileName + capture: MissionEH, OnUserKicked tag string connectedServer + tag string playerUID field string playerID field string ownerId field string playerUID @@ -293,9 +301,9 @@ classDiagram capture: MissionEH, PlayerDisconnected capture: MissionEH, OnUserClientStateChanged capture: MissionEH, OnUserAdminStateChanged - tag string profileName + capture: MissionEH, OnUserKicked tag string connectedServer - field string playerUID + tag string playerUID field int clientStateNumber field int adminState } @@ -303,20 +311,19 @@ classDiagram player_state --> player_performance class player_performance { capture: ServerPoll - tag string profileName tag string connectedServer - field string playerUID + tag string playerUID field float avgPing field float avgBandwidth field float desync } - player_state --> unit_loadout - class unit_loadout { + player_state --> unit_inventory + class unit_inventory { capture: InventoryClosedEH - tag string profileName tag string connectedServer - field string playerUID + tag string playerUID + field string currentWeapon field string uniform field string vest field string backpack @@ -333,6 +340,8 @@ classDiagram player_state --> unit_state class unit_state { + capture: UnitEH, GetInMan + capture: UnitEH, GetOutMan tag string connectedServer tag string playerUID field float health @@ -342,6 +351,9 @@ classDiagram field bool in_vehicle field string vehicle_role field float speed_kmh + field string unitTraitX + field bool unitTraitY + field int unitTraitZ } class player_events @@ -400,4 +412,24 @@ player_events --> Dammaged field int score field string object field string objectclass + } + + player_events --> InventoryClosed + class InventoryClosed { + capture: UnitEH, InventoryClosed + tag string connectedServer + tag string playerUID + field string currentWeaponClass + field string uniformClass + field string vestClass + field string backpackClass + field string headgearClass + field string gogglesClass + field string hmdClass + field string primaryWeaponClass + field string primaryWeaponMagazineClass + field string secondaryWeaponClass + field string secondaryWeaponMagazineClass + field string handgunWeaponClass + field string handgunMagazineClass } \ No newline at end of file diff --git a/InfluxDB/bucketsTemplate.json b/InfluxDB/bucketsTemplate.json new file mode 100644 index 0000000..4d0eca7 --- /dev/null +++ b/InfluxDB/bucketsTemplate.json @@ -0,0 +1,52 @@ +[ + { + "apiVersion": "influxdata.com/v2alpha1", + "kind": "Bucket", + "metadata": { + "name": "alerting-chaum-a8c001" + }, + "spec": { + "name": "config_state" + } + }, + { + "apiVersion": "influxdata.com/v2alpha1", + "kind": "Bucket", + "metadata": { + "name": "hardcore-hodgkin-a8c005" + }, + "spec": { + "name": "player_state" + } + }, + { + "apiVersion": "influxdata.com/v2alpha1", + "kind": "Bucket", + "metadata": { + "name": "objective-curie-a8c003" + }, + "spec": { + "name": "player_events" + } + }, + { + "apiVersion": "influxdata.com/v2alpha1", + "kind": "Bucket", + "metadata": { + "name": "romantic-blackwell-a8c009" + }, + "spec": { + "name": "server_state" + } + }, + { + "apiVersion": "influxdata.com/v2alpha1", + "kind": "Bucket", + "metadata": { + "name": "thirsty-leakey-a8c007" + }, + "spec": { + "name": "server_events" + } + } +] diff --git a/InfluxDB/exportToTemplate.ps1 b/InfluxDB/exportToTemplate.ps1 new file mode 100644 index 0000000..90c1355 --- /dev/null +++ b/InfluxDB/exportToTemplate.ps1 @@ -0,0 +1,3 @@ +# Requires Influx CLI to be installed. Used to quickly generate a template of buckets to import to an instance for pre-setup. +# https://docs.influxdata.com/influxdb/v2.7/reference/cli/influx/export/ +influx export all -f "bucketsTemplate.json" --filter=resourceKind=Bucket \ No newline at end of file From 6cf4297ab9caa0d2dd42ba3e91bdd08039cbc52e Mon Sep 17 00:00:00 2001 From: IndigoFox Date: Fri, 14 Apr 2023 11:17:41 -0700 Subject: [PATCH 3/3] reworked the extension init/de-init logic to be asynchronous --- .../addons/RangerMetrics/config.cpp | 12 +++-- .../callbackHandlers/fn_callbackHandler.sqf | 45 +++++++++++++++++ .../callbackHandlers/fn_loadSettings.sqf | 33 ++++++++++++ .../functions/core/fn_callbackHandler.sqf | 14 ------ .../functions/core/fn_postInit.sqf | 50 +++---------------- @RangerMetrics/settings.json.example | 17 +++++-- 6 files changed, 108 insertions(+), 63 deletions(-) create mode 100644 @RangerMetrics/addons/RangerMetrics/functions/callbackHandlers/fn_callbackHandler.sqf create mode 100644 @RangerMetrics/addons/RangerMetrics/functions/callbackHandlers/fn_loadSettings.sqf delete mode 100644 @RangerMetrics/addons/RangerMetrics/functions/core/fn_callbackHandler.sqf diff --git a/@RangerMetrics/addons/RangerMetrics/config.cpp b/@RangerMetrics/addons/RangerMetrics/config.cpp index 366f6ba..ced4b89 100644 --- a/@RangerMetrics/addons/RangerMetrics/config.cpp +++ b/@RangerMetrics/addons/RangerMetrics/config.cpp @@ -2,7 +2,7 @@ class CfgPatches { class RangerMetrics { units[] = {}; weapons[] = {}; - requiredVersion = 0.1; + requiredVersion = 2.10; requiredAddons[] = {}; author[] = {"EagleTrooper","Gary","IndigoFox"}; authorUrl = "http://example.com"; @@ -10,6 +10,13 @@ class CfgPatches { }; class CfgFunctions { + class RangerMetrics_callback { + class functions { + file = "\RangerMetrics\functions\callbackHandlers"; + class callbackHandler {}; + class loadSettings {}; + }; + }; class RangerMetrics_event { class functions { file = "\RangerMetrics\functions\capture\EHOnly"; @@ -23,7 +30,7 @@ class CfgFunctions { class MarkerUpdated {}; class milsim_serverEfficiency {}; }; - } + }; class RangerMetrics_cDefinitions { class functions { file = "\RangerMetrics\functions\captureDefinitions"; @@ -62,7 +69,6 @@ class CfgFunctions { class log {}; class queue {}; class send {}; - class callbackHandler {}; class sendClientPoll {}; class startServerPoll {}; class classHandlers {}; diff --git a/@RangerMetrics/addons/RangerMetrics/functions/callbackHandlers/fn_callbackHandler.sqf b/@RangerMetrics/addons/RangerMetrics/functions/callbackHandlers/fn_callbackHandler.sqf new file mode 100644 index 0000000..6cafb32 --- /dev/null +++ b/@RangerMetrics/addons/RangerMetrics/functions/callbackHandlers/fn_callbackHandler.sqf @@ -0,0 +1,45 @@ +params ["_name", "_function", "_data"]; +if !(_name == "RangerMetrics") exitWith {}; + +// Validate data param +if (isNil "_data") then {_data = ""}; + +if (_data isEqualTo "") exitWith { + [ + format ["Callback empty data: %1", _function], + "WARN" + ] call RangerMetrics_fnc_log; + false; +}; + +// Parse response from string array +private "_response"; +try { + diag_log format ["Raw callback: %1: %2", _function, _data]; + _response = parseSimpleArray _data; +} catch { + [ + format ["Callback invalid data: %1: %2", _function, _data], + "WARN" + ] call RangerMetrics_fnc_log; +}; + + +switch (_function) do { + case "deinitExtension": { + diag_log format ["RangerMetrics: deinitExtension: %1", _response]; + // Our first call is deinitExtension. When we received a single "true" value, we can then run init processes for the extension connections. + if ((_response select 0) isEqualTo true) then { + "RangerMetrics" callExtension "initExtension"; + } else { + _response call RangerMetrics_fnc_log; + }; + }; + case "loadSettings": { + // Load settings + _response call RangerMetrics_callback_fnc_loadSettings; + }; + default { + _response call RangerMetrics_fnc_log; + } +} diff --git a/@RangerMetrics/addons/RangerMetrics/functions/callbackHandlers/fn_loadSettings.sqf b/@RangerMetrics/addons/RangerMetrics/functions/callbackHandlers/fn_loadSettings.sqf new file mode 100644 index 0000000..23eadb4 --- /dev/null +++ b/@RangerMetrics/addons/RangerMetrics/functions/callbackHandlers/fn_loadSettings.sqf @@ -0,0 +1,33 @@ +private _data = _this; + +switch (_data select 0) do { + case "CREATED SETTINGS": { + [ + "settings.json did not exist and has been created - you will need to update it with your own settings before the addon will initialize further.", + "ERROR" + ] call RangerMetrics_fnc_log; + }; + case "SETTINGS LOADED": { + + RangerMetrics_settings = createHashMapFromArray (_data # 1); + [ + format [ + "Settings loaded successfully from JSON. %1", + RangerMetrics_settings + ], + "INFO" + ] call RangerMetrics_fnc_log; + + // send server profile name to all clients with JIP, so HC or player reporting knows what server it's connected to + if (isServer) then { + ["RangerMetrics_serverProfileName", profileName] remoteExecCall ["setVariable", 0, true]; + RangerMetrics_serverProfileName = profileName; + }; + }; + default { + [ + _data select 0, + "INFO" + ] call RangerMetrics_fnc_log; + }; +}; \ No newline at end of file diff --git a/@RangerMetrics/addons/RangerMetrics/functions/core/fn_callbackHandler.sqf b/@RangerMetrics/addons/RangerMetrics/functions/core/fn_callbackHandler.sqf deleted file mode 100644 index 35a5331..0000000 --- a/@RangerMetrics/addons/RangerMetrics/functions/core/fn_callbackHandler.sqf +++ /dev/null @@ -1,14 +0,0 @@ -params ["_name", "_function", "_data"]; -if (_name == "RangerMetrics") then { - if (isNil "_data") then {_data = ""}; - try { - if (_data isEqualType "") exitWith { - _data = parseSimpleArray _data; - _data call RangerMetrics_fnc_log; - }; - - diag_log format ["Callback unsupported type: %1: %2", _function, _data]; - } catch { - _data = format ["%1", _data]; - }; -}; \ No newline at end of file diff --git a/@RangerMetrics/addons/RangerMetrics/functions/core/fn_postInit.sqf b/@RangerMetrics/addons/RangerMetrics/functions/core/fn_postInit.sqf index 6986bcf..8b2e1d8 100644 --- a/@RangerMetrics/addons/RangerMetrics/functions/core/fn_postInit.sqf +++ b/@RangerMetrics/addons/RangerMetrics/functions/core/fn_postInit.sqf @@ -19,49 +19,18 @@ RangerMetrics_sendBatchHandle = scriptNull; [format ["CBA detected: %1", RangerMetrics_cbaPresent]] call RangerMetrics_fnc_log; ["Initializing v0.1"] call RangerMetrics_fnc_log; - -// load settings from extension / settings.json -private _settingsLoaded = "RangerMetrics" callExtension "loadSettings"; -// if (isNil "_settingsLoaded") exitWith { -// ["Extension not found, disabling"] call RangerMetrics_fnc_log; -// RangerMetrics_run = false; -// }; -if (_settingsLoaded isEqualTo [] || _settingsLoaded isEqualTo "") exitWith { - ["Failed to load settings, exiting", "ERROR"] call RangerMetrics_fnc_log; -}; -_settingsLoaded = parseSimpleArray (_settingsLoaded); -[format["Settings loaded: %1", _settingsLoaded]] call RangerMetrics_fnc_log; -RangerMetrics_settings = createHashMap; -RangerMetrics_settings set [ - "influxDB", - createHashMapFromArray [ - ["host", _settingsLoaded#1], - ["org", _settingsLoaded#2] - ] -]; -RangerMetrics_settings set [ - "arma3", - createHashMapFromArray [ - ["refreshRateMs", _settingsLoaded#3] - ] +// Create listener - extension calls are async, so we need to listen for the response +addMissionEventHandler [ + "ExtensionCallback", + RangerMetrics_callback_fnc_callbackHandler ]; +// Deinit to start fresh. See callback handler for the remainder of async init code +"RangerMetrics" callExtension "deinitExtension"; -// connect to DB, extension is now ready -private _dbConnection = "RangerMetrics" callExtension "connectToInflux"; -if (_dbConnection isEqualTo "") exitWith { - ["Failed to connect to InfluxDB, disabling"] call RangerMetrics_fnc_log; -}; -_response = parseSimpleArray _dbConnection; -(_response) call RangerMetrics_fnc_log; -systemChat str _response; -// send server profile name to all clients with JIP, so HC or player reporting knows what server it's connected to -if (isServer) then { - ["RangerMetrics_serverProfileName", profileName] remoteExecCall ["setVariable", 0, true]; - RangerMetrics_serverProfileName = profileName; -}; +if (true) exitWith {}; // define the metrics to capture by sideloading definition files @@ -158,10 +127,7 @@ RangerMetrics_captureDefinitions = createHashMapFromArray [ [] spawn { sleep 1; isNil { - addMissionEventHandler [ - "ExtensionCallback", - RangerMetrics_fnc_callbackHandler - ]; + // set up CBA class inits if CBA loaded call RangerMetrics_fnc_classHandlers; diff --git a/@RangerMetrics/settings.json.example b/@RangerMetrics/settings.json.example index 4a3c403..854191b 100644 --- a/@RangerMetrics/settings.json.example +++ b/@RangerMetrics/settings.json.example @@ -1,6 +1,15 @@ { - "host" : "http://INFLUX_URL:8086", - "token": "XXXXXXXXXXXXXXXXXXXXXXXXXXXX_AUTH_TOKEN_XXXXXXXXXXXXXXXXXXXXXXXXXXX", - "org" : "ORG_NAME", - "bucket" : "BUCKET_NAME", + "influxdb": { + "host": "http://host:8086", + "token": "my-token", + "org": "ranger-metrics" + }, + "timescaledb": { + "connectionUrl": "postgresql://postgres:password@host:5432", + "databaseName": "ranger_metrics", + "description": "TimescaleDB is an open-source time-series database built on Postgres. Please leave the final section of the connection URL as 'postgres' as this is the maintenance database name. The extension will connect here first and create the database with desired name, then shift connections to the newly created database to create the schema and conduct write operations." + }, + "arma3": { + "refreshRateMs": 1000 + } } \ No newline at end of file