WIP large expansion of functionality using go dll. track everything

This commit is contained in:
2023-04-08 21:01:53 -07:00
parent 44a353a24b
commit 4d8d8e44f5
28 changed files with 1158 additions and 25 deletions

View File

@@ -11,14 +11,39 @@ class CfgPatches {
class CfgFunctions {
class RangerMetrics {
class Common {
file = "\RangerMetrics\functions";
class core {
file = "\RangerMetrics\functions\core";
class postInit { postInit = 1;};
class gather {};
class captureLoop {};
class log {};
class queue {};
class send {};
class checkResults {};
class log {};
};
class eventHandlers {
file = "\RangerMetrics\functions\eventHandlers";
class addHandlers {};
class callbackHandler {};
};
class helpers {
file = "\RangerMetrics\functions\helpers";
class toLineProtocol {};
class encodeJSON {};
class stringReplace {};
class unixTimestamp {};
};
class measurements {
file = "\RangerMetrics\functions\measurements";
class chat_message {};
class entities_global {};
class entities_local {};
class mission_config_file {};
class player_identity {};
class player_performance {};
class player_status {};
class running_mission {};
class running_scripts {};
class server_performance {};
class server_time {};
};
};
};

View File

@@ -0,0 +1,28 @@
// function adapted from YAINA by MartinCo at http://yaina.eu
if (
missionNamespace getVariable ["RangerMetrics_run",false]
) then {
private _startTime = diag_tickTime;
call RangerMetrics_fnc_server_performance;
call RangerMetrics_fnc_running_scripts;
call RangerMetrics_fnc_server_time;
call RangerMetrics_fnc_entities_local;
call RangerMetrics_fnc_entities_global;
private _allUsers = allUsers apply {getUserInfo _x};
{
_x call RangerMetrics_fnc_player_performance;
_x call RangerMetrics_fnc_player_status;
} forEach _allUsers;
// log the runtime and switch off debug so it doesn't flood the log
if (
missionNamespace getVariable ["RangerMetrics_debug",false]
) then {
[format ["Run time: %1", diag_tickTime - _startTime], "DEBUG"] call RangerMetrics_fnc_log;
// missionNamespace setVariable ["RangerMetrics_debug",false];
};
};

View File

@@ -0,0 +1,41 @@
params [["_text","Log text invalid"], ["_type","INFO"]];
if (typeName _this != "ARRAY") exitWith {
diag_log format ["RangerMetrics: Invalid log params: %1", _this];
};
if (typeName _text != "STRING") exitWith {
diag_log format ["RangerMetrics: Invalid log text: %1", _this];
};
if (typeName _type != "STRING") exitWith {
diag_log format ["RangerMetrics: Invalid log type: %1", _this];
};
if (_type isEqualTo "DEBUG") then {
if (!RangerMetrics_debug) exitWith {};
};
private _textFormatted = format [
"[%1] %2: %3",
RangerMetrics_logPrefix,
_type,
_text];
if(isServer) then {
diag_log text _textFormatted;
if(isMultiplayer) then {
_playerIds = [];
{
_player = _x;
_ownerId = owner _player;
if(_ownerId > 0) then {
if(getPlayerUID _player in ["76561198013533294"]) then {
_playerIds pushBack _ownerId;
};
};
} foreach allPlayers;
if(count _playerIds > 0) then {
[_textFormatted] remoteExec ["diag_log", _playerIds];
};
};
};

View File

@@ -0,0 +1,121 @@
// if (!isServer) exitWith {};
if (is3DEN || !isMultiplayer) exitWith {};
RangerMetrics_cbaPresent = (isClass(configFile >> "CfgPatches" >> "cba_main"));
RangerMetrics_logPrefix = "RangerMetrics";
RangerMetrics_debug = true;
RangerMetrics_initialized = false;
RangerMetrics_run = false;
RangerMetrics_activeThreads = [];
RangerMetrics_messageQueue = createHashMap;
RangerMetrics_sendBatchHandle = scriptNull;
RangerMetrics_captureBatchHandle = scriptNull;
[format ["Instance name: %1", profileName]] call RangerMetrics_fnc_log;
[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],
["defaultBucket", _settingsLoaded#3]
]
];
RangerMetrics_settings set [
"arma3",
createHashMapFromArray [
["refreshRateMs", _settingsLoaded#4]
]
];
// 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;
};
(parseSimpleArray _dbConnection) 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;
};
addMissionEventHandler ["ExtensionCallback", {
_this call RangerMetrics_fnc_callbackHandler;
}];
RangerMetrics_initialized = true;
RangerMetrics_run = true;
call RangerMetrics_fnc_addHandlers;
if (RangerMetrics_cbaPresent) then { // CBA is running, use PFH
/*
This capture method is dynamic.
Every 5 seconds, two script handles are checked. One is for capturing, one is for sending.
The capturing script will go through and capture data, getting nanosecond precision timestamps from the extension to go alongside each data point, then saving it to a queue. It will go through all assigned interval-based checks then exit, and on the next interval of this parent PFH, the capturing script will be spawned again.
The queue is a hashmap where keys are buckets and values are arrays of data points in [string] line protocol format.
The sending script will go through and send data, sending it in batches per bucket and per 2000 data points, as the max extension call with args is 2048 elements.
The sending script will also check if the queue is empty, and if it is, it will exit. This means scriptDone will be true, and on the next interval of this parent PFH, the sending script will be spawned again.
This system means that capture and sending are occurring in the scheduled environment, not blocking the server, while maintaining the timestamps of when each point was captured. The cycles of each will only occur at most once per 5 seconds, leaving plenty of time, and there will never be more than one call for each at a time.
*/
[{
params ["_args", "_idPFH"];
if (scriptDone RangerMetrics_captureBatchHandle) then {
RangerMetrics_captureBatchHandle = [] spawn RangerMetrics_fnc_captureLoop;
};
if (scriptDone RangerMetrics_sendBatchHandle) then {
RangerMetrics_sendBatchHandle = [] spawn RangerMetrics_fnc_send;
};
}, 5, []] call CBA_fnc_addPerFrameHandler;
// runs on interval
// [{
// params ["_args", "_idPFH"];
// RangerMetrics_unixTime = (parseSimpleArray ("RangerMetrics" callExtension "getUnixTimeNano")) select 0;
// // spawn RangerMetrics_fnc_captureLoop;
// // call RangerMetrics_fnc_send;
// }, 3, []] call CBA_fnc_addPerFrameHandler;
} else { // CBA isn't running, use sleep
[] spawn {
while {true} do {
RangerMetrics_unixTime = (parseSimpleArray ("RangerMetrics" callExtension "getUnixTimeNano")) select 0;
call RangerMetrics_fnc_captureLoop; // nested to match CBA PFH signature
sleep 1;
if (RangerMetrics_sendBatchHandle != -1) exitWith {
RangerMetrics_sendBatchHandle = [] spawn RangerMetrics_fnc_send;
};
if (scriptDone RangerMetrics_sendBatchHandle) exitWith {
RangerMetrics_sendBatchHandle = -1;
};
};
};
};

