mirror of
https://github.com/valmojr/armatak.git
synced 2026-06-13 23:23:30 +00:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10d0b6f86c | ||
|
|
db16d94596 | ||
|
|
9fc7a0e1bd | ||
|
|
f9b5860f9b | ||
|
|
51b1a06e6c | ||
|
|
63c60bc81a | ||
|
|
10683d9e2f | ||
|
|
871d6ec9d3 | ||
|
|
6eca9e92b6 | ||
|
|
a8f6b04221 | ||
|
|
7831b25062 | ||
|
|
c490483abb | ||
|
|
1719a16894 | ||
|
|
8e4b967f7d | ||
|
|
91eeee9989 | ||
|
|
48512f587b | ||
|
|
ddf60c439c | ||
|
|
8e1fc2170a | ||
|
|
25628016ee | ||
|
|
2ac29f50a6 |
@@ -4,10 +4,10 @@ name = "ARMA Team Awareness Kit"
|
|||||||
prefix = "armatak"
|
prefix = "armatak"
|
||||||
|
|
||||||
[version]
|
[version]
|
||||||
build = 0
|
build = 9
|
||||||
major = 0
|
major = 0
|
||||||
minor = 9
|
minor = 9
|
||||||
patch = 0
|
patch = 9
|
||||||
|
|
||||||
git_hash = 0
|
git_hash = 0
|
||||||
|
|
||||||
@@ -44,7 +44,22 @@ workshop = [
|
|||||||
"1779063631", # Zeus enhanced
|
"1779063631", # Zeus enhanced
|
||||||
"1673595418", # User Input Menus
|
"1673595418", # User Input Menus
|
||||||
"1678581937", # Extended Function Viewer
|
"1678581937", # Extended Function Viewer
|
||||||
"1231625987" #Debug Console
|
"1231625987", #Debug Console
|
||||||
|
"751965892", # ACRE2
|
||||||
|
"843577117", # RHSUSAF
|
||||||
|
"843425103", # RHSAFRF
|
||||||
|
"843593391", # RHSGREF
|
||||||
|
"735566597", # Project OPFOR
|
||||||
|
"3030830594", # Western Dusk
|
||||||
|
"3147473073", # TOTT Core
|
||||||
|
"3147480843", # TOTT CAG
|
||||||
|
"3147476552", # TOTT Optics
|
||||||
|
"2975268929", # GPNVG
|
||||||
|
"583496184", # CUP Terrains
|
||||||
|
"421620913", # Kunduz
|
||||||
|
"2397360831", # USAF Core
|
||||||
|
"2397376046", # USAF Utility
|
||||||
|
"2226368165", # USAF AC130
|
||||||
]
|
]
|
||||||
|
|
||||||
parameters = [
|
parameters = [
|
||||||
|
|||||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -83,7 +83,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "armatak"
|
name = "armatak"
|
||||||
version = "0.9.0"
|
version = "0.9.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arma-rs",
|
"arma-rs",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "armatak"
|
name = "armatak"
|
||||||
version = "0.9.0"
|
version = "0.9.9"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ class CfgFunctions {
|
|||||||
class init {
|
class init {
|
||||||
file = "\armatak\armatak\armatak_main\functions\fn_init.sqf";
|
file = "\armatak\armatak\armatak_main\functions\fn_init.sqf";
|
||||||
};
|
};
|
||||||
|
class video_init {
|
||||||
|
file = "\armatak\armatak\armatak_main\functions\fn_video_init.sqf";
|
||||||
|
};
|
||||||
class log_message {
|
class log_message {
|
||||||
file = "\armatak\armatak\armatak_main\functions\fn_log_message.sqf";
|
file = "\armatak\armatak\armatak_main\functions\fn_log_message.sqf";
|
||||||
};
|
};
|
||||||
@@ -43,6 +46,10 @@ class CfgFunctions {
|
|||||||
class extract_uuid {
|
class extract_uuid {
|
||||||
file = "\armatak\armatak\armatak_main\functions\extract_data\fn_extract_uuid.sqf";
|
file = "\armatak\armatak\armatak_main\functions\extract_data\fn_extract_uuid.sqf";
|
||||||
};
|
};
|
||||||
|
class shorten_name {
|
||||||
|
file = "\armatak\armatak\armatak_main\functions\extract_data\fn_shorten_name.sqf";
|
||||||
|
};
|
||||||
|
|
||||||
class convert_location {
|
class convert_location {
|
||||||
file = "\armatak\armatak\armatak_main\functions\map\fn_convert_location.sqf";
|
file = "\armatak\armatak\armatak_main\functions\map\fn_convert_location.sqf";
|
||||||
};
|
};
|
||||||
@@ -61,6 +68,9 @@ class CfgFunctions {
|
|||||||
class convert_to_cucui {
|
class convert_to_cucui {
|
||||||
file = "\armatak\armatak\armatak_main\functions\map\fn_convert_to_cucui.sqf";
|
file = "\armatak\armatak\armatak_main\functions\map\fn_convert_to_cucui.sqf";
|
||||||
};
|
};
|
||||||
|
class convert_to_kunduz {
|
||||||
|
file = "\armatak\armatak\armatak_main\functions\map\fn_convert_to_kunduz.sqf";
|
||||||
|
};
|
||||||
class convert_to_livonia {
|
class convert_to_livonia {
|
||||||
file = "\armatak\armatak\armatak_main\functions\map\fn_convert_to_livonia.sqf";
|
file = "\armatak\armatak\armatak_main\functions\map\fn_convert_to_livonia.sqf";
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -49,9 +49,6 @@ class CfgVehicles {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class Attributes: AttributesBase {
|
class Attributes: AttributesBase {
|
||||||
class Units: Units {
|
|
||||||
property = "armatak_module_attached_units";
|
|
||||||
};
|
|
||||||
class armatak_module_tak_server_instance_address: Edit {
|
class armatak_module_tak_server_instance_address: Edit {
|
||||||
property = "armatak_module_tak_server_instance_address";
|
property = "armatak_module_tak_server_instance_address";
|
||||||
displayname = "TAK Server Address";
|
displayname = "TAK Server Address";
|
||||||
@@ -64,7 +61,86 @@ class CfgVehicles {
|
|||||||
displayname = "TAK Server TCP Port";
|
displayname = "TAK Server TCP Port";
|
||||||
tooltip = "TAK Server instance Port for TCP connection";
|
tooltip = "TAK Server instance Port for TCP connection";
|
||||||
typeName = "NUMBER";
|
typeName = "NUMBER";
|
||||||
defaultValue = "8080";
|
defaultValue = "8088";
|
||||||
|
};
|
||||||
|
class ModuleDescription: ModuleDescription {};
|
||||||
|
};
|
||||||
|
|
||||||
|
class ModuleDescription: ModuleDescription {
|
||||||
|
description = "Generate the initial ARMATAK configuration, syncronizing all players to the TAK server instance";
|
||||||
|
sync[] = {"LocationArea_F"};
|
||||||
|
|
||||||
|
class LocationArea_F {
|
||||||
|
description[] = {
|
||||||
|
"First line",
|
||||||
|
"Second line"
|
||||||
|
};
|
||||||
|
position = 1;
|
||||||
|
direction = 1;
|
||||||
|
optional = 1;
|
||||||
|
duplicate = 1;
|
||||||
|
synced[] = { "BluforUnit", "AnyBrain" };
|
||||||
|
};
|
||||||
|
class BluforUnit
|
||||||
|
{
|
||||||
|
description = "Short description";
|
||||||
|
displayName = "Any BLUFOR unit";
|
||||||
|
icon = "iconMan";
|
||||||
|
side = 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
class armatak_module_video_stream_core: armatak_module_core {
|
||||||
|
scope = 2;
|
||||||
|
displayname = "ARMATAK MediaMTX Video Feed Parser";
|
||||||
|
icon = "\a3\Modules_F_Curator\Data\iconcuratorsetcamera_ca.paa";
|
||||||
|
category = "armatak_module_category";
|
||||||
|
function = "armatak_fnc_video_init";
|
||||||
|
functionPriority = 1;
|
||||||
|
isGlobal = 0;
|
||||||
|
isTriggerActivated = 0;
|
||||||
|
isDisposable = 1;
|
||||||
|
is3den = 0;
|
||||||
|
curatorCanAttach = 0;
|
||||||
|
curatorInfoType = "RscDisplayAttributeModuleNuke";
|
||||||
|
|
||||||
|
canSetArea = 0;
|
||||||
|
canSetAreaShape = 0;
|
||||||
|
canSetAreaHeight = 0;
|
||||||
|
|
||||||
|
class AttributesValues {
|
||||||
|
size3[] = { 1, 1, -1 };
|
||||||
|
isRectangle = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Attributes: AttributesBase {
|
||||||
|
class armatak_module_mediamtx_video_stream_instance_address: Edit {
|
||||||
|
property = "armatak_module_mediamtx_video_stream_instance_address";
|
||||||
|
displayname = "MediaMTX Provider Address";
|
||||||
|
tooltip = "MediaMTX Provider Instance Address";
|
||||||
|
typeName = "STRING";
|
||||||
|
defaultValue = "localhost";
|
||||||
|
};
|
||||||
|
class armatak_module_mediamtx_video_stream_instance_port: Edit {
|
||||||
|
property = "armatak_module_mediamtx_video_stream_instance_port";
|
||||||
|
displayname = "MediaMTX Provider Port";
|
||||||
|
tooltip = "MediaMTX Provider Port for handling video streams";
|
||||||
|
typeName = "STRING";
|
||||||
|
defaultValue = "8554";
|
||||||
|
};
|
||||||
|
class armatak_module_mediamtx_video_stream_instance_auth_user: Edit {
|
||||||
|
property = "armatak_module_mediamtx_video_stream_instance_auth_user";
|
||||||
|
displayname = "MediaMTX Provider Username";
|
||||||
|
tooltip = "MediaMTX Provider Instance Username";
|
||||||
|
typeName = "STRING";
|
||||||
|
defaultValue = "administrator";
|
||||||
|
};
|
||||||
|
class armatak_module_mediamtx_video_stream_instance_auth_pass: Edit {
|
||||||
|
property = "armatak_module_mediamtx_video_stream_instance_auth_pass";
|
||||||
|
displayname = "MediaMTX Provider Password";
|
||||||
|
tooltip = "MediaMTX Provider Instance Password";
|
||||||
|
typeName = "STRING";
|
||||||
|
defaultValue = "password";
|
||||||
};
|
};
|
||||||
class ModuleDescription: ModuleDescription {};
|
class ModuleDescription: ModuleDescription {};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ params["_unit"];
|
|||||||
private _callsign = "";
|
private _callsign = "";
|
||||||
|
|
||||||
if (roleDescription _unit != "") then {
|
if (roleDescription _unit != "") then {
|
||||||
_callsign = name _unit + " | " + roleDescription _unit;
|
_callsign = ([name _unit] call armatak_fnc_shorten_name) + " | " + roleDescription _unit;
|
||||||
} else {
|
} else {
|
||||||
_callsign = name _unit;
|
_callsign = name _unit;
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ if ((([_unit] call BIS_fnc_objectType) select 0) == "Vehicle") then {
|
|||||||
_callsign = getText (configFile >> "CfgVehicles" >> typeOf _unit >> "displayName");
|
_callsign = getText (configFile >> "CfgVehicles" >> typeOf _unit >> "displayName");
|
||||||
|
|
||||||
if (!isNull driver _unit) then {
|
if (!isNull driver _unit) then {
|
||||||
_callsign = getText (configFile >> "CfgVehicles" >> typeOf _unit >> "displayName") + " | " + name (driver _unit);
|
_callsign = getText (configFile >> "CfgVehicles" >> typeOf _unit >> "displayName") + " | " + ([name (driver _unit)] call armatak_fnc_shorten_name);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -34,10 +34,10 @@ if (unitIsUAV _unit) then {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
_pre_defined_callsign = _unit getVariable "_atak_callsign";
|
_atak_pre_defined_callsign = _unit getVariable "_atak_callsign";
|
||||||
|
|
||||||
if (!isNil "_pre_defined_callsign") then {
|
if (!isNil "_atak_pre_defined_callsign") then {
|
||||||
_callsign = _pre_defined_callsign;
|
_callsign = _atak_pre_defined_callsign;
|
||||||
};
|
};
|
||||||
|
|
||||||
_callsign
|
_callsign
|
||||||
17
addons/main/functions/extract_data/fn_shorten_name.sqf
Normal file
17
addons/main/functions/extract_data/fn_shorten_name.sqf
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
params ["_fullName"];
|
||||||
|
|
||||||
|
private _nameParts = _fullName splitString " ";
|
||||||
|
private _nameCount = count _nameParts;
|
||||||
|
|
||||||
|
if (_nameCount == 1) then {
|
||||||
|
_fullName
|
||||||
|
} else {
|
||||||
|
private _firstName = _nameParts select 0;
|
||||||
|
private _lastName = _nameParts select (_nameCount - 1);
|
||||||
|
|
||||||
|
if ((count _lastName) >= 7) then {
|
||||||
|
_lastName
|
||||||
|
} else {
|
||||||
|
format ["%1 %2", _firstName select [0, 1], _lastName]
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -12,7 +12,22 @@ if (isServer) exitWith {
|
|||||||
params ["_name", "_function", "_data"];
|
params ["_name", "_function", "_data"];
|
||||||
|
|
||||||
if (_name == "armatak_tcp_socket") then {
|
if (_name == "armatak_tcp_socket") then {
|
||||||
_warning = format ["<t color='#FF8021'>ARMATAK</t><br/> %1", _function];
|
_warning = format ["<t color='#00FF21'>ARMATAK</t><br/> %1", _function];
|
||||||
|
[[_warning, 1.5]] call CBA_fnc_notify;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_name == "armatak_tcp_socket_error") then {
|
||||||
|
_warning = format ["<t color='#FF0021'>ARMATAK</t><br/> %1", _function];
|
||||||
|
[[_warning, 1.5]] call CBA_fnc_notify;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_name == "armatak_video") then {
|
||||||
|
_warning = format ["<t color='#00FF21'>ARMATAK</t><br/> %1", _function];
|
||||||
|
[[_warning, 1.5]] call CBA_fnc_notify;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_name == "armatak_video_error") then {
|
||||||
|
_warning = format ["<t color='#FF0021'>ARMATAK</t><br/> %1", _function];
|
||||||
[[_warning, 1.5]] call CBA_fnc_notify;
|
[[_warning, 1.5]] call CBA_fnc_notify;
|
||||||
};
|
};
|
||||||
}];
|
}];
|
||||||
|
|||||||
73
addons/main/functions/fn_video_init.sqf
Normal file
73
addons/main/functions/fn_video_init.sqf
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
params [
|
||||||
|
["_logic", objNull, [objNull]],
|
||||||
|
["_units", [], [[]]],
|
||||||
|
["_activated", true, [true]]
|
||||||
|
];
|
||||||
|
|
||||||
|
if (isServer) exitWith {
|
||||||
|
armatak_module_mediamtx_video_stream_instance_address = _logic getVariable "armatak_module_mediamtx_video_stream_instance_address";
|
||||||
|
armatak_module_mediamtx_video_stream_instance_port = _logic getVariable "armatak_module_mediamtx_video_stream_instance_port";
|
||||||
|
armatak_module_mediamtx_video_stream_instance_auth_user = _logic getVariable "armatak_module_mediamtx_video_stream_instance_auth_user";
|
||||||
|
armatak_module_mediamtx_video_stream_instance_auth_pass = _logic getVariable "armatak_module_mediamtx_video_stream_instance_auth_pass";
|
||||||
|
|
||||||
|
missionNamespace setVariable ["armatak_mediamtx_video_stream_instance_address", armatak_module_mediamtx_video_stream_instance_address];
|
||||||
|
missionNamespace setVariable ["armatak_mediamtx_video_stream_instance_port", armatak_module_mediamtx_video_stream_instance_port];
|
||||||
|
missionNamespace setVariable ["armatak_mediamtx_video_stream_instance_auth_user", armatak_module_mediamtx_video_stream_instance_auth_user];
|
||||||
|
missionNamespace setVariable ["armatak_mediamtx_video_stream_instance_auth_pass", armatak_module_mediamtx_video_stream_instance_auth_pass];
|
||||||
|
|
||||||
|
_startAction = [
|
||||||
|
"ArmatakStartStream",
|
||||||
|
"Start Video Feed",
|
||||||
|
"",
|
||||||
|
{
|
||||||
|
_uuid = (_this select 0) call armatak_fnc_extract_uuid;
|
||||||
|
|
||||||
|
armatak_mediamtx_video_stream_instance_address = missionNamespace getVariable "armatak_mediamtx_video_stream_instance_address";
|
||||||
|
armatak_mediamtx_video_stream_instance_port = missionNamespace getVariable "armatak_mediamtx_video_stream_instance_port";
|
||||||
|
armatak_mediamtx_video_stream_instance_auth_user = missionNamespace getVariable "armatak_mediamtx_video_stream_instance_auth_user";
|
||||||
|
armatak_mediamtx_video_stream_instance_auth_pass = missionNamespace getVariable "armatak_mediamtx_video_stream_instance_auth_pass";
|
||||||
|
|
||||||
|
"armatak" callExtension ["video_stream:start", [armatak_mediamtx_video_stream_instance_address, armatak_mediamtx_video_stream_instance_port, _uuid, armatak_mediamtx_video_stream_instance_auth_user, armatak_mediamtx_video_stream_instance_auth_pass]];
|
||||||
|
(_this select 0) setVariable ["armatak_video_feed_is_streaming", true];
|
||||||
|
},
|
||||||
|
{
|
||||||
|
(_this select 0) getVariable "armatak_video_feed_is_streaming" == false
|
||||||
|
}
|
||||||
|
] call ace_interact_menu_fnc_createAction;
|
||||||
|
[
|
||||||
|
"Man",
|
||||||
|
1,
|
||||||
|
["ACE_SelfActions"],
|
||||||
|
_startAction,
|
||||||
|
true
|
||||||
|
] call ace_interact_menu_fnc_addActionToClass;
|
||||||
|
|
||||||
|
_stopAction = [
|
||||||
|
"ArmatakStopStream",
|
||||||
|
"Stop Video Feed",
|
||||||
|
"",
|
||||||
|
{
|
||||||
|
"armatak" callExtension ["video_stream:stop", []];
|
||||||
|
(_this select 0) setVariable ["armatak_video_feed_is_streaming", false];
|
||||||
|
},
|
||||||
|
{
|
||||||
|
(_this select 0) getVariable "armatak_video_feed_is_streaming"
|
||||||
|
}
|
||||||
|
] call ace_interact_menu_fnc_createAction;
|
||||||
|
[
|
||||||
|
"Man",
|
||||||
|
1,
|
||||||
|
["ACE_SelfActions"],
|
||||||
|
_stopAction,
|
||||||
|
true
|
||||||
|
] call ace_interact_menu_fnc_addActionToClass;
|
||||||
|
if (isMultiplayer) then {
|
||||||
|
{
|
||||||
|
_x setVariable ["armatak_video_feed_is_streaming", false];
|
||||||
|
} forEach playableUnits;
|
||||||
|
} else {
|
||||||
|
player setVariable ["armatak_video_feed_is_streaming", false];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
true;
|
||||||
@@ -42,11 +42,14 @@ switch (toLower worldName) do {
|
|||||||
_realLocation = _position call armatak_fnc_convert_to_united_sahrani;
|
_realLocation = _position call armatak_fnc_convert_to_united_sahrani;
|
||||||
};
|
};
|
||||||
case "saralite": {
|
case "saralite": {
|
||||||
_realLocation = _position call armatak_fnc_convert_to_united_sahrani;
|
_realLocation = _position call armatak_fnc_convert_to_southen_sahrani;
|
||||||
};
|
};
|
||||||
case "enoch": {
|
case "enoch": {
|
||||||
_realLocation = _position call armatak_fnc_convert_to_livonia;
|
_realLocation = _position call armatak_fnc_convert_to_livonia;
|
||||||
};
|
};
|
||||||
|
case "kunduz": {
|
||||||
|
_realLocation = _position call armatak_fnc_convert_to_kunduz;
|
||||||
|
};
|
||||||
default {
|
default {
|
||||||
_warning = format ["<t color='#FF8021'>ARMATAK</t><br/> %1", "Unsupported Map"];
|
_warning = format ["<t color='#FF8021'>ARMATAK</t><br/> %1", "Unsupported Map"];
|
||||||
[[_warning, 1.5]] call CBA_fnc_notify;
|
[[_warning, 1.5]] call CBA_fnc_notify;
|
||||||
|
|||||||
30
addons/main/functions/map/fn_convert_to_kunduz.sqf
Normal file
30
addons/main/functions/map/fn_convert_to_kunduz.sqf
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
params ["_longitudeInGame", "_latitudeInGame", "_altitude"];
|
||||||
|
|
||||||
|
private _mapWidth = 5120;
|
||||||
|
private _mapHeight = 5120;
|
||||||
|
|
||||||
|
// SW corner (used as origin)
|
||||||
|
private _SW_lat = 36.588437;
|
||||||
|
private _SW_lon = 68.834763;
|
||||||
|
|
||||||
|
// SE corner
|
||||||
|
private _SE_lat = 36.574950;
|
||||||
|
private _SE_lon = 68.899151;
|
||||||
|
|
||||||
|
// NW corner
|
||||||
|
private _NW_lat = 36.640080;
|
||||||
|
private _NW_lon = 68.847941;
|
||||||
|
|
||||||
|
private _edgeSE_lat = _SE_lat - _SW_lat;
|
||||||
|
private _edgeSE_lon = _SE_lon - _SW_lon;
|
||||||
|
|
||||||
|
private _edgeNW_lat = _NW_lat - _SW_lat;
|
||||||
|
private _edgeNW_lon = _NW_lon - _SW_lon;
|
||||||
|
|
||||||
|
private _fx = _longitudeInGame / _mapWidth;
|
||||||
|
private _fy = _latitudeInGame / _mapHeight;
|
||||||
|
|
||||||
|
private _realLat = _SW_lat + (_fx * _edgeSE_lat) + (_fy * _edgeNW_lat);
|
||||||
|
private _realLon = _SW_lon + (_fx * _edgeSE_lon) + (_fy * _edgeNW_lon);
|
||||||
|
|
||||||
|
[_realLat, _realLon, _altitude]
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#define build 0
|
#define build 0
|
||||||
#define major 0
|
#define major 1
|
||||||
#define minor 7
|
#define minor 0
|
||||||
#define patch 5
|
#define patch 0
|
||||||
@@ -32,14 +32,14 @@ impl TcpClient {
|
|||||||
|
|
||||||
let tcp_thread = thread::spawn(move || match TcpStream::connect(&address) {
|
let tcp_thread = thread::spawn(move || match TcpStream::connect(&address) {
|
||||||
Ok(stream) => {
|
Ok(stream) => {
|
||||||
info!("Connected to TCP server at {}", address);
|
let _ = ctx.callback_data("armatak_tcp_socket", "Connected to TCP Server", address);
|
||||||
*connection_clone.lock().unwrap() = Some(stream);
|
*connection_clone.lock().unwrap() = Some(stream);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let _ = ctx.callback_data(
|
let _ = ctx.callback_data(
|
||||||
"armatak_tcp_socket",
|
"armatak_tcp_socket_error",
|
||||||
"tak_socket_failed_connection",
|
"TAK Socket connection failed",
|
||||||
format!("Failed to connect to TCP server: {}", e),
|
e.to_string(),
|
||||||
);
|
);
|
||||||
info!("Failed to connect to TCP server: {}", e);
|
info!("Failed to connect to TCP server: {}", e);
|
||||||
}
|
}
|
||||||
@@ -53,15 +53,17 @@ impl TcpClient {
|
|||||||
info!("Failed to send message: {}", e);
|
info!("Failed to send message: {}", e);
|
||||||
|
|
||||||
let _ = context.callback_data(
|
let _ = context.callback_data(
|
||||||
"armatak_tcp_socket",
|
"armatak_tcp_socket_error",
|
||||||
"tak_socket_disconnected",
|
"TAK Socket disconnected",
|
||||||
e.to_string(),
|
e.to_string(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
running = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let _ = context.callback_null(
|
let _ = context.callback_null(
|
||||||
"armatak_tcp_socket",
|
"armatak_tcp_socket_error",
|
||||||
"tak_socket_not_active",
|
"TAK Socket is not active",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,16 +109,14 @@ pub fn start(ctx: Context, address: String) -> &'static str {
|
|||||||
let mut client_guard = TCP_CLIENT.lock().unwrap();
|
let mut client_guard = TCP_CLIENT.lock().unwrap();
|
||||||
*client_guard = Some(client);
|
*client_guard = Some(client);
|
||||||
|
|
||||||
info!("TCP client started.");
|
|
||||||
|
|
||||||
"Starting TCP Client"
|
"Starting TCP Client"
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_payload(ctx: Context, payload: String) -> &'static str {
|
pub fn send_payload(ctx: Context, payload: String) -> &'static str {
|
||||||
if let Some(ref client) = *TCP_CLIENT.lock().unwrap() {
|
if let Some(ref client) = *TCP_CLIENT.lock().unwrap() {
|
||||||
info!("Sending payload: {}", payload);
|
|
||||||
client.send_payload(ctx, payload);
|
client.send_payload(ctx, payload);
|
||||||
} else {
|
} else {
|
||||||
|
let _ = ctx.callback_null("armatak_tcp_socket_error", "TCP Client is not running");
|
||||||
info!("TCP client is not running.");
|
info!("TCP client is not running.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,12 +144,12 @@ pub fn send_digital_pointer_cot(ctx: Context, cursor_over_time: DigitalPointerPa
|
|||||||
"Sending Digital Pointer Cursor Over Time to TCP server"
|
"Sending Digital Pointer Cursor Over Time to TCP server"
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop() -> &'static str {
|
pub fn stop(ctx: Context) -> &'static str {
|
||||||
if let Some(ref client) = *TCP_CLIENT.lock().unwrap() {
|
if let Some(ref client) = *TCP_CLIENT.lock().unwrap() {
|
||||||
client.stop();
|
client.stop();
|
||||||
info!("TCP client stopped.");
|
let _ = ctx.callback_null("armatak_tcp_socket", "TCP client stopped");
|
||||||
} else {
|
} else {
|
||||||
info!("TCP client is not running.");
|
let _ = ctx.callback_null("armatak_tcp_socket_error", "TCP client is not running");
|
||||||
}
|
}
|
||||||
|
|
||||||
"Stopping TCP Client"
|
"Stopping TCP Client"
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ mod websocket;
|
|||||||
mod util;
|
mod util;
|
||||||
mod cot_router;
|
mod cot_router;
|
||||||
mod cot_generator;
|
mod cot_generator;
|
||||||
|
mod video_stream;
|
||||||
|
|
||||||
#[arma]
|
#[arma]
|
||||||
pub fn init() -> Extension {
|
pub fn init() -> Extension {
|
||||||
@@ -49,5 +50,11 @@ pub fn init() -> Extension {
|
|||||||
.command("send_digital_pointer_cot", cot_router::send_digital_pointer_cot)
|
.command("send_digital_pointer_cot", cot_router::send_digital_pointer_cot)
|
||||||
.command("stop", cot_router::stop)
|
.command("stop", cot_router::stop)
|
||||||
)
|
)
|
||||||
|
.group(
|
||||||
|
"video_stream",
|
||||||
|
Group::new()
|
||||||
|
.command("start", video_stream::start_stream)
|
||||||
|
.command("stop", video_stream::stop_stream)
|
||||||
|
)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
|
|||||||
110
src/video_stream.rs
Normal file
110
src/video_stream.rs
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
use arma_rs::Context;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use std::process::Command;
|
||||||
|
use std::sync::mpsc::{self, Receiver, Sender};
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
use std::os::windows::process::CommandExt;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref STREAM_CTRL: Mutex<Option<Sender<()>>> = Mutex::new(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||||
|
|
||||||
|
pub fn start_stream(
|
||||||
|
ctx: Context,
|
||||||
|
address: String,
|
||||||
|
port: String,
|
||||||
|
stream_path: String,
|
||||||
|
username: String,
|
||||||
|
password: String,
|
||||||
|
) -> &'static str {
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
ctx.callback_null(
|
||||||
|
"armatak_video_error",
|
||||||
|
"Screen capture is only supported on Windows",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
{
|
||||||
|
let (tx, rx): (Sender<()>, Receiver<()>) = mpsc::channel();
|
||||||
|
let rtsp_url = format!(
|
||||||
|
"rtsp://{}:{}@{}:{}/{}",
|
||||||
|
username, password, address, port, stream_path
|
||||||
|
);
|
||||||
|
let rtsp_url_clone = rtsp_url.clone();
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
|
let mut cmd = Command::new("ffmpeg");
|
||||||
|
|
||||||
|
cmd.args(&[
|
||||||
|
"-f",
|
||||||
|
"gdigrab",
|
||||||
|
"-i",
|
||||||
|
"desktop",
|
||||||
|
"-f",
|
||||||
|
"rtsp",
|
||||||
|
"-rtsp_transport",
|
||||||
|
"tcp",
|
||||||
|
&rtsp_url_clone,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let mut child = cmd.creation_flags(CREATE_NO_WINDOW).spawn().unwrap();
|
||||||
|
|
||||||
|
if rx.recv().is_err() {
|
||||||
|
let _ = ctx.callback_null("armatak_video_error", "Error receiving stop signal");
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = child.kill() {
|
||||||
|
let _ = ctx.callback_data(
|
||||||
|
"armatak_video_error",
|
||||||
|
"Failed to Stop FFmpeg",
|
||||||
|
e.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
match STREAM_CTRL.lock() {
|
||||||
|
Ok(mut lock) => *lock = Some(tx),
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Failed to acquire lock: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"starting video stream"
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stop_stream(ctx: Context) -> &'static str {
|
||||||
|
match STREAM_CTRL.lock() {
|
||||||
|
Ok(mut lock) => {
|
||||||
|
if let Some(tx) = lock.take() {
|
||||||
|
if let Err(e) = tx.send(()) {
|
||||||
|
let _ = ctx.callback_data(
|
||||||
|
"armatak_video_error",
|
||||||
|
"Failed to send stop signal",
|
||||||
|
e.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let _ =
|
||||||
|
ctx.callback_null("armatak_video_error", "Tried to stop a nonexistent stream");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
let _ = ctx.callback_data(
|
||||||
|
"armatak_video_error",
|
||||||
|
"Failed to acquire lock",
|
||||||
|
e.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"stopping video stream"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user