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/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, 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/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