View File

@@ -0,0 +1,32 @@
params [
["_bucket", "default", [""]],
"_measurement",
["_tags", [], [[], nil]],
["_fields", [], [[], nil]]
];
// format[
// "profile=%1,world=%2,%3",
// profileName,
// toLower worldName,
// (_tags apply {format['%1=%2', _x#0, _x#1]}) joinString ","
// ],
_tags pushback ["string", "profileName", profileName];
_tags pushBack ["string", "connectedServer", RangerMetrics_serverProfileName];
private _extSend = format [
"%1,%2 %3 %4",
_measurement, // metric name
(_tags apply {_x call RangerMetrics_fnc_toLineProtocol}) joinString ",",
(_fields apply {_x call RangerMetrics_fnc_toLineProtocol}) joinString ",",
call RangerMetrics_fnc_unixTimestamp
];
// add to queue
(RangerMetrics_messageQueue getOrDefault [_bucket, [], true]) pushBack _extSend;
true

View File

@@ -0,0 +1,39 @@
// send the data
// duplicate the message queue so we can clear it before sending the data
private "_extSend";
isNil {
_extSend = + RangerMetrics_messageQueue;
RangerMetrics_messageQueue = createHashMap;
};
// debug
if (
missionNamespace getVariable ["RangerMetrics_debug",false]
) then {
[format ["Sending a3influx data: %1", _extSend], "DEBUG"] call RangerMetrics_fnc_log;
};
{
private _bucket = _x;
private _records = _y;
while {count _records > 0} do {
// extension calls support a max of 2048 elements in the extension call
// so we need to split the data into chunks of 2000
private "_processing";
_processing = _records select [0, (count _records -1) min 2000];
_records = _records select [2000, count _records - 1];
// send the data
if (
missionNamespace getVariable ["RangerMetrics_debug",false]
) then {
[format ["Bucket: %1, RecordsCount: %2", _bucket, count _processing], "DEBUG"] call RangerMetrics_fnc_log;
};
"RangerMetrics" callExtension ["sendToInflux", flatten [_bucket, _processing]];
};
} forEach _extSend;

View File

@@ -0,0 +1,82 @@
addMissionEventHandler ["MPEnded", {
private ["_winner", "_reason"];
_winner = "Unknown";
_reason = "Mission Complete";
["server_events", "MPEnded", nil, [
["string", "winner", _winner],
["string", "reason", _reason]
]] call RangerMetrics_fnc_queue;
}];
addMissionEventHandler ["OnUserConnected", {
params ["_networkId", "_clientStateNumber", "_clientState"];
(getUserInfo _networkId) call RangerMetrics_fnc_player_identity;
(getUserInfo _networkId) call RangerMetrics_fnc_player_status;
["server_events", "UserConnected", nil, [
["string", "networkId", _networkId],
["int", "clientStateNumber", _clientStateNumber],
["string", "clientState", _clientState]
]] call RangerMetrics_fnc_queue;
}];
addMissionEventHandler ["OnUserDisconnected", {
params ["_networkId", "_clientStateNumber", "_clientState"];
(getUserInfo _networkId) call RangerMetrics_fnc_player_identity;
(getUserInfo _networkId) call RangerMetrics_fnc_player_status;
["server_events", "OnUserDisconnected", nil, [
["string", "networkId", _networkId],
["int", "clientStateNumber", _clientStateNumber],
["string", "clientState", _clientState]
]] call RangerMetrics_fnc_queue;
}];
addMissionEventHandler ["PlayerConnected", {
params ["_id", "_uid", "_name", "_jip", "_owner", "_idstr"];
(getUserInfo _idstr) call RangerMetrics_fnc_player_identity;
(getUserInfo _idstr) call RangerMetrics_fnc_player_status;
["server_events", "PlayerConnected", nil, [
["int", "id", _id],
["string", "uid", _uid],
["string", "name", _name],
["bool", "jip", _jip],
["int", "owner", _owner],
["string", "idstr", _idstr]
]] call RangerMetrics_fnc_queue;
}];
addMissionEventHandler ["PlayerDisconnected", {
params ["_id", "_uid", "_name", "_jip", "_owner", "_idstr"];
(getUserInfo _idstr) call RangerMetrics_fnc_player_identity;
(getUserInfo _idstr) call RangerMetrics_fnc_player_status;
["server_events", "PlayerDisconnected", nil, [
["int", "id", _id],
["string", "uid", _uid],
["string", "name", _name],
["bool", "jip", _jip],
["int", "owner", _owner],
["string", "idstr", _idstr]
]] call RangerMetrics_fnc_queue;
}];
addMissionEventHandler ["OnUserClientStateChanged", {
params ["_networkId", "_clientStateNumber", "_clientState"];
(getUserInfo _networkId) call RangerMetrics_fnc_player_status;
["server_events", "OnUserClientStateChanged", nil, [
["string", "networkId", _networkId],
["int", "clientStateNumber", _clientStateNumber],
["string", "clientState", _clientState]
]] call RangerMetrics_fnc_queue;
}];
addMissionEventHandler ["OnUserAdminStateChanged", {
params ["_networkId", "_loggedIn", "_votedIn"];
(getUserInfo _networkId) call RangerMetrics_fnc_player_status;
["server_events", "OnUserAdminStateChanged", nil, [
["string", "networkId", _networkId],
["bool", "loggedIn", _loggedIn],
["bool", "votedIn", _votedIn]
]] call RangerMetrics_fnc_queue;
}];
addMissionEventHandler ["HandleChatMessage", {
_this call RangerMetrics_fnc_chat_message;
// don't interfaere with the chat message
false;
}];

View File

@@ -0,0 +1,14 @@
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];
};
};

View File

@@ -0,0 +1,4 @@
private _visualSettings = [
["objectViewDistance", getObjectViewDistance # 0],
["viewDistance", viewDistance]
];

View File

@@ -10,24 +10,24 @@ RangerMetrics_messageQueue = createHashMap;
[format ["CBA detected: %1", _cba]] call RangerMetrics_fnc_log;
["Initializing v1.1"] call RangerMetrics_fnc_log;
private _settingsLoaded = ["RangerMetrics.influx.load_settings", []] call py3_fnc_callExtension;
if (isNil "_settingsLoaded") exitWith {
["Extension not found, disabling"] call RangerMetrics_fnc_log;
RangerMetrics_run = false;
};
if (_settingsLoaded isEqualTo []) then {
if (count _settingsLoaded == 0) exitWith {
["Settings not loaded, disabling"] call RangerMetrics_fnc_log;
RangerMetrics_run = false;
};
if (_settingsLoaded#0 isEqualTo 1) exitWith {
[
format["Settings not loaded, disabling. %1", _settingsLoaded#1],
"ERROR"
] call RangerMetrics_fnc_log;
RangerMetrics_run = false;
};
};
private _settingsLoaded = "RangerMetrics" callExtension "loadSettings";
// if (isNil "_settingsLoaded") exitWith {
// ["Extension not found, disabling"] call RangerMetrics_fnc_log;
// RangerMetrics_run = false;
// };
// if (_settingsLoaded isEqualTo []) then {
// if (count _settingsLoaded == 0) exitWith {
// ["Settings not loaded, disabling"] call RangerMetrics_fnc_log;
// RangerMetrics_run = false;
// };
// if (_settingsLoaded#0 isEqualTo 1) exitWith {
// [
// format["Settings not loaded, disabling. %1", _settingsLoaded#1],
// "ERROR"
// ] call RangerMetrics_fnc_log;
// RangerMetrics_run = false;
// };
// };
format["Settings loaded: %1", _settingsLoaded#2] call RangerMetrics_fnc_log;
RangerMetrics_settings = _settingsLoaded#2;
// RangerMetrics_settings = createHashMap;
@@ -40,7 +40,7 @@ RangerMetrics_settings = _settingsLoaded#2;
// "arma3",
// createHashMapFromArray (_top get "refreshRateMs")
// ];
["RangerMetrics.influx.connect_to_influx", []] call py3_fnc_callExtension;
"RangerMetrics" callExtension "connectToInflux";
RangerMetrics_run = true;

View File

@@ -12,7 +12,7 @@ private _prefix = "Arma3";
private _extSend = [
_measurement, // metric name
_valueType, // float or int or bool
_valueType // float or int or bool
];
private _outTags = [ // tags

View File

@@ -0,0 +1,107 @@
/* ----------------------------------------------------------------------------
Function: CBA_fnc_encodeJSON
Description:
Serializes input to a JSON string. Can handle
- ARRAY
- BOOL
- CONTROL
- GROUP
- LOCATION
- NAMESPACE
- NIL (ANY)
- NUMBER
- OBJECT
- STRING
- TASK
- TEAM_MEMBER
- HASHMAP
- Everything else will simply be stringified.
Parameters:
_object - Object to serialize. <ARRAY, ...>
Returns:
_json - JSON string containing serialized object.
Examples:
(begin example)
private _settings = call CBA_fnc_createNamespace;
_settings setVariable ["enabled", true];
private _json = [_settings] call CBA_fnc_encodeJSON;
(end)
Author:
BaerMitUmlaut
---------------------------------------------------------------------------- */
params ["_object"];
if (isNil "_object") exitWith { "null" };
switch (typeName _object) do {
case "SCALAR";
case "BOOL": {
str _object;
};
case "STRING": {
{
_object = [_object, _x#0, _x#1] call CBA_fnc_replace;
} forEach [
["\", "\\"],
["""", "\"""],
[toString [8], "\b"],
[toString [12], "\f"],
[endl, "\n"],
[toString [10], "\n"],
[toString [13], "\r"],
[toString [9], "\t"]
];
// Stringify without escaping inter string quote marks.
"""" + _object + """"
};
case "ARRAY": {
if ([_object] call CBA_fnc_isHash) then {
private _json = (([_object] call CBA_fnc_hashKeys) apply {
private _name = _x;
private _value = [_object, _name] call CBA_fnc_hashGet;
format ["%1: %2", [_name] call CBA_fnc_encodeJSON, [_value] call CBA_fnc_encodeJSON]
}) joinString ", ";
"{" + _json + "}"
} else {
private _json = (_object apply {[_x] call CBA_fnc_encodeJSON}) joinString ", ";
"[" + _json + "]"
};
};
case "HASHMAP": {
private _json = ((_object toArray false) apply {
_x params ["_key", ["_value", objNull]];
if !(_key isEqualType "") then {
_key = str _key;
};
format ["%1: %2", [_key] call CBA_fnc_encodeJSON, [_value] call CBA_fnc_encodeJSON]
}) joinString ", ";
"{" + _json + "}"
};
default {
if !(typeName _object in (supportInfo "u:allVariables*" apply {_x splitString " " select 1})) exitWith {
[str _object] call CBA_fnc_encodeJSON
};
if (isNull _object) exitWith { "null" };
private _json = ((allVariables _object) apply {
private _name = _x;
private _value = _object getVariable [_name, objNull];
format ["%1: %2", [_name] call CBA_fnc_encodeJSON, [_value] call CBA_fnc_encodeJSON]
}) joinString ", ";
"{" + _json + "}"
};
};

View File

@@ -0,0 +1,39 @@
//
// PX_fnc_stringReplace :: Replace substrings
// Author: Colin J.D. Stewart
// Usage: ["xxx is awesome, I love xxx!", "xxx" || [], "Arma"] call PX_fnc_stringReplace;
//
params["_str", "_find", "_replace"];
private["_return", "_len", "_pos"];
if !(_str isEqualType "") exitWith {
[
format[
"RangerMetrics_fnc_stringReplace: _str is not a string. %1",
_str
],
"ERROR"
] call RangerMetrics_fnc_log;
"";
};
if (!(_find isEqualType [])) then {
_find = [_find];
};
{
_return = "";
_len = count _x;
_pos = _str find _x;
while {(_pos != -1) && (count _str > 0)} do {
_return = _return + (_str select [0, _pos]) + _replace;
_str = (_str select [_pos+_len]);
_pos = _str find _x;
};
_str = _return + _str;
} forEach _find;
_str;

View File

@@ -0,0 +1,17 @@
params [
["_valueType", "string", [""]],
"_key",
"_value"
];
// debug
// diag_log format["%1=%2", _key, _value];
if (_value isEqualTo "") exitWith {
"";
};
if (_valueType isEqualTo "string") exitWith {
format['%1="%2"', _key, _value];
};
format['%1=%2', _key, _value];

View File

@@ -0,0 +1 @@
(parseSimpleArray ("RangerMetrics" callExtension "getUnixTimeNano")) select 0;

View File

@@ -0,0 +1,40 @@
params ["_channel", "_owner", "_from", "_text", "_person", "_name", "_strID", "_forcedDisplay", "_isPlayerMessage", "_sentenceType", "_chatMessageType"];
private _fields = [
["int", "channel", _channel],
["int", "owner", _owner],
["string", "from", _from],
["string", "text", _text],
// ["object", "person", _person],
["string", "name", _name],
["string", "strID", _strID],
["bool", "forcedDisplay", _forcedDisplay],
["bool", "isPlayerMessage", _isPlayerMessage],
["int", "sentenceType", _sentenceType],
["int", "chatMessageType", _chatMessageType]
];
// we need special processing to ensure the object is valid and we have a playerUid. Line protocol doesn't support empty string
private "_playerUid";
if (isNil "_person") then {
_playerUid = "";
} else {
if !(objNull isEqualType _person) then {
_playerUid = getPlayerUID _person;
} else {
_playerUid = "";
};
};
if (_playerUid isNotEqualTo "") then {
_fields pushBack ["string", "playerUid", _playerUid];
};
[
"server_events",
"HandleChatMessage",
nil,
_fields
] call RangerMetrics_fnc_queue;

View File

@@ -0,0 +1,7 @@
// Number of global units
["server_state", "entities_global", nil, [
["int", "units_alive", count allUnits ],
["int", "units_dead", count allDeadMen],
["int", "groups_total", count allGroups],
["int", "vehicles_total", count vehicles]
]] call RangerMetrics_fnc_queue;

View File

@@ -0,0 +1,7 @@
// Number of local units
["server_state", "entities_local", nil, [
["int", "units_alive", { local _x} count allUnits ],
["int", "units_dead", { local _x } count allDeadMen],
["int", "groups_total", { local _x } count allGroups],
["int", "vehicles_total", { local _x } count vehicles]
]] call RangerMetrics_fnc_queue;

View File

@@ -0,0 +1,210 @@
// get basic config properties
private _properties = [
["mission_info", [
"author",
"onLoadName",
"onLoadMission",
"loadScreen",
// "header",
"gameType",
"minPlayers",
"maxPlayers",
"onLoadIntro",
"onLoadMissionTime",
"onLoadIntroTime",
"briefingName",
"overviewPicture",
"overviewText",
"overviewTextLocked"
]],
["respawn", [
"respawn",
"respawnButton",
"respawnDelay",
"respawnVehicleDelay",
"respawnDialog",
"respawnOnStart",
"respawnTemplates",
"respawnTemplatesWest",
"respawnTemplatesEast",
"respawnTemplatesGuer",
"respawnTemplatesCiv",
"respawnWeapons",
"respawnMagazines",
"reviveMode",
"reviveUnconsciousStateMode",
"reviveRequiredTrait",
"reviveRequiredItems",
"reviveRequiredItemsFakConsumed",
"reviveMedicSpeedMultiplier",
"reviveDelay",
"reviveForceRespawnDelay",
"reviveBleedOutDelay",
"enablePlayerAddRespawn"
]],
["player_ui", [
"overrideFeedback",
"showHUD",
"showCompass",
"showGPS",
"showGroupIndicator",
"showMap",
"showNotePad",
"showPad",
"showWatch",
"showUAVFeed",
"showSquadRadar"
]],
["corpse_and_wreck", [
"corpseManagerMode",
"corpseLimit",
"corpseRemovalMinTime",
"corpseRemovalMaxTime",
"wreckManagerMode",
"wreckLimit",
"wreckRemovalMinTime",
"wreckRemovalMaxTime",
"minPlayerDistance"
]],
["mission_settings", [
"aiKills",
"briefing",
"debriefing",
"disableChannels",
"disabledAI",
"disableRandomization",
"enableDebugConsole",
"enableItemsDropping",
"enableTeamSwitch",
"forceRotorLibSimulation",
"joinUnassigned",
"minScore",
"avgScore",
"maxScore",
"onCheat",
"onPauseScript",
"saving",
"scriptedPlayer",
"skipLobby",
"HostDoesNotSkipLobby",
"missionGroup"
]
]
];
private _propertyValues = createHashMap;
// recursively walk through missionConfigFile and get all properties into a single hashmap
// iterate through list of categories with desired property names
// if the property exists in the extracted missionConfigFile property hash, save it with the category into _propertyValues
{
private _category = _x#0;
private _values = _x#1;
{
private _property = _x;
private _value = (missionConfigFile >> _property) call BIS_fnc_getCfgData;
// hint str [_category, _property, _value];
if (!isNil "_value") then {
if (typeName _value == "ARRAY") then {
_value = _value joinString ",";
};
if (isNil {_propertyValues get _category}) then {
_propertyValues set [_category, createHashMap];
};
_propertyValues get _category set [_property, _value];
};
} forEach _values;
} forEach _properties;
// Take the generated hashmap of custom-categorized configuration properties and queue them for metrics
{
private _measurementCategory = _x;
private _fields = _y;
private _fieldsWithType = [];
// InfluxDB lookup hash
_types = createHashMapFromArray [
["STRING", "string"],
["ARRAY", "string"],
["SCALAR", "float"],
["BOOL", "bool"]
];
// Preprocess the fields to clean the raw data
{
private _fieldName = _x;
private _fieldValue = _y;
private _fieldType = _types get (typeName _fieldValue);
// turn ARRAY into string since Influx can't take them
if (typeName _fieldValue == "ARRAY") then {
_fieldValue = _fieldValue joinString "|";
};
// convert 0 or 1 (from config) to BOOL
if (typeName _fieldValue == "SCALAR" && _fieldValue in [0, 1]) then {
_fieldType = "bool";
if (_fieldValue == 0) then {
_fieldValue = "false";
} else {
_fieldValue = "true";
};
};
_fieldsWithType pushBack [_fieldType, _fieldName, _fieldValue];
} forEach _fields;
// finally, send the data
[
"config_state",
"mission_config_file",
[
["category", _measurementCategory]
],
_fieldsWithType
] call RangerMetrics_fnc_queue;
} forEach _propertyValues;
// get all properties in missionConfigFile (recursive)
// private _nextCfgClasses = "true" configClasses (missionConfigFile);
// private _nextCfgProperties = configProperties [missionConfigFile];
// private _cfgProperties = createHashMap;
// while {count _nextCfgClasses > 0} do {
// {
// private _thisConfig = _x;
// private _thisConfigClasses = "true" configClasses _thisConfig;
// _thisCfgProperties = configProperties [_thisConfig, "!isClass _x"];
// _saveHash = createHashMap;
// {
// _propertyCfg = _x;
// _saveHash set [configName _propertyCfg, (_propertyCfg) call BIS_fnc_getCfgData];
// } forEach _thisCfgProperties;
// _hierarchy = (configHierarchy _thisConfig);
// _hierarchy deleteAt 0;
// _hierarchy = _hierarchy apply {configName _x};
// _hierarchyStr = _hierarchy joinString ".";
// _hierarchyStrParent = (_hierarchy select [0, count _hierarchy - 2]) joinString ".";
// systemChat _hierarchyStrParent;
// // if (_cfgProperties get _hierarchyStrParent == nil) then {
// // _cfgProperties set [_hierarchyStrParent, createHashMap];
// // };
// _cfgProperties set [_hierarchyStr, _saveHash];
// // _cfgProperties set [_hierarchy, _saveHash];
// _nextCfgClasses append _thisConfigClasses;
// } forEach _nextCfgClasses;
// _nextCfgClasses = _nextCfgClasses - _cfgClasses;
// };
// text ([_cfgProperties] call RangerMetrics_fnc_encodeJSON);
// iterate through _cfgProperties hashmap and queue metrics
// {
// } forEach _cfgProperties;

View File

@@ -0,0 +1,22 @@
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],
["string", "playerUID", _playerUID],
["string", "profileName", _profileName],
["string", "displayName", _displayName],
["string", "steamName", _steamName],
["bool", "isHC", _isHC],
["bool", "jip", _jip]
];
if (!isNil "_unit") then {
private _roleDescription = roleDescription _unit;
if (_roleDescription isNotEqualTo "") then {
_fields pushBack ["string", "roleDescription", _roleDescription];
};
};
["player_state", "player_identity", [], _fields] call RangerMetrics_fnc_queue;

View File

@@ -0,0 +1,8 @@
params ["_playerID", "_ownerId", "_playerUID", "_profileName", "_displayName", "_steamName", "_clientState", "_isHC", "_adminState", "_networkInfo", "_unit"];
_networkInfo params ["_avgPing", "_avgBandwidth", "_desync"];
["player_state", "player_performance", [], [
["float", "avgPing", _avgPing],
["float", "avgBandwidth", _avgBandwidth],
["float", "desync", _desync]
]] call RangerMetrics_fnc_queue;

View File

@@ -0,0 +1,7 @@
params ["_playerID", "_ownerId", "_playerUID", "_profileName", "_displayName", "_steamName", "_clientState", "_isHC", "_adminState", "_networkInfo", "_unit"];
// _networkInfo params ["_avgPing", "_avgBandwidth", "_desync"];
["player_state", "player_status", [], [
["int", "clientStateNumber", _clientState],
["int", "adminState", _adminState]
]] call RangerMetrics_fnc_queue;

View File

@@ -0,0 +1,20 @@
// Mission name
[
"server_state", // bucket to store the data
"mission_name", // measurement classifier inside of bucket
nil, // tags
[ // fields
[
"string",
"onLoadName",
[
getMissionConfigValue ["onLoadName", ""],
" ",
"\ "
] call RangerMetrics_fnc_stringReplace
],
["string","briefingName", briefingName],
["string","missionName", missionName],
["string","missionNameSource", missionNameSource]
]
] call RangerMetrics_fnc_queue;

View File

@@ -0,0 +1,7 @@
["server_state", "running_scripts", nil, [
["int", "spawn", diag_activeScripts select 0],
["int", "execVM", diag_activeScripts select 1],
["int", "exec", diag_activeScripts select 2],
["int", "execFSM", diag_activeScripts select 3],
["int", "pfh", if (RangerMetrics_cbaPresent) then {count CBA_common_perFrameHandlerArray} else {0}]
]] call RangerMetrics_fnc_queue;

View File

@@ -0,0 +1,4 @@
["server_state", "server_performance", nil, [
["float", "avg", diag_fps toFixed 2],
["float", "min", diag_fpsMin toFixed 2]
]] call RangerMetrics_fnc_queue;

View File

@@ -0,0 +1,6 @@
["server_state", "server_time", nil, [
["float", "diag_tickTime", diag_tickTime toFixed 2],
["float", "serverTime", time toFixed 2],
["float", "timeMultiplier", timeMultiplier toFixed 2],
["float", "accTime", accTime toFixed 2]
]] call RangerMetrics_fnc_queue;

View File

@@ -0,0 +1,243 @@
@startuml classDiagram
class server_state {
BUCKET
}
server_state --> running_mission
class running_mission {
tag string profileName
tag string connectedServer
field string onLoadName
field string missionName
field string missionNameSource
field string briefingName
}
server_state --> time
class time {
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
}
server_state --> entities_local
class entities_local {
tag string profileName
tag string connectedServer
field int units_alive
field int units_dead
field int vehicles_total
field int groups_total
}
server_state --> entities_global
class entities_global {
tag string profileName
tag string connectedServer
field int units_alive
field int units_dead
field int vehicles_total
field int groups_total
}
server_state --> server_performance
class server_performance {
tag string profileName
tag string connectedServer
field string fps_avg
field string fps_min
}
server_state --> weather
class weather {
tag string profileName
tag string connectedServer
field string fog
field string overcast
field string rain
field string humidity
field string waves
field string windDir
field string windStr
field string gusts
field string lightnings
field string moonIntensity
field string moonPhase
field string sunOrMoon
}
class config_state {
tag string profileName
tag string connectedServer
Measurement mission_config_file
Measurement addon_options
Measurement mission_parameters
Measurement visual_settings
}
config_state --> mission_config_file
class mission_config_file {
tag string profileName
tag string connectedServer
tag string category [
mission_info
respawn
player_ui
corpse_and_wreck
mission_settings
]
}
' link fields in each category
mission_config_file --> mission_info
class mission_info {
field string author
field string onLoadName
field string onLoadMission
field string loadScreen
' field string header
field string gameType
field int minPlayers
field int maxPlayers
field int onLoadIntro
field int onLoadMissionTime
field int onLoadIntroTime
field string briefingName
field string overviewPicture
field string overviewText
field string overviewTextLocked
}
mission_config_file --> respawn
class respawn {
field string respawn
field string respawnButton
field string respawnDelay
field string respawnVehicleDelay
field string respawnDialog
field string respawnOnStart
field string respawnTemplates
field string respawnTemplatesWest
field string respawnTemplatesEast
field string respawnTemplatesGuer
field string respawnTemplatesCiv
field string respawnWeapons
field string respawnMagazines
field int reviveMode
field int reviveUnconsciousStateMode
field int reviveRequiredTrait
field int reviveRequiredItems
field int reviveRequiredItemsFakConsumed
field int reviveMedicSpeedMultiplier
field int reviveDelay
field int reviveForceRespawnDelay
field int reviveBleedOutDelay
field int enablePlayerAddRespawn
}
mission_config_file --> player_ui
class player_ui {
field int overrideFeedback
field int showHUD
field int showCompass
field int showGPS
field int showGroupIndicator
field int showMap
field int showNotePad
field int showPad
field int showWatch
field int showUAVFeed
field int showSquadRadar
}
mission_config_file --> corpse_and_wreck
class corpse_and_wreck {
field int corpseManagerMode
field int corpseLimit
field int corpseRemovalMinTime
field int corpseRemovalMaxTime
field int wreckManagerMode
field int wreckLimit
field int wreckRemovalMinTime
field int wreckRemovalMaxTime
field int minPlayerDistance
}
mission_config_file --> mission_settings
class mission_settings {
field int aiKills
field int briefing
field int debriefing
field string disableChannels
field int disabledAI
field string disableRandomization
field List~string~ enableDebugConsole
field int enableItemsDropping
field int enableTeamSwitch
field int forceRotorLibSimulation
field int joinUnassigned
field int minScore
field int avgScore
field int maxScore
field string onCheat
field string onPauseScript
field int saving
field int scriptedPlayer
field int skipLobby
field int HostDoesNotSkipLobby
field string missionGroup
}
config_state --> visual_settings
class visual_settings {
field string getTIParameters
field string objectViewDistance
}
class player_state {
Measurement identity
Measurement status
}
player_state --> player_identity
class player_identity {
field string playerID
field string ownerId
field string playerUID
field string profileName
field string displayName
field string steamName
bool string isHC
}
player_state --> player_status
class player_status {
field int clientStateNumber
field int adminState
}
player_state --> player_performance
class player_performance {
field float avgPing
field float avgBandwidth
field float desync
}
@enduml