94 Commits

Author SHA1 Message Date
Valmo Trindade
0340a84e9c TEMP removed cfgvehicles 2025-05-22 23:22:14 -03:00
Valmo Trindade
50cf9b10bd TEMP moved CfgVehicles 2025-05-22 23:20:48 -03:00
Valmo Trindade
6e220e5807 changed release drafter cannon to prepare the release but not to do it 2025-05-22 23:14:12 -03:00
Valmo Trindade
c1f61dc4aa Added initial custom marker dialog and fixed core module background 2025-05-22 23:12:36 -03:00
Valmo Trindade
d8e08b8da8 removed group stuff because arma 3 code sucks 2025-05-22 23:12:17 -03:00
Valmo Trindade
1fee414ff0 added condition for adding groups into cot router 2025-05-22 03:34:11 -03:00
Valmo Trindade
b41008df8b improved curator core module gui, still missing background color 2025-05-22 03:24:47 -03:00
Valmo Trindade
3d775db2cc removed core module logic after use 2025-05-22 03:24:24 -03:00
Valmo Trindade
7b5510698e refactored video streaming module 2025-05-22 03:24:10 -03:00
Valmo Trindade
3f028b48c8 Improved server module defines 2025-05-21 20:04:33 -03:00
Valmo Trindade
aa7b27ac2e Added functions for adding and removing entities into the marked entitis gvar for the CoT Router 2025-05-21 20:03:59 -03:00
Valmo Trindade
2d207965db removed unused funcs 2025-05-20 21:42:15 -03:00
Valmo Trindade
2e2a321006 Added Mark Entity curator module to add entities in game 2025-05-20 21:40:27 -03:00
Valmo Trindade
67fa1cfe52 added synced units as global variables 2025-05-20 01:49:17 -03:00
Valmo Trindade
730440f4ac fixed position convert function calls 2025-05-20 01:48:55 -03:00
Valmo Trindade
1d65705aa8 Refactored server addon Cfgvehicle to allow core module to work on zeus 2025-05-19 23:35:12 -03:00
Valmo Trindade
f04c0141ef Moved dialog window for TCP Socket connection startup to server addon 2025-05-19 23:34:18 -03:00
Valmo Trindade
e06c199816 fixed CfgPatches modules 2025-05-19 22:54:26 -03:00
Valmo Trindade
94c7752844 Removed CfgVehicles from main addon 2025-05-19 21:58:23 -03:00
Valmo Trindade
9bdb4770c9 added notify func to cot router function 2025-05-19 21:57:32 -03:00
Valmo Trindade
98e63a7152 Rollbacked Client MEHs to the main addon to center all MEH 2025-05-18 17:58:25 -03:00
Valmo Trindade
3a179ea958 Fixed GVAR external calls 2025-05-17 23:24:30 -03:00
Valmo Trindade
af15a44fb6 Moved Server and video info from main to its own addons 2025-05-17 23:15:02 -03:00
Valmo Trindade
ac18c45bef Added MEH for EUD connection to set vars on players 2025-05-17 22:11:05 -03:00
Valmo Trindade
65b8aaebe3 removed video function from main 2025-05-17 22:10:47 -03:00
Valmo Trindade
b3bd58385e Created video addon 2025-05-17 22:10:28 -03:00
Valmo Trindade
29574bc36c Improved callback for EUD connections in websocket 2025-05-17 05:26:36 -03:00
Valmo Trindade
6208286294 linted callback EH logging 2025-05-17 05:02:39 -03:00
Valmo Trindade
2299a3f5c9 linted video streaming callbacks 2025-05-17 05:01:50 -03:00
Valmo Trindade
098de89d95 linted TCP Socket callbacks 2025-05-17 05:01:40 -03:00
Valmo Trindade
8f7d5425ad linted all websocket callbacks 2025-05-17 04:47:01 -03:00
Valmo Trindade
938f0bcb6a Fixed postInit EH classnames 2025-05-17 04:07:03 -03:00
Valmo Trindade
577125120d Added systemChat for unhandled notify calls 2025-05-16 03:35:08 -03:00
Valmo Trindade
5c599877ca Removed callback handler, added XEH stuff to main addon 2025-05-16 03:34:51 -03:00
Valmo Trindade
c1b00cb050 Switched WebSocket start motor to allow callbacks when client connects 2025-05-16 03:33:08 -03:00
Valmo Trindade
319ea9b32a added XEH_postInit to start websocket on client side 2025-05-16 01:11:22 -03:00
Valmo Trindade
eda786931d added initial version of server addon to handle TCP socket into the TAK Server 2025-05-16 00:38:42 -03:00
Valmo Trindade
31f7b5178e changed main addon prefix 2025-05-16 00:25:35 -03:00
Valmo Trindade
8a6dcff05d added notify function for DRY 2025-05-16 00:25:24 -03:00
Valmo Trindade
f2fe8b586c added documentation for convertClientLocation function 2025-05-16 00:23:34 -03:00
Valmo Trindade
bcefc7670f removed padding on websocket router 2025-05-15 23:56:52 -03:00
Valmo Trindade
12c8781236 removed padding on macro call during socketrouter var handling 2025-05-15 23:56:35 -03:00
Valmo Trindade
d81c8fa67b fixed convert location xeh call 2025-05-15 23:56:02 -03:00
Valmo Trindade
6de6a29a09 Added whole websocket handling to the client addon 2025-05-15 23:48:14 -03:00
Valmo Trindade
ffcd4feb4e exploded main addon to divide componenets into addons for each feature 2025-05-15 17:53:25 -03:00
Valmo Trindade
39f4936bf7 added defines for main addon 2025-05-14 05:07:45 -03:00
Valmo Trindade
25549cda25 Added initial zeus core config module function 2025-05-14 05:07:29 -03:00
Valmo Trindade
5231dae17a added CfgMods to main config 2025-05-14 05:07:05 -03:00
Valmo Trindade
f669cc1cc0 added CBA and ACE Macros for better coding xp 2025-05-13 22:36:20 -03:00
Valmo Trindade
43f1cd3bb2 added JSRS to launch mods for cool shootings 2025-05-13 01:11:13 -03:00
Valmo Trindade
ec9d3693bf added initial GUI interface for zeus core module config window 2025-05-13 01:10:56 -03:00
Valmo Trindade
01efe4e01d removed private identifier from global variables 2025-05-13 01:09:51 -03:00
Valmo Trindade
a997c54170 added initial zeus core module functions 2025-05-13 01:09:12 -03:00
Valmo Trindade
68b0fd1e38 added handleCallbacks function for DRY 2025-05-13 01:08:58 -03:00
Valmo Trindade
9c82832a54 updated callsign function calls i cot router loop 2025-05-10 06:18:48 -03:00
Valmo Trindade
d9e7dfe497 changed extract role to handle attribute provided 2025-05-10 05:51:06 -03:00
Valmo Trindade
49afb54b80 removed old callsign function 2025-05-10 05:39:30 -03:00
Valmo Trindade
5d6846c5fa dismantled callsign function from units and markers 2025-05-10 05:38:53 -03:00
Valmo Trindade
9bb4a8feaa added 3den attributes for callsign, group color, roles, and marker types 2025-05-10 05:23:28 -03:00
Valmo Trindade
50b118cbfe called 3den config on main addon config 2025-05-09 18:46:51 -03:00
Valmo Trindade
d11050fcc8 added initial Cfg3den config with callsign and role handlers 2025-05-09 18:46:36 -03:00
Valmo Trindade
3d8baae344 updated tcp scoket handler on SQF functions 2025-05-09 18:29:17 -03:00
Valmo Trindade
1d7cce8409 added stop tcp socket function to check gvars and sto the socket 2025-05-09 18:27:12 -03:00
Valmo Trindade
3f4535cf17 switched cot router anme to tcp socket on extension caller 2025-05-09 18:26:51 -03:00
Valmo Trindade
4412b67d63 updated send_human_cot to send-eud_cot to match new cot generation flow 2025-04-17 06:37:50 -03:00
Valmo Trindade
3128ac0022 Improved code structure on Cursor Over Time generation 2025-04-17 06:37:29 -03:00
Valmo Trindade
b230515675 moved rust util functions to use folder structure 2025-04-16 16:51:21 -03:00
Valmo Trindade
ad2f529345 defined warning in armatak video callback EHs 2025-04-16 16:50:19 -03:00
Valmo Trindade
162f3c7e7a handled RTSP video stream requests without authentication 2025-04-16 05:34:27 -03:00
Valmo Trindade
ca4231f669 improved video stream filepath generation 2025-04-11 02:35:16 -03:00
Valmo Trindade
8334ff4be5 added version 2025-04-10 22:15:12 -03:00
Valmo Trindade
19907184be improved precision on kunduz map 2025-04-10 19:16:49 -03:00
Valmo Trindade
f2ba112ae4 linted PR template 2025-04-08 16:05:27 -03:00
Valmo Trindade
b8bb01e985 improved video feed path generator to use player's name and role 2025-04-07 04:46:53 -03:00
Valmo Trindade
10d0b6f86c separed video stream oss in scopes 2025-04-07 01:28:10 -03:00
Valmo Trindade
db16d94596 added scopes for windows and linux on video stream 2025-04-06 17:53:47 -03:00
Valmo Trindade
9fc7a0e1bd added windows target to video stop callback 2025-04-06 16:46:33 -03:00
Valmo Trindade
f9b5860f9b Added handling for letting only windows calls work and throw a callback when called form a linux on video streams 2025-04-06 16:24:40 -03:00
Valmo Trindade
51b1a06e6c updated project version 2025-04-06 06:03:08 -03:00
Valmo Trindade
63c60bc81a made ffmpeg thread running with no window 2025-04-06 06:02:53 -03:00
Valmo Trindade
10683d9e2f Added function and editor module for handling the video stream paramethers 2025-04-06 05:42:30 -03:00
Valmo Trindade
871d6ec9d3 Added extension calls for starting and stopping a RTSP Video Stream for being used by OTS MEDIAMTX 2025-04-06 05:38:41 -03:00
Valmo Trindade
6eca9e92b6 added loop breaker for Disconnected TCP socket 2025-04-06 05:37:59 -03:00
Valmo Trindade
a8f6b04221 Improved TCP Socket Callbacks 2025-04-06 04:20:25 -03:00
Valmo Trindade
7831b25062 linted tcp socket error callback messages 2025-04-06 04:01:13 -03:00
Valmo Trindade
c490483abb added more extension callback EH for handling error msgs in game 2025-04-06 04:00:26 -03:00
Valmo Trindade
1719a16894 fixed Kunduz map size on convert function 2025-04-03 12:26:35 -03:00
Valmo Trindade
8e4b967f7d improved error callback messages 2025-04-03 12:26:07 -03:00
Valmo Trindade
91eeee9989 Added Kunduz map integration 2025-03-30 23:20:22 -03:00
Valmo Trindade
48512f587b Fixed Southen sahrani convertion call 2025-03-30 23:20:04 -03:00
Valmo Trindade
ddf60c439c switched to v1 2025-03-21 18:52:02 -03:00
Valmo Trindade
8e1fc2170a Changed default port to TCP 8088 2025-03-21 18:51:48 -03:00
Valmo Trindade
25628016ee Merge branch 'short_name' 2025-03-21 18:46:33 -03:00
Valmo Trindade
2ac29f50a6 added function to handle names for vehicles 2025-02-19 17:48:05 -03:00
90 changed files with 4291 additions and 711 deletions

View File

@@ -1,4 +1,5 @@
**When merged this pull request will:** **When merged this pull request will:**
- _Describe what this pull request will do_ - _Describe what this pull request will do_
- _Each change in a separate line_ - _Each change in a separate line_

View File

@@ -122,7 +122,7 @@ jobs:
body: | body: |
Changes in this release: Changes in this release:
${{ env.COMMIT_MESSAGES }} ${{ env.COMMIT_MESSAGES }}
draft: false draft: true
prerelease: false prerelease: false
- name: Upload Release Asset - name: Upload Release Asset
uses: actions/upload-release-asset@v1 uses: actions/upload-release-asset@v1

View File

@@ -5,8 +5,8 @@ prefix = "armatak"
[version] [version]
build = 0 build = 0
major = 0 major = 1
minor = 9 minor = 1
patch = 0 patch = 0
git_hash = 0 git_hash = 0
@@ -44,9 +44,24 @@ 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
"3030830594", # Western Dusk
"2268351256", # Tier One Weapons
"583496184", # CUP Terrains
"583544987", # CUP Maps
"3015795970", # No zoom
"2941986336", # Hatchet Framework
"1745501605", # Hatchet H-60 Pack
"333310405", # Enhanced Moviments
"2034363662", # Enhanced Moviments Rework
"2257686620", # Blastcore Murr
"3407948300", # JSRS Sound Mod
] ]
mission = "armatak_jtac.Mountains_ACR"
parameters = [ parameters = [
"-skipIntro", "-skipIntro",
"-noSplash", "-noSplash",

2
Cargo.lock generated
View File

@@ -83,7 +83,7 @@ dependencies = [
[[package]] [[package]]
name = "armatak" name = "armatak"
version = "0.9.0" version = "1.0.1"
dependencies = [ dependencies = [
"arma-rs", "arma-rs",
"chrono", "chrono",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "armatak" name = "armatak"
version = "0.9.0" version = "1.0.1"
edition = "2021" edition = "2021"
[dependencies] [dependencies]

View File

@@ -0,0 +1 @@
armatak\armatak\addons\client

View File

@@ -0,0 +1,17 @@
class Extended_PreStart_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_SCRIPT(XEH_preStart));
};
};
class Extended_PreInit_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_SCRIPT(XEH_preInit));
};
};
class Extended_PostInit_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_SCRIPT(XEH_postInit));
};
};

View File

@@ -0,0 +1,2 @@
PREP(convertClientLocation);
PREP(extractClientPosition);

View File

@@ -0,0 +1,14 @@
#include "script_component.hpp"
if (!hasInterface) exitWith {};
_local_address = "armatak" callExtension ["local_ip", []] select 0;
"armatak" callExtension ["websocket:start", []];
SETVAR(player,GVAR(localAddress),_local_address);
SETVAR(player,GVAR(eudConnected),false);
[{
"armatak" callExtension ["websocket:location", [player call armatak_client_fnc_extractClientPosition]];
}, 1, []] call CBA_fnc_addPerFrameHandler;

View File

@@ -0,0 +1,9 @@
#include "script_component.hpp"
ADDON = false;
PREP_RECOMPILE_START;
#include "XEH_PREP.hpp"
PREP_RECOMPILE_END;
ADDON = true;

View File

@@ -0,0 +1,3 @@
#include "script_component.hpp"
#include "XEH_PREP.hpp"

19
addons/client/config.cpp Normal file
View File

@@ -0,0 +1,19 @@
#include "script_component.hpp"
class CfgPatches {
class ADDON {
name = COMPONENT_NAME;
units[] = {};
weapons[] = {};
requiredAddons[] = {
"cba_main",
"ace_main",
"armatak_main"
};
requiredVersion = REQUIRED_VERSION;
author = PROJECT_AUTHOR;
url = "https://github.com/valmojr/armatak";
};
};
#include "CfgEventHandlers.hpp"

View File

@@ -1,3 +1,24 @@
#include "..\script_component.hpp"
/*
* Author: Valmo Trindade
* This function is used to convert the position of a unit to the world world location.
*
* Argument:
* 0: in game latitude <NUMBER> is the latitude of the unit.
* 1: in game longitude <NUMBER> is the longitude of the unit.
* 2: in game altitude <NUMBER> is the altitude of the unit.
* 3: in game bearing <NUMBER> is the bearing of the unit.
*
* Return Value:
* ARRAY -> [latitude, longitude, altitude, bearing]
*
* Example:
* [player] call armatak_client_fnc_convertClientLocation;
*
* Public: Yes
*/
params["_latitude", "_longitude", "_altitude"]; params["_latitude", "_longitude", "_altitude"];
_position = [_latitude, _longitude, _altitude]; _position = [_latitude, _longitude, _altitude];
@@ -42,11 +63,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;

View File

@@ -0,0 +1,30 @@
#include "..\script_component.hpp"
/*
* Author: Valmo Trindade
* This function is used to extract the position of a unit and convert it to a format suitable for SIMTAK.
*
* Argument:
* 0: The first argument <OBJECT> is the unit whose position you want to extract.
*
* Return Value:
* ARRAY -> [latitude, longitude, altitude, bearing]
*
* Example:
* [player] call armatak_client_fnc_extractClientPosition;
*
* Public: Yes
*/
params["_unit"];
private _location = (getPos _unit) call FUNC(convertClientLocation);
private _atak_latitude = _location select 0;
private _atak_longitude = _location select 1;
private _atak_altitude = _location select 2;
private _atak_bearing = parseNumber ((getDir _unit) toFixed 0);
_unit_info = [_atak_latitude,_atak_longitude,_atak_altitude,_atak_bearing];
_unit_info

View File

@@ -0,0 +1,17 @@
#define COMPONENT client
#define COMPONENT_BEAUTIFIED WebSocket Client
#include "\armatak\armatak\addons\main\script_mod.hpp"
// #define DEBUG_MODE_FULL
// #define DISABLE_COMPILE_CACHE
// #define ENABLE_PERFORMANCE_COUNTERS
#ifdef DEBUG_ENABLED_CLIENT
#define DEBUG_MODE_FULL
#endif
#ifdef DEBUG_SETTINGS_CLIENT
#define DEBUG_SETTINGS DEBUG_SETTINGS_CLIENT
#endif
#include "\z\ace\addons\main\script_macros.hpp"

View File

@@ -1 +1 @@
armatak\armatak\armatak_main armatak\armatak\addons\main

160
addons/main/Cfg3den.hpp Normal file
View File

@@ -0,0 +1,160 @@
class Cfg3den {
class Object {
class AttributeCategories {
class armatak_3den_attributes {
displayName = "ARMA Team Awareness Kit";
class Attributes {
class armatak_attribute_unit_callsign {
displayName = "Unit Callsign";
tooltip = "Unit callsign on TAK";
property = "armatak_attribute_unit_callsign";
control = "Edit";
expression = "_this setVariable ['armatak_attribute_unit_callsign',_value]";
defaultValue = "''";
condition = "objectBrain";
typeName = "STRING";
};
class armatak_attribute_unit_role {
displayName = "Unit Role";
tooltip = "Unit role on TAK";
property = "armatak_attribute_unit_role";
control = "Combo";
class Values {
class armatak_attribute_unit_role_value_teammember {
name = "Team Member";
value = "Team Member";
default = 1;
};
class armatak_attribute_unit_role_value_teamleader {
name = "Team Leader";
value = "Team Lead";
};
class armatak_attribute_unit_role_value_hq {
name = "Headquarters";
value = "HQ";
};
class armatak_attribute_unit_role_value_sniper {
name = "Sniper";
value = "Sniper";
};
class armatak_attribute_unit_role_value_medic {
name = "Medic";
value = "Medic";
};
class armatak_attribute_unit_role_value_forward_observer {
name = "Forward Observer";
value = "Forward Observer";
};
class armatak_attribute_unit_role_value_rto {
name = "Radio Telephone Operator";
value = "RTO";
};
class armatak_attribute_unit_role_value_k9 {
name = "K9 Operator";
value = "K9";
};
};
expression = "_this setVariable ['%s',_value];";
unique = 0;
condition = "objectBrain";
};
class armatak_attribute_marker_callsign {
displayName = "Marker Callsign";
tooltip = "Marker Callsign on TAK";
property = "armatak_attribute_marker_callsign";
control = "Edit";
expression = "_this setVariable ['armatak_attribute_marker_callsign',_value]";
defaultValue = "''";
condition = "objectVehicle";
typeName = "STRING";
};
class armatak_attribute_marker_type {
displayName = "Marker Type";
tooltip = "Marker type on TAK";
property = "armatak_attribute_marker_type";
control = "Edit";
expression = "_this setVariable ['armatak_attribute_marker_type',_value]";
defaultValue = "''";
condition = "objectVehicle";
typeName = "STRING";
};
};
};
};
};
class Group {
class AttributeCategories {
class armatak_3den_group_attributes {
displayName = "ARMA Team Awareness Kit";
class Attributes {
class armatak_attribute_group_color {
displayName = "Color";
tooltip = "Group on TAK";
property = "armatak_attribute_group_color";
control = "Combo";
class Values {
class armatak_attribute_group_color_value_white {
name = "White";
value = "White";
};
class armatak_attribute_group_color_value_yellow {
name = "Yellow";
value = "Yellow";
};
class armatak_attribute_group_color_value_orange {
name = "Orange";
value = "Orange";
};
class armatak_attribute_group_color_value_magenta {
name = "Magenta";
value = "Magenta";
};
class armatak_attribute_group_color_value_red {
name = "Red";
value = "Red";
};
class armatak_attribute_group_color_value_maroon {
name = "Maroon";
value = "Maroon";
};
class armatak_attribute_group_color_value_purple {
name = "Purple";
value = "Purple";
};
class armatak_attribute_group_color_value_darkblue {
name = "Dark Blue";
value = "DarkBlue";
};
class armatak_attribute_group_color_value_blue {
name = "Blue";
value = "Blue";
};
class armatak_attribute_group_color_value_cyan {
name = "Cyan";
value = "Cyan";
};
class armatak_attribute_group_color_value_teal {
name = "Teal";
value = "Teal";
};
class armatak_attribute_group_color_value_green {
name = "Green";
value = "Green";
};
class armatak_attribute_group_color_value_darkgreen {
name = "Dark Green";
value = "DarkGreen";
};
class armatak_attribute_group_color_value_brown {
name = "Brown";
value = "Brown";
};
};
expression = "_this setVariable ['%s',_value];";
unique = 0;
};
};
};
};
};
};

View File

@@ -0,0 +1,17 @@
class Extended_PreStart_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_SCRIPT(XEH_preStart));
};
};
class Extended_PreInit_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_SCRIPT(XEH_preInit));
};
};
class Extended_PostInit_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_SCRIPT(XEH_postInit));
};
};

View File

@@ -1,89 +1,87 @@
class CfgFunctions { class CfgFunctions {
class armatak { class armatak {
class functions { class functions {
class init {
file = "\armatak\armatak\armatak_main\functions\fn_init.sqf";
};
class log_message {
file = "\armatak\armatak\armatak_main\functions\fn_log_message.sqf";
};
class send_digital_pointer_cot { class send_digital_pointer_cot {
file = "\armatak\armatak\armatak_main\functions\api\fn_send_digital_pointer_cot.sqf"; file = "\armatak\armatak\addons\main\functions\api\fn_send_digital_pointer_cot.sqf";
}; };
class send_drone_cot { class send_drone_cot {
file = "\armatak\armatak\armatak_main\functions\api\fn_send_drone_cot.sqf"; file = "\armatak\armatak\addons\main\functions\api\fn_send_drone_cot.sqf";
}; };
class send_group_cots { class send_group_cots {
file = "\armatak\armatak\armatak_main\functions\api\fn_send_group_cots.sqf"; file = "\armatak\armatak\addons\main\functions\api\fn_send_group_cots.sqf";
}; };
class send_human_cot { class send_eud_cot {
file = "\armatak\armatak\armatak_main\functions\api\fn_send_human_cot.sqf"; file = "\armatak\armatak\addons\main\functions\api\fn_send_eud_cot.sqf";
}; };
class send_marker_cot { class send_marker_cot {
file = "\armatak\armatak\armatak_main\functions\api\fn_send_marker_cot.sqf"; file = "\armatak\armatak\addons\main\functions\api\fn_send_marker_cot.sqf";
}; };
class send_message_cot { class stop_tcp_socket {
file = "\armatak\armatak\armatak_main\functions\api\fn_send_marker_cot.sqf"; file = "\armatak\armatak\addons\main\functions\api\fn_stop_tcp_socket.sqf";
};
class extract_callsign {
file = "\armatak\armatak\armatak_main\functions\extract_data\fn_extract_callsign.sqf";
}; };
class extract_group_color { class extract_group_color {
file = "\armatak\armatak\armatak_main\functions\extract_data\fn_extract_group_color.sqf"; file = "\armatak\armatak\addons\main\functions\extract_data\fn_extract_group_color.sqf";
}; };
class extract_group_role { class extract_group_role {
file = "\armatak\armatak\armatak_main\functions\extract_data\fn_extract_group_role.sqf"; file = "\armatak\armatak\addons\main\functions\extract_data\fn_extract_group_role.sqf";
};
class extract_marker_callsign {
file = "\armatak\armatak\addons\main\functions\extract_data\fn_extract_marker_callsign.sqf";
}; };
class extract_role { class extract_role {
file = "\armatak\armatak\armatak_main\functions\extract_data\fn_extract_role.sqf"; file = "\armatak\armatak\addons\main\functions\extract_data\fn_extract_role.sqf";
}; };
class extract_position { class extract_unit_callsign {
file = "\armatak\armatak\armatak_main\functions\extract_data\fn_extract_position.sqf"; file = "\armatak\armatak\addons\main\functions\extract_data\fn_extract_unit_callsign.sqf";
}; };
class extract_uuid { class extract_uuid {
file = "\armatak\armatak\armatak_main\functions\extract_data\fn_extract_uuid.sqf"; file = "\armatak\armatak\addons\main\functions\extract_data\fn_extract_uuid.sqf";
}; };
class convert_location { class shorten_name {
file = "\armatak\armatak\armatak_main\functions\map\fn_convert_location.sqf"; file = "\armatak\armatak\addons\main\functions\extract_data\fn_shorten_name.sqf";
}; };
class convert_to_altis { class convert_to_altis {
file = "\armatak\armatak\armatak_main\functions\map\fn_convert_to_altis.sqf"; file = "\armatak\armatak\addons\main\functions\map\fn_convert_to_altis.sqf";
}; };
class convert_to_bukovina { class convert_to_bukovina {
file = "\armatak\armatak\armatak_main\functions\map\fn_convert_to_bukovina.sqf"; file = "\armatak\armatak\addons\main\functions\map\fn_convert_to_bukovina.sqf";
}; };
class convert_to_bystrika { class convert_to_bystrika {
file = "\armatak\armatak\armatak_main\functions\map\fn_convert_to_bystrika.sqf"; file = "\armatak\armatak\addons\main\functions\map\fn_convert_to_bystrika.sqf";
}; };
class convert_to_chernarus { class convert_to_chernarus {
file = "\armatak\armatak\armatak_main\functions\map\fn_convert_to_chernarus.sqf"; file = "\armatak\armatak\addons\main\functions\map\fn_convert_to_chernarus.sqf";
}; };
class convert_to_cucui { class convert_to_cucui {
file = "\armatak\armatak\armatak_main\functions\map\fn_convert_to_cucui.sqf"; file = "\armatak\armatak\addons\main\functions\map\fn_convert_to_cucui.sqf";
};
class convert_to_kunduz {
file = "\armatak\armatak\addons\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\addons\main\functions\map\fn_convert_to_livonia.sqf";
}; };
class convert_to_malden { class convert_to_malden {
file = "\armatak\armatak\armatak_main\functions\map\fn_convert_to_malden.sqf"; file = "\armatak\armatak\addons\main\functions\map\fn_convert_to_malden.sqf";
}; };
class convert_to_southen_sahrani { class convert_to_southen_sahrani {
file = "\armatak\armatak\armatak_main\functions\map\fn_convert_to_southen_sahrani.sqf"; file = "\armatak\armatak\addons\main\functions\map\fn_convert_to_southen_sahrani.sqf";
}; };
class convert_to_stratis { class convert_to_stratis {
file = "\armatak\armatak\armatak_main\functions\map\fn_convert_to_stratis.sqf"; file = "\armatak\armatak\addons\main\functions\map\fn_convert_to_stratis.sqf";
}; };
class convert_to_takistan_montains { class convert_to_takistan_montains {
file = "\armatak\armatak\armatak_main\functions\map\fn_convert_to_takistan_montains.sqf"; file = "\armatak\armatak\addons\main\functions\map\fn_convert_to_takistan_montains.sqf";
}; };
class convert_to_tanoa { class convert_to_tanoa {
file = "\armatak\armatak\armatak_main\functions\map\fn_convert_to_tanoa.sqf"; file = "\armatak\armatak\addons\main\functions\map\fn_convert_to_tanoa.sqf";
}; };
class convert_to_united_sahrani { class convert_to_united_sahrani {
file = "\armatak\armatak\armatak_main\functions\map\fn_convert_to_united_sahrani.sqf"; file = "\armatak\armatak\addons\main\functions\map\fn_convert_to_united_sahrani.sqf";
}; };
class convert_to_vr { class convert_to_vr {
file = "\armatak\armatak\armatak_main\functions\map\fn_convert_to_vr.sqf"; file = "\armatak\armatak\addons\main\functions\map\fn_convert_to_vr.sqf";
}; };
}; };
}; };

View File

@@ -1,96 +0,0 @@
class CfgFactionClasses {
class NO_CATEGORY;
class armatak_module_category: NO_CATEGORY {
displayName = "Team Awareness Kit";
};
};
class CfgVehicles {
class Logic;
class Module_F : Logic
{
class AttributesBase
{
class Default;
class Edit; // Default edit box (i.e. text input field)
class Combo; // Default combo box (i.e. drop-down menu)
class Checkbox; // Default checkbox (returned value is Boolean)
class CheckboxNumber; // Default checkbox (returned value is Number)
class ModuleDescription; // Module description
class Units; // Selection of units on which the module is applied
};
class ModuleDescription
{
class AnyBrain;
};
};
class armatak_module_core: Module_F {
scope = 2;
displayname = "ARMATAK CoT Router";
icon = "\a3\Modules_F_Curator\Data\iconRadio_ca.paa";
category = "armatak_module_category";
function = "armatak_fnc_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 Units: Units {
property = "armatak_module_attached_units";
};
class armatak_module_tak_server_instance_address: Edit {
property = "armatak_module_tak_server_instance_address";
displayname = "TAK Server Address";
tooltip = "TAK Server Instance Address";
typeName = "STRING";
defaultValue = "localhost";
};
class armatak_module_tak_server_instance_port: Edit {
property = "armatak_module_tak_server_instance_port";
displayname = "TAK Server TCP Port";
tooltip = "TAK Server instance Port for TCP connection";
typeName = "NUMBER";
defaultValue = "8080";
};
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;
};
};
};
};

2
addons/main/XEH_PREP.hpp Normal file
View File

@@ -0,0 +1,2 @@
PREP(logMessage);
PREP(notify);

View File

@@ -0,0 +1,43 @@
#include "script_component.hpp"
addMissionEventHandler ["ExtensionCallback", {
params ["_name", "_function", "_data"];
if (_name == "WEBSOCKET") then {
[_function, "success", _name] call FUNC(notify);
switch (_function) do {
case "EUD connected": {
SETVAR(player,EGVAR(client,eudConnected),true);
};
case "EUD disconnected": {
SETVAR(player,GVAR(eudConnected),false);
};
default {};
};
};
if (_name == "WEBSOCKET WARNING") then {
[_function, "warning", "WEBSOCKET"] call FUNC(notify);
};
if (_name == "WEBSOCKET ERROR") then {
[_function, "error", _name] call FUNC(notify);
};
if (_name == "TCP SOCKET") then {
[_function, "success", _name] call FUNC(notify);
};
if (_name == "TCP SOCKET ERROR") then {
[_function, "error", _name] call FUNC(notify);
};
if (_name == "VIDEO") then {
[_function, "success", _name] call FUNC(notify);
};
if (_name == "VIDEO ERROR") then {
[_function, "error", _name] call FUNC(notify);
};
}];

View File

@@ -0,0 +1,9 @@
#include "script_component.hpp"
ADDON = false;
PREP_RECOMPILE_START;
#include "XEH_PREP.hpp"
PREP_RECOMPILE_END;
ADDON = true;

View File

@@ -0,0 +1,3 @@
#include "script_component.hpp"
#include "XEH_PREP.hpp"

View File

@@ -1,26 +1,46 @@
#include "script_component.hpp"
class CfgPatches { class CfgPatches {
class armatak_main { class ADDON {
units[] = { name = COMPONENT_NAME;
"armatak_module_core", units[] = {};
"armatak_module_callsign" weapons[] = {};
}; requiredAddons[] = {
weapons[] = {""};
author = "Valmo";
url = "https://github.com/valmojr/armatak";
requiredAddons[] =
{
"cba_main", "cba_main",
"ace_main" "ace_main"
}; };
requiredVersion = 2.06; requiredVersion = REQUIRED_VERSION;
author = PROJECT_AUTHOR;
url = "https://github.com/valmojr/armatak";
}; };
}; };
class Extended_PostInit_EventHandlers { class CfgMods {
class armatak_main { class PREFIX {
init = "call compileScript ['\armatak\armatak\armatak_main\initPlayerLocal.sqf']"; name = "Arma Team Awareness Kit";
author = PROJECT_AUTHOR;
logo = "logo_ca.paa";
logoOver = "logo_hover_ca.paa";
tooltip = "ARMATAK";
tooltipOwned = "ARMATAK";
picture = "picture.paa";
actionName = "GitHub";
action = "https://github.com/valmojr/armatak";
overview = "ARMATAK Addons allows Arma 3 sessions to be parsed to TAK Clients";
hideName = 0;
hidePicture = 0;
dlcColor[] = { 0.23, 0.39, 0.30, 1 };
logoSmall = "logo_small_ca.paa";
}; };
}; };
class CfgFactionClasses {
class NO_CATEGORY;
class GVAR(moduleCategory): NO_CATEGORY {
displayName = "Team Awareness Kit";
};
};
#include "CfgEventHandlers.hpp"
#include "CfgFunctions.hpp" #include "CfgFunctions.hpp"
#include "CfgVehicles.hpp" #include "Cfg3den.hpp"

View File

@@ -10,5 +10,5 @@ if (!isNull _digitalPointer) then {
_dpCot = [_link_uid, _contact_callsign, _digitalPointerPosition select 0, _digitalPointerPosition select 1, _digitalPointerPosition select 2]; _dpCot = [_link_uid, _contact_callsign, _digitalPointerPosition select 0, _digitalPointerPosition select 1, _digitalPointerPosition select 2];
"armatak" callExtension ["cot_router:send_digital_pointer_cot", [_dpCot]]; "armatak" callExtension ["tcp_socket:send_digital_pointer_cot", [_dpCot]];
}; };

View File

@@ -0,0 +1,13 @@
// function name: armatak_fnc_extract_eud_cot_info
// function author: Valmo
// function description: Gets the information necessary for generating the EUD Cursor Over Time
params ["_unit", "_callsign", "_group_name", "_group_role"];
_position = _unit call armatak_client_fnc_extractClientPosition;
_uuid = _unit call armatak_fnc_extract_uuid;
_eud_cot = [_uuid, _position select 0, _position select 1, _position select 2, _callsign, _group_name, _group_role, _position select 3, speed player / 3.6];
"armatak" callExtension ["tcp_socket:send_eud_cot", [_eud_cot]];

View File

@@ -9,5 +9,5 @@ params["_group"];
_group_name = [_group] call armatak_fnc_extract_group_color; _group_name = [_group] call armatak_fnc_extract_group_color;
_group_role = [_x] call armatak_fnc_extract_group_role; _group_role = [_x] call armatak_fnc_extract_group_role;
[_x, _callsign, _group_name, _group_role] call armatak_fnc_send_human_cot; [_x, _callsign, _group_name, _group_role] call armatak_fnc_send_eud_cot;
} forEach (units _group); } forEach (units _group);

View File

@@ -1,12 +0,0 @@
// function name: armatak_fnc_extract_human_cot_info
// function author: Valmo
// function description: Gets the information necessary for generating the Human Cursor Over Time
params ["_unit", "_callsign", "_group_name", "_group_role"];
_position = _unit call armatak_fnc_extract_position;
_uuid = _unit call armatak_fnc_extract_uuid;
_human_cot = [_uuid, _position select 0, _position select 1, _position select 2, _callsign, _group_name, _group_role, _position select 3, speed player / 3.6];
"armatak" callExtension ["cot_router:send_human_cot", [_human_cot]];

View File

@@ -4,9 +4,10 @@
params ["_unit", "_type", "_callsign"]; params ["_unit", "_type", "_callsign"];
_unit_position = _unit call armatak_fnc_extract_position; _unit_position = _unit call armatak_client_fnc_extractClientPosition;
_uuid = _unit call armatak_fnc_extract_uuid; _uuid = _unit call armatak_fnc_extract_uuid;
_marker_cot = [_uuid, _type, _unit_position select 0, _unit_position select 1, _unit_position select 2, _callsign, _unit_position select 3, speed _unit / 3.6]; _marker_cot = [_uuid, _type, _unit_position select 0, _unit_position select 1, _unit_position select 2, _callsign, _unit_position select 3, speed _unit / 3.6];
"armatak" callExtension ["cot_router:send_marker_cot", [_marker_cot]]; "armatak" callExtension ["tcp_socket:send_marker_cot", [_marker_cot]];

View File

@@ -0,0 +1,10 @@
_armatak_tcp_socket_is_running = missionNamespace getVariable "armatak_tcp_socket_is_running";
if (_armatak_tcp_socket_is_running) then {
missionNamespace setVariable ["armatak_tcp_socket_is_running", false];
"armatak" callExtension ["tcp_socket:stop", []];
} else {
_warning = format ["<t color='#FF0021'>ARMATAK</t><br/> %1", "There is no TCP Socket running to be stopped"];
[[_warning, 1.5]] call CBA_fnc_notify;
};

View File

@@ -3,7 +3,7 @@ params["_group"];
_group_name = _group getVariable "_atak_group_name"; _group_name = _group getVariable "_atak_group_name";
if (isNil "_group_name") then { if (isNil "_group_name") then {
_group_colors = missionNamespace getVariable "_group_colors"; _group_colors = missionNamespace getVariable "armatak_group_colors";
_group_name = selectRandom _group_colors; _group_name = selectRandom _group_colors;
if (count _group_colors > 0) then { if (count _group_colors > 0) then {
@@ -15,7 +15,7 @@ if (isNil "_group_name") then {
_group_name = _selectedColor; _group_name = _selectedColor;
_group setVariable ["_atak_group_name", _group_name]; _group setVariable ["_atak_group_name", _group_name];
missionNamespace setVariable ["_group_colors", _group_colors] missionNamespace setVariable ["armatak_group_colors", _group_colors]
} else { } else {
_group_name = "Red"; _group_name = "Red";
_group setVariable ["_atak_group_name", _group_name]; _group setVariable ["_atak_group_name", _group_name];

View File

@@ -1,26 +1,16 @@
// function name: armatak_fnc_extract_callsign // function name: armatak_fnc_extract_marker_callsign
// function author: Valmo // function author: Valmo
// function description: Gets the unit name or classname to be used as TAK callsign // function description: Gets the unit name or classname to be used as TAK callsign in a Marker
params["_unit"]; params["_unit"];
private _callsign = ""; private _callsign = "";
if (roleDescription _unit != "") then {
_callsign = name _unit + " | " + roleDescription _unit;
} else {
_callsign = name _unit;
if (_callsign == "Error: No unit") then {
_callsign = getText (configFile >> "CfgVehicles" >> typeOf _unit >> "displayName");
};
};
if ((([_unit] call BIS_fnc_objectType) select 0) == "Vehicle") then { 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 +24,10 @@ if (unitIsUAV _unit) then {
} }
}; };
_pre_defined_callsign = _unit getVariable "_atak_callsign"; armatak_attribute_marker_callsign = _unit getVariable "armatak_attribute_marker_callsign";
if (!isNil "_pre_defined_callsign") then { if (!isNil "armatak_attribute_marker_callsign" or armatak_attribute_marker_callsign != '') then {
_callsign = _pre_defined_callsign; _callsign = armatak_attribute_marker_callsign;
}; };
_callsign _callsign

View File

@@ -1,16 +0,0 @@
// function name: armatak_fnc_extract_position
// function author: Valmo
// function description: Receives a player's unit as param and return the information array for SIMTAK
params["_unit"];
private _location = (getPos _unit) call armatak_fnc_convert_location;
private _atak_latitude = _location select 0;
private _atak_longitude = _location select 1;
private _atak_altitude = _location select 2;
private _atak_bearing = parseNumber ((getDir _unit) toFixed 0);
_unit_info = [_atak_latitude,_atak_longitude,_atak_altitude,_atak_bearing];
_unit_info

View File

@@ -110,4 +110,11 @@ if ((typeOf (vehicle _unit) != typeOf _unit) or ((_unit_type select 0) == "Vehic
_role = "a-" + _affiliation + "-" + _type; _role = "a-" + _affiliation + "-" + _type;
armatak_attribute_marker_type = _unit getVariable "armatak_attribute_marker_type";
if (!isNil "armatak_attribute_marker_type" or armatak_attribute_marker_type != '') then {
_role = armatak_attribute_marker_type;
};
_role _role

View File

@@ -0,0 +1,25 @@
// function name: armatak_fnc_extract_unit_callsign
// function author: Valmo
// function description: Gets the unit name or classname to be used as TAK callsign from a unit
params["_unit"];
private _callsign = "";
if (roleDescription _unit != "") then {
_callsign = ([name _unit] call armatak_fnc_shorten_name) + " | " + roleDescription _unit;
} else {
_callsign = name _unit;
if (_callsign == "Error: No unit") then {
_callsign = getText (configFile >> "CfgVehicles" >> typeOf _unit >> "displayName");
};
};
armatak_attribute_unit_callsign = _unit getVariable "armatak_attribute_unit_callsign";
if (!isNil "armatak_attribute_unit_callsign" or armatak_attribute_unit_callsign != '') then {
_callsign = armatak_attribute_unit_callsign;
};
_callsign

View 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]
};
};

View File

@@ -1,63 +0,0 @@
params [
["_logic", objNull, [objNull]],
["_units", [], [[]]],
["_activated", true, [true]]
];
if (isServer) exitWith {
_warning = format ["<t color='#FF8021'>ARMATAK</t><br/> %1", "Connecting to TAK Server TCP Socket..."];
[[_warning, 1.5]] call CBA_fnc_notify;
addMissionEventHandler ["ExtensionCallback", {
params ["_name", "_function", "_data"];
if (_name == "armatak_tcp_socket") then {
_warning = format ["<t color='#FF8021'>ARMATAK</t><br/> %1", _function];
[[_warning, 1.5]] call CBA_fnc_notify;
};
}];
_tak_server_instance_address = _logic getVariable "armatak_module_tak_server_instance_address";
_tak_server_instance_port = _logic getVariable "armatak_module_tak_server_instance_port";
_tak_server_fulladdress = _tak_server_instance_address + ":" + (str _tak_server_instance_port);
missionNamespace setVariable ["_atak_server_instance", _tak_server_fulladdress];
missionNamespace setVariable ["_group_colors", ["White", "Yellow", "Orange", "Magenta", "Red", "Maroon", "Purple", "DarkBlue", "Blue", "Cyan", "Teal", "Green", "DarkGreen", "Brown"]];
"armatak" callExtension ["cot_router:start", [_tak_server_fulladdress]];
_syncUnits = synchronizedObjects _logic;
missionNamespace setVariable ["_armatak_marked_units", _syncUnits];
_syncedUnits = missionNamespace getVariable "_armatak_marked_units";
[{
_syncedUnits = missionNamespace getVariable "_armatak_marked_units";
{
_objectType = _x call BIS_fnc_objectType;
if ((_objectType select 0) == "Soldier") then {
_callsign = [_x] call armatak_fnc_extract_callsign;
_group_name = [group _x] call armatak_fnc_extract_group_color;
_group_role = [_x] call armatak_fnc_extract_group_role;
[_x, _callsign, _group_name, _group_role] call armatak_fnc_send_human_cot;
[_x] call armatak_fnc_send_digital_pointer_cot;
};
if ((_objectType select 0) == "Vehicle") then {
_atak_type = [_x] call armatak_fnc_extract_role;
_callsign = [_x] call armatak_fnc_extract_callsign;
[_x, _atak_type, _callsign] call armatak_fnc_send_marker_cot;
};
if (unitIsUAV _x) then {
[_x] call armatak_fnc_send_drone_cot;
[_x] call armatak_fnc_send_digital_pointer_cot;
};
} forEach _syncedUnits;
}, 2, []] call CBA_fnc_addPerFrameHandler;
};
true;

View File

@@ -0,0 +1,21 @@
#include "..\script_component.hpp"
params ["_message", "_type", ["_title", "ARMATAK"]];
switch (_type) do {
case "success": {
_warning = format ["<t color='#00FF21'>ARMATAK - %1</t><br/> %2", _title, _message];
[[_warning, 1.5]] call CBA_fnc_notify;
};
case "warning": {
_warning = format ["<t color='#ffff16'>ARMATAK - %1</t><br/> %2", _title, _message];
[[_warning, 1.5]] call CBA_fnc_notify;
};
case "error": {
_warning = format ["<t color='#FF0021'>ARMATAK - %1</t><br/> %2", _title, _message];
[[_warning, 1.5]] call CBA_fnc_notify;
};
default {
systemChat format ["%1 - %2", _title, _message];
};
};

View File

@@ -0,0 +1,30 @@
params ["_longitudeInGame", "_latitudeInGame", "_altitude"];
private _mapWidth = 5120;
private _mapHeight = 5120;
// SW corner (used as origin)
private _SW_lat = 36.581566;
private _SW_lon = 68.839770;
// SE corner
private _SE_lat = 36.581566;
private _SE_lon = 68.905867;
// NW corner
private _NW_lat = 36.633309;
private _NW_lon = 68.839770;
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]

View File

@@ -1,18 +0,0 @@
if (!hasInterface) exitWith {};
_local_address = "armatak" callExtension ["local_ip", []] select 0;
player setVariable ["localAddress", _local_address];
player addEventHandler ["Respawn", {
params["_unit", "_corpse"];
[{
if (alive _this) then {
"armatak" callExtension ["websocket:location", [player call armatak_fnc_extract_position]];
};
}, 1, []] call CBA_fnc_addPerFrameHandler;
}];
[{
"armatak" callExtension ["websocket:location", [player call armatak_fnc_extract_position]];
}, 1, []] call CBA_fnc_addPerFrameHandler;

View File

@@ -0,0 +1,17 @@
#define COMPONENT main
#define COMPONENT_BEAUTIFIED Main
#include "\armatak\armatak\addons\main\script_mod.hpp"
// #define DEBUG_MODE_FULL
// #define DISABLE_COMPILE_CACHE
// #define ENABLE_PERFORMANCE_COUNTERS
#ifdef DEBUG_ENABLED_MAIN
#define DEBUG_MODE_FULL
#endif
#ifdef DEBUG_SETTINGS_MAIN
#define DEBUG_SETTINGS DEBUG_SETTINGS_MAIN
#endif
#include "\z\ace\addons\main\script_macros.hpp"

View File

@@ -0,0 +1,32 @@
// COMPONENT should be defined in the script_component.hpp and included BEFORE this hpp
#define MAINPREFIX armatak
#define PREFIX armatak
#include "script_version.hpp"
#define VERSION MAJOR.MINOR
#define VERSION_STR MAJOR.MINOR.PATCHLVL.BUILD
#define VERSION_AR MAJOR,MINOR,PATCHLVL,BUILD
#define PROJECT_AUTHOR QUOTE(Valmo Trindade)
// MINIMAL required version for the Mod. Components can specify others..
#define REQUIRED_VERSION 2.18
#define REQUIRED_CBA_VERSION {3,18,2}
#ifndef COMPONENT_BEAUTIFIED
#define COMPONENT_BEAUTIFIED COMPONENT
#endif
#ifdef SUBCOMPONENT_BEAUTIFIED
#define COMPONENT_NAME QUOTE(ARMATAK - COMPONENT_BEAUTIFIED - SUBCOMPONENT_BEAUTIFIED)
#else
#define COMPONENT_NAME QUOTE(ARMATAK - COMPONENT_BEAUTIFIED)
#endif
// Custom ARMATAK MACRO defines
#define EXTENSION_NAME QUOTE(armatak)
#define CALLEXT(var) EXTENSION_NAME callExtension [QUOTE(var),[]]
#define CALLEXTP(var1, var2) EXTENSION_NAME callExtension [QUOTE(var1), var2] select 0

View File

@@ -1,4 +1,4 @@
#define build 0 #define build 0
#define major 0 #define major 1
#define minor 7 #define minor 1
#define patch 5 #define patch 0

View File

@@ -0,0 +1 @@
armatak\armatak\addons\server

View File

@@ -0,0 +1,11 @@
class Extended_PreStart_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_SCRIPT(XEH_preStart));
};
};
class Extended_PreInit_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_SCRIPT(XEH_preInit));
};
};

View File

@@ -0,0 +1,90 @@
class CfgVehicles {
class Logic;
class Module_F : Logic
{
class AttributesBase
{
class Default;
class Edit;
class Combo;
class Checkbox;
class CheckboxNumber;
class ModuleDescription;
class Units;
};
class ModuleDescription
{
class AnyBrain;
};
};
class GVAR(moduleBase): Module_F {
author = PROJECT_AUTHOR;
category = QEGVAR(main,moduleCategory);
function = QUOTE({});
functionPriority = 1;
isGlobal = 1;
isTriggerActivated = 0;
scope = 1;
scopeCurator = 2;
};
class GVAR(coreModule): GVAR(moduleBase) {
scope = 2;
scopeCurator = 0;
displayname = "CoT Router";
icon = "\a3\Modules_F_Curator\Data\iconRadio_ca.paa";
category = QEGVAR(main,moduleCategory);
function = QFUNC(3denCoreModuleConfig);
functionPriority = 1;
isGlobal = 0;
isTriggerActivated = 1;
isDisposable = 1;
is3den = 0;
curatorCanAttach = 0;
curatorInfoType = "RscDisplayAttributeModuleNuke";
canSetArea = 0;
canSetAreaShape = 0;
canSetAreaHeight = 0;
class Attributes: AttributesBase {
class GVAR(moduleInstanceAddress): Edit {
property = QGVAR(moduleInstanceAddress);
displayname = "TAK Server Address";
tooltip = "TAK Server Instance Address";
typeName = "STRING";
defaultValue = "localhost";
};
class GVAR(moduleInstancePort): Edit {
property = QGVAR(moduleInstancePort);
displayname = "TAK Server TCP Port";
tooltip = "TAK Server instance Port for TCP connection";
typeName = "NUMBER";
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 GVAR(coreModuleCurator): GVAR(coreModule) {
scope = 1;
scopeCurator = 2;
function = "";
displayName = "CoT Router (Zeus)";
curatorInfoType = "armatak_zeus_core_module_dialog";
};
class GVAR(markEntity): GVAR(moduleBase) {
curatorCanAttach = 1;
category = QEGVAR(main,moduleCategory);
displayname = "Mark Entity";
function = QFUNC(routerEntityAdd);
icon = "\a3\Modules_F_Curator\Data\iconRadio_ca.paa";
};
};

View File

@@ -0,0 +1,4 @@
PREP(3denCoreModuleConfig);
PREP(routerEntityAdd);
PREP(routerEntityRemove);
PREP(ZeusCoreModuleConfig);

View File

@@ -0,0 +1,9 @@
#include "script_component.hpp"
ADDON = false;
PREP_RECOMPILE_START;
#include "XEH_PREP.hpp"
PREP_RECOMPILE_END;
ADDON = true;

View File

@@ -0,0 +1,3 @@
#include "script_component.hpp"
#include "XEH_PREP.hpp"

25
addons/server/config.cpp Normal file
View File

@@ -0,0 +1,25 @@
#include "script_component.hpp"
class CfgPatches {
class ADDON {
name = COMPONENT_NAME;
units[] = {
QGVAR(coreModule),
QGVAR(coreModuleCurator),
QGVAR(markEntity)
};
weapons[] = {};
requiredAddons[] = {
"cba_main",
"ace_main",
"armatak_main"
};
requiredVersion = REQUIRED_VERSION;
author = PROJECT_AUTHOR;
url = "https://github.com/valmojr/armatak";
};
};
#include "CfgEventHandlers.hpp"
#include "dialog.hpp"
#include "CfgVehicles.hpp"

174
addons/server/dialog.hpp Normal file
View File

@@ -0,0 +1,174 @@
class RscObject;
class RscText;
class RscFrame;
class RscLine;
class RscProgress;
class RscPicture;
class RscBackground;
class RscPictureKeepAspect;
class RscVideo;
class RscHTML;
class RscButton;
class RscShortcutButton;
class RscEdit;
class RscCombo;
class RscListBox;
class RscListNBox;
class RscXListBox;
class RscTree;
class RscSlider;
class RscXSliderH;
class RscActiveText;
class RscActivePicture;
class RscActivePictureKeepAspect;
class RscStructuredText;
class RscToolbox;
class RscControlsGroup;
class RscControlsGroupNoScrollbars;
class RscControlsGroupNoHScrollbars;
class RscControlsGroupNoVScrollbars;
class RscButtonTextOnly;
class RscButtonMenu;
class RscButtonMenuOK;
class RscButtonMenuCancel;
class RscButtonMenuSteam;
class RscMapControl;
class RscMapControlEmpty;
class RscCheckBox;
class armatak_zeus_core_module_dialog {
idd = 999991;
movingEnable = 0;
class ControlsBackground {
class armatak_gui_module_zeus_core_dialog_main_frame: RscBackground {
idc = 1800;
x = "0.386562 * safezoneW + safezoneX";
y = "0.401 * safezoneH + safezoneY";
w = "0.216563 * safezoneW";
h = "0.242 * safezoneH";
colorBackground[]={0,0,0,0.45};
};
};
class Controls {
class armatak_gui_module_zeus_core_dialog_address_edit: RscEdit {
idc = 14000;
text = "localhost";
x = "0.391719 * safezoneW + safezoneX";
y = "0.445 * safezoneH + safezoneY";
w = "0.20625 * safezoneW";
h = "0.044 * safezoneH";
colorBackground[]={0,0,0,0.5};
};
class armatak_gui_module_zeus_core_dialog_address_port_edit: RscEdit {
idc = 14001;
text = "8088";
x = "0.391719 * safezoneW + safezoneX";
y = "0.522 * safezoneH + safezoneY";
w = "0.20625 * safezoneW";
h = "0.044 * safezoneH";
colorBackground[]={0,0,0,0.5};
};
class armatak_gui_module_zeus_core_dialog_address_text: RscText {
idc = 1000;
text = "TAK Server Address";
x = "0.391719 * safezoneW + safezoneX";
y = "0.412 * safezoneH + safezoneY";
w = "0.20625 * safezoneW";
h = "0.033 * safezoneH";
};
class armatak_gui_module_zeus_core_dialog_address_port_text: RscText {
idc = 1001;
text = "TAK Server Port";
x = "0.391719 * safezoneW + safezoneX";
y = "0.489 * safezoneH + safezoneY";
w = "0.20625 * safezoneW";
h = "0.033 * safezoneH";
};
class armatak_gui_module_zeus_core_dialog_address_button_cancel: RscButton {
idc = 1601;
text = "Cancel";
action = "closeDialog 2;";
x = "0.551563 * safezoneW + safezoneX";
y = "0.577 * safezoneH + safezoneY";
w = "0.0464063 * safezoneW";
h = "0.055 * safezoneH";
};
class armatak_gui_module_zeus_core_dialog_address_button_ok: RscButton {
idc = 1600;
text = "Ok";
action = QUOTE(call FUNC(zeusCoreModuleConfig));
x = "0.5 * safezoneW + safezoneX";
y = "0.577 * safezoneH + safezoneY";
w = "0.0464063 * safezoneW";
h = "0.055 * safezoneH";
};
};
};
class armatak_zeus_custom_marker_dialog {
idd = 990991;
movingEnable = 0;
class Controls {
class RscFrame_1800: RscFrame
{
idc = 1800;
x = "0.37625 * safezoneW + safezoneX";
y = "0.357 * safezoneH + safezoneY";
w = "0.237187 * safezoneW";
h = "0.275 * safezoneH";
};
class RscEdit_1400: RscEdit
{
idc = 1400;
x = "0.386562 * safezoneW + safezoneX";
y = "0.423 * safezoneH + safezoneY";
w = "0.216563 * safezoneW";
h = "0.044 * safezoneH";
};
class RscText_1000: RscText
{
idc = 1000;
text = "Entity Callsign";
x = "0.386562 * safezoneW + safezoneX";
y = "0.379 * safezoneH + safezoneY";
w = "0.216563 * safezoneW";
h = "0.044 * safezoneH";
};
class RscText_1001: RscText
{
idc = 1001;
text = "Entity Type (only for vehicles)";
x = "0.386562 * safezoneW + safezoneX";
y = "0.467 * safezoneH + safezoneY";
w = "0.216563 * safezoneW";
h = "0.044 * safezoneH";
};
class RscEdit_1401: RscEdit
{
idc = 1401;
x = "0.386562 * safezoneW + safezoneX";
y = "0.511 * safezoneH + safezoneY";
w = "0.216563 * safezoneW";
h = "0.044 * safezoneH";
};
class RscButton_1600: RscButton
{
idc = 1600;
text = "Cancel";
x = "0.551562 * safezoneW + safezoneX";
y = "0.566 * safezoneH + safezoneY";
w = "0.0515625 * safezoneW";
h = "0.055 * safezoneH";
};
class RscButton_1601: RscButton
{
idc = 1601;
text = "OK";
x = "0.489687 * safezoneW + safezoneX";
y = "0.566 * safezoneH + safezoneY";
w = "0.0515625 * safezoneW";
h = "0.055 * safezoneH";
};
};
};

View File

@@ -0,0 +1,56 @@
#include "..\script_component.hpp"
params [
["_logic", objNull, [objNull]],
["_units", [], [[]]],
["_activated", true, [true]]
];
if (isServer) exitWith {
["Connecting to TCP Socket", "success", "TCP Socket"] call EFUNC(main,notify);
_tak_server_instance_address = _logic getVariable QGVAR(moduleInstanceAddress);
_tak_server_instance_port = _logic getVariable QGVAR(moduleInstancePort);
_tak_server_fulladdress = _tak_server_instance_address + ":" + (str _tak_server_instance_port);
missionNamespace setVariable ["armatak_server_instance", _tak_server_fulladdress];
missionNamespace setVariable ["armatak_tcp_socket_is_running", true];
missionNamespace setVariable ["armatak_group_colors", ["White", "Yellow", "Orange", "Magenta", "Red", "Maroon", "Purple", "DarkBlue", "Blue", "Cyan", "Teal", "Green", "DarkGreen", "Brown"]];
"armatak" callExtension ["tcp_socket:start", [_tak_server_fulladdress]];
_syncUnits = synchronizedObjects _logic;
missionNamespace setVariable ["armatak_marked_units", _syncUnits];
GVAR(syncedUnits) = missionNamespace getVariable "armatak_marked_units";
[{
GVAR(syncedUnits) = missionNamespace getVariable "armatak_marked_units";
{
_objectType = _x call BIS_fnc_objectType;
if ((_objectType select 0) == "Soldier") then {
_callsign = [_x] call armatak_fnc_extract_unit_callsign;
_group_name = [group _x] call armatak_fnc_extract_group_color;
_group_role = [_x] call armatak_fnc_extract_group_role;
[_x, _callsign, _group_name, _group_role] call armatak_fnc_send_eud_cot;
[_x] call armatak_fnc_send_digital_pointer_cot;
};
if ((_objectType select 0) == "Vehicle") then {
_atak_type = [_x] call armatak_fnc_extract_role;
_callsign = [_x] call armatak_fnc_extract_marker_callsign;
[_x, _atak_type, _callsign] call armatak_fnc_send_marker_cot;
};
if (unitIsUAV _x) then {
[_x] call armatak_fnc_send_drone_cot;
[_x] call armatak_fnc_send_digital_pointer_cot;
};
} forEach GVAR(syncedUnits);
}, 2, []] call CBA_fnc_addPerFrameHandler;
};
true;

View File

@@ -0,0 +1,60 @@
#include "..\script_component.hpp"
params ["_logic"];
_socket_is_running = missionNamespace getVariable ["armatak_tcp_socket_is_running", false];
if (_socket_is_running) exitWith {
["Socket was called twice","error", "TCP Socket"] call EFUNC(main,notify);
closeDialog 1;
};
disableSerialization;
["Connecting to TCP Socket", "success", "TCP Socket"] call EFUNC(main,notify);
_tak_server_instance_address = ctrlText 14000;
_tak_server_instance_port = ctrlText 14001;
_tak_server_fulladdress = ((_tak_server_instance_address) + ":" + (_tak_server_instance_port));
missionNamespace setVariable ["armatak_server_instance", _tak_server_fulladdress];
missionNamespace setVariable ["armatak_tcp_socket_is_running", true];
missionNamespace setVariable ["armatak_group_colors", ["White", "Yellow", "Orange", "Magenta", "Red", "Maroon", "Purple", "DarkBlue", "Blue", "Cyan", "Teal", "Green", "DarkGreen", "Brown"]];
"armatak" callExtension ["tcp_socket:start", [_tak_server_fulladdress]];
_syncUnits = [];
missionNamespace setVariable ["armatak_marked_units", _syncUnits];
GVAR(syncedUnits) = missionNamespace getVariable "armatak_marked_units";
[{
GVAR(syncedUnits) = missionNamespace getVariable "armatak_marked_units";
{
_objectType = _x call BIS_fnc_objectType;
if ((_objectType select 0) == "Soldier") then {
_callsign = [_x] call armatak_fnc_extract_unit_callsign;
_group_name = [group _x] call armatak_fnc_extract_group_color;
_group_role = [_x] call armatak_fnc_extract_group_role;
[_x, _callsign, _group_name, _group_role] call armatak_fnc_send_eud_cot;
[_x] call armatak_fnc_send_digital_pointer_cot;
};
if ((_objectType select 0) == "Vehicle") then {
_atak_type = [_x] call armatak_fnc_extract_role;
_callsign = [_x] call armatak_fnc_extract_marker_callsign;
[_x, _atak_type, _callsign] call armatak_fnc_send_marker_cot;
};
if (unitIsUAV _x) then {
[_x] call armatak_fnc_send_drone_cot;
[_x] call armatak_fnc_send_digital_pointer_cot;
};
} forEach GVAR(syncedUnits);
}, 2, []] call CBA_fnc_addPerFrameHandler;
deleteVehicle _logic;
closeDialog 1;

View File

@@ -0,0 +1,39 @@
#include "..\script_component.hpp"
/*
* Author: Valmo
* Adds a unit into the global marked units array.
*
* Arguments:
* 0: The module logic <OBJECT>
*
* Return Value:
* None
*
* Example:
* [LOGIC] call armatak_server_fnc_routerEntityAdd;
*
* Public: No
*/
params ["_logic"];
if (!local _logic) exitWith {};
private _unit = attachedTo _logic;
deleteVehicle _logic;
switch (false) do {
case (!isNull _unit): {
deleteVehicle _logic;
["Nothing selected", "error", "TCP Socket"] call EFUNC(main,notify);
};
default {
GVAR(syncedUnits) = missionNamespace getVariable "armatak_marked_units";
GVAR(syncedUnits) pushBack _unit;
missionNamespace setVariable ["armatak_marked_units", GVAR(syncedUnits)];
deleteVehicle _logic;
};
};

View File

@@ -0,0 +1,41 @@
#include "..\script_component.hpp"
/*
* Author: Valmo
* Removes a unit from the global marked units array.
*
* Arguments:
* 0: The module logic <OBJECT>
*
* Return Value:
* None
*
* Example:
* [LOGIC] call armatak_server_fnc_routerEntityRemove;
*
* Public: No
*/
params ["_logic"];
if (!local _logic) exitWith {};
private _unit = attachedTo _logic;
deleteVehicle _logic;
switch (false) do {
case (!isNull _unit): {
deleteVehicle _logic;
["Nothing selected", "error", "TCP Socket"] call EFUNC(main,notify);
};
default {
{
if (_x isEqualTo _unit) then {
GVAR(syncedUnits) deleteAt _forEachIndex;
};
} forEach GVAR(syncedUnits);
missionNmaespace setVariable ["armatak_marked_units", GVAR(syncedUnits)];
deleteVehicle _logic;
};
};

View File

@@ -0,0 +1,17 @@
#define COMPONENT server
#define COMPONENT_BEAUTIFIED TCP Socket Client
#include "\armatak\armatak\addons\main\script_mod.hpp"
// #define DEBUG_MODE_FULL
// #define DISABLE_COMPILE_CACHE
// #define ENABLE_PERFORMANCE_COUNTERS
#ifdef DEBUG_ENABLED_CLIENT
#define DEBUG_MODE_FULL
#endif
#ifdef DEBUG_SETTINGS_CLIENT
#define DEBUG_SETTINGS DEBUG_SETTINGS_CLIENT
#endif
#include "\z\ace\addons\main\script_macros.hpp"

1
addons/video/$PBOPREFIX$ Normal file
View File

@@ -0,0 +1 @@
armatak\armatak\addons\video

View File

@@ -0,0 +1,11 @@
class Extended_PreStart_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_SCRIPT(XEH_preStart));
};
};
class Extended_PreInit_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_SCRIPT(XEH_preInit));
};
};

View File

@@ -0,0 +1,72 @@
class CfgVehicles {
class Logic;
class Module_F : Logic
{
class AttributesBase
{
class Default;
class Edit;
class Combo;
class Checkbox;
class CheckboxNumber;
class ModuleDescription;
class Units;
};
class ModuleDescription
{
class AnyBrain;
};
};
class EGVAR(server,moduleBase);
class GVAR(videoModule): EGVAR(server,moduleBase) {
scope = 2;
scopeCurator = 0;
displayname = "Video Streaming Handler";
icon = "\a3\Modules_F_Curator\Data\iconRadio_ca.paa";
category = QEGVAR(main,moduleCategory);
function = QFUNC(videoParser);
functionPriority = 1;
isGlobal = 0;
isTriggerActivated = 1;
isDisposable = 1;
is3den = 0;
curatorCanAttach = 0;
curatorInfoType = "RscDisplayAttributeModuleNuke";
canSetArea = 0;
canSetAreaShape = 0;
canSetAreaHeight = 0;
class Attributes: AttributesBase {
class GVAR(instanceAddress): Edit {
property = QGVAR(instanceAddress);
displayname = "MediaMTX Provider Address";
tooltip = "MediaMTX Provider Instance Address";
typeName = "STRING";
defaultValue = "localhost";
};
class GVAR(instancePort): 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 GVAR(instanceAuthUser): Edit {
property = QGVAR(instanceAuthUser);
displayname = "MediaMTX Provider Username";
tooltip = "MediaMTX Provider Instance Username";
typeName = "STRING";
defaultValue = "administrator";
};
class GVAR(instanceAuthPassword): Edit {
property = QGVAR(instanceAuthPassword);
displayname = "MediaMTX Provider Password";
tooltip = "MediaMTX Provider Instance Password";
typeName = "STRING";
defaultValue = "password";
};
};
};
};

View File

@@ -0,0 +1 @@
PREP(videoParser);

View File

@@ -0,0 +1,9 @@
#include "script_component.hpp"
ADDON = false;
PREP_RECOMPILE_START;
#include "XEH_PREP.hpp"
PREP_RECOMPILE_END;
ADDON = true;

View File

@@ -0,0 +1,3 @@
#include "script_component.hpp"
#include "XEH_PREP.hpp"

42
addons/video/config.cpp Normal file
View File

@@ -0,0 +1,42 @@
#include "script_component.hpp"
class CfgPatches {
class ADDON {
name = COMPONENT_NAME;
units[] = {
QGVAR(videoModule)
};
weapons[] = {};
requiredAddons[] = {
"cba_main",
"ace_main",
"armatak_main",
"armatak_server"
};
requiredVersion = REQUIRED_VERSION;
author = PROJECT_AUTHOR;
url = "https://github.com/valmojr/armatak";
};
};
class CfgMods {
class PREFIX {
name = "Arma Team Awareness Kit";
author = PROJECT_AUTHOR;
logo = "logo_ca.paa";
logoOver = "logo_hover_ca.paa";
tooltip = "ARMATAK";
tooltipOwned = "ARMATAK";
picture = "picture.paa";
actionName = "GitHub";
action = "https://github.com/valmojr/armatak";
overview = "ARMATAK Addons allows Arma 3 sessions to be parsed to TAK Clients";
hideName = 0;
hidePicture = 0;
dlcColor[] = { 0.23, 0.39, 0.30, 1 };
logoSmall = "logo_small_ca.paa";
};
};
#include "CfgEventHandlers.hpp"
#include "CfgVehicles.hpp"

View File

@@ -0,0 +1,83 @@
#include "..\script_component.hpp"
params [
["_logic", objNull, [objNull]],
["_units", [], [[]]],
["_activated", true, [true]]
];
if (isServer) exitWith {
private _instance_address = GETVAR(_logic,GVAR(instanceAddress),false);
private _instance_port = GETVAR(_logic,GVAR(instancePort),false);
private _instance_auth_user = GETVAR(_logic,GVAR(instanceAuthUser),false);
private _instance_auth_pass = GETVAR(_logic,GVAR(instanceAuthPassword),false);
SETMVAR(GVAR(instanceAddress),_instance_address);
SETMVAR(GVAR(instancePort),_instance_port);
SETMVAR(GVAR(instanceAuthUser),_instance_auth_user);
SETMVAR(GVAR(instanceAuthPassword),_instance_auth_pass);
_startAction = [
QGVAR(startStream),
"Start Video Feed",
"",
{
_uuid = (_this select 0) call armatak_fnc_extract_uuid;
_uuid_short = _uuid select [0, 8];
_role = roleDescription (_this select 0);
_name = name (_this select 0);
_role = [_role] call BIS_fnc_filterString;
_name = [_name] call BIS_fnc_filterString;
_stream_path = _name + "_" + _role + "_" + _uuid_short;
armatak_mediamtx_video_stream_instance_address = GETMVAR(instance_address,false);
armatak_mediamtx_video_stream_instance_port = missionNamespace getVariable "instance_port";
armatak_mediamtx_video_stream_instance_auth_user = missionNamespace getVariable "instance_auth_user";
armatak_mediamtx_video_stream_instance_auth_pass = missionNamespace getVariable "instance_auth_pass";
"armatak" callExtension ["video_stream:start", [armatak_mediamtx_video_stream_instance_address, armatak_mediamtx_video_stream_instance_port, _stream_path, 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", []];
SETVAR(_this select 0,GVAR(isStreaming),false);
},
{
GETVAR((this select 0),GVAR(isStreaming),false)
}
] call ace_interact_menu_fnc_createAction;
[
"Man",
1,
["ACE_SelfActions"],
_stopAction,
true
] call ace_interact_menu_fnc_addActionToClass;
if (isMultiplayer) then {
{
SETVAR(_x,GVAR(isStreaming),false);
} forEach playableUnits;
} else {
SETVAR(player,GVAR(isStreaming),false);
};
};
true;

View File

@@ -0,0 +1,17 @@
#define COMPONENT video
#define COMPONENT_BEAUTIFIED Video Streaming
#include "\armatak\armatak\addons\main\script_mod.hpp"
// #define DEBUG_MODE_FULL
// #define DISABLE_COMPILE_CACHE
// #define ENABLE_PERFORMANCE_COUNTERS
#ifdef DEBUG_ENABLED_MAIN
#define DEBUG_MODE_FULL
#endif
#ifdef DEBUG_SETTINGS_MAIN
#define DEBUG_SETTINGS DEBUG_SETTINGS_MAIN
#endif
#include "\z\ace\addons\main\script_macros.hpp"

View File

@@ -0,0 +1 @@
x\cba\addons\main

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
x\cba\addons\xeh

View File

@@ -0,0 +1,146 @@
/*
Header: script_xeh.hpp
Description:
Used internally.
*/
/////////////////////////////////////////////////////////////////////////////////
// MACRO: EXTENDED_EVENTHANDLERS
// Add all XEH event handlers
/////////////////////////////////////////////////////////////////////////////////
#define EXTENDED_EVENTHANDLERS init = "call cba_xeh_fnc_init"; \
fired = "call cba_xeh_fnc_fired"; \
animChanged = "call cba_xeh_fnc_animChanged"; \
animDone = "call cba_xeh_fnc_animDone"; \
animStateChanged = "call cba_xeh_fnc_animStateChanged"; \
assembled = "call cba_xeh_fnc_assembled"; \
attached = "call cba_xeh_fnc_attached"; \
cargoLoaded = "call cba_xeh_fnc_cargoLoaded"; \
cargoUnloaded = "call cba_xeh_fnc_cargoUnloaded"; \
containerClosed = "call cba_xeh_fnc_containerClosed"; \
containerOpened = "call cba_xeh_fnc_containerOpened"; \
controlsShifted = "call cba_xeh_fnc_controlsShifted"; \
dammaged = "call cba_xeh_fnc_dammaged"; \
deleted = "call cba_xeh_fnc_deleted"; \
detached = "call cba_xeh_fnc_detached"; \
disassembled = "call cba_xeh_fnc_disassembled"; \
engine = "call cba_xeh_fnc_engine"; \
epeContact = "call cba_xeh_fnc_epeContact"; \
epeContactEnd = "call cba_xeh_fnc_epeContactEnd"; \
epeContactStart = "call cba_xeh_fnc_epeContactStart"; \
explosion = "call cba_xeh_fnc_explosion"; \
firedMan = "call cba_xeh_fnc_firedMan"; \
firedNear = "call cba_xeh_fnc_firedNear"; \
fuel = "call cba_xeh_fnc_cba_xeh_fuel"; \
gear = "call cba_xeh_fnc_gear"; \
gestureChanged = "call cba_xeh_fnc_gestureChanged"; \
gestureDone = "call cba_xeh_fnc_gestureDone"; \
getIn = "call cba_xeh_fnc_getIn"; \
getInMan = "call cba_xeh_fnc_getInMan"; \
getOut = "call cba_xeh_fnc_getOut"; \
getOutMan = "call cba_xeh_fnc_getOutMan"; \
handleHeal = "call cba_xeh_fnc_handleHeal"; \
hit = "call cba_xeh_fnc_hit"; \
hitPart = "call cba_xeh_fnc_hitPart"; \
incomingMissile = "call cba_xeh_fnc_incomingMissile"; \
inventoryClosed = "call cba_xeh_fnc_inventoryClosed"; \
inventoryOpened = "call cba_xeh_fnc_inventoryOpened"; \
killed = "call cba_xeh_fnc_killed"; \
landedTouchDown = "call cba_xeh_fnc_landedTouchDown"; \
landedStopped = "call cba_xeh_fnc_landedStopped"; \
leaningChanged = "call cba_xeh_fnc_leaningChanged"; \
local = "call cba_xeh_fnc_local"; \
magazineReloading = "call cba_xeh_fnc_magazineReloading"; \
opticsModeChanged = "call cba_xeh_fnc_opticsModeChanged"; \
opticsSwitch = "call cba_xeh_fnc_opticsSwitch"; \
put = "call cba_xeh_fnc_put"; \
reloaded = "call cba_xeh_fnc_reloaded"; \
respawn = "call cba_xeh_fnc_respawn"; \
ropeAttach = "call cba_xeh_fnc_ropeAttach"; \
ropeBreak = "call cba_xeh_fnc_ropeBreak"; \
seatSwitched = "call cba_xeh_fnc_seatSwitched"; \
seatSwitchedMan = "call cba_xeh_fnc_seatSwitchedMan"; \
slotItemChanged = "call cba_xeh_fnc_slotItemChanged"; \
suppressed = "call cba_xeh_fnc_suppressed"; \
soundPlayed = "call cba_xeh_fnc_soundPlayed"; \
take = "call cba_xeh_fnc_take"; \
turnIn = "call cba_xeh_fnc_turnIn"; \
turnOut = "call cba_xeh_fnc_turnOut"; \
visionModeChanged = "call cba_xeh_fnc_visionModeChanged"; \
weaponAssembled = "call cba_xeh_fnc_weaponAssembled"; \
weaponChanged = "call cba_xeh_fnc_weaponChanged"; \
weaponDisassembled = "call cba_xeh_fnc_weaponDisassembled"; \
weaponDeployed = "call cba_xeh_fnc_weaponDeployed"; \
weaponRested = "call cba_xeh_fnc_weaponRested";
/*
MACRO: DELETE_EVENTHANDLERS
Removes all event handlers.
*/
#define DELETE_EVENTHANDLERS init = ""; \
fired = ""; \
animChanged = ""; \
animDone = ""; \
animStateChanged = ""; \
assembled = ""; \
attached = ""; \
cargoLoaded = ""; \
cargoUnloaded = ""; \
containerClosed = ""; \
containerOpened = ""; \
controlsShifted = ""; \
dammaged = ""; \
deleted = ""; \
detached = ""; \
disassembled = ""; \
engine = ""; \
epeContact = ""; \
epeContactEnd = ""; \
epeContactStart = ""; \
explosion = ""; \
firedMan = ""; \
firedNear = ""; \
fuel = ""; \
gear = ""; \
gestureChanged = ""; \
gestureDone = "" \
getIn = ""; \
getInMan = ""; \
getOut = ""; \
getOutMan = ""; \
handleHeal = ""; \
hit = ""; \
hitPart = ""; \
incomingMissile = ""; \
inventoryClosed = ""; \
inventoryOpened = ""; \
killed = ""; \
landedTouchDown = ""; \
landedStopped = ""; \
leaningChanged = ""; \
local = ""; \
magazineReloading = ""; \
opticsModeChanged = ""; \
opticsSwitch = ""; \
put = ""; \
reloaded = ""; \
respawn = ""; \
ropeAttach = ""; \
ropeBreak = ""; \
seatSwitched = ""; \
seatSwitchedMan = ""; \
soundPlayed = ""; \
suppressed = ""; \
take = ""; \
turnIn = ""; \
turnOut = ""; \
visionModeChanged = ""; \
weaponAssembled = ""; \
weaponChanged = ""; \
weaponDisassembled = ""; \
weaponDeployed = ""; \
weaponRested = "";

View File

@@ -0,0 +1,58 @@
/**
Fast Recompiling via function
**/
// #define DISABLE_COMPILE_CACHE
// To Use: [] call ACE_PREP_RECOMPILE;
#ifdef DISABLE_COMPILE_CACHE
#define LINKFUNC(x) {call FUNC(x)}
#define PREP_RECOMPILE_START if (isNil "ACE_PREP_RECOMPILE") then {ACE_RECOMPILES = []; ACE_PREP_RECOMPILE = {{call _x} forEach ACE_RECOMPILES;}}; private _recomp = {
#define PREP_RECOMPILE_END }; call _recomp; ACE_RECOMPILES pushBack _recomp;
#else
#define LINKFUNC(x) FUNC(x)
#define PREP_RECOMPILE_START ; /* disabled */
#define PREP_RECOMPILE_END ; /* disabled */
#endif
/**
STACK TRACING
**/
//#define ENABLE_CALLSTACK
//#define ENABLE_PERFORMANCE_COUNTERS
//#define DEBUG_EVENTS
#ifdef ENABLE_CALLSTACK
#define CALLSTACK(function) {if(ACE_IS_ERRORED) then { ['AUTO','AUTO'] call ACE_DUMPSTACK_FNC; ACE_IS_ERRORED = false; }; ACE_IS_ERRORED = true; ACE_STACK_TRACE set [ACE_STACK_DEPTH, [diag_tickTime, __FILE__, __LINE__, ACE_CURRENT_FUNCTION, 'ANON', _this]]; ACE_STACK_DEPTH = ACE_STACK_DEPTH + 1; ACE_CURRENT_FUNCTION = 'ANON'; private _ret = _this call ##function; ACE_STACK_DEPTH = ACE_STACK_DEPTH - 1; ACE_IS_ERRORED = false; _ret;}
#define CALLSTACK_NAMED(function, functionName) {if(ACE_IS_ERRORED) then { ['AUTO','AUTO'] call ACE_DUMPSTACK_FNC; ACE_IS_ERRORED = false; }; ACE_IS_ERRORED = true; ACE_STACK_TRACE set [ACE_STACK_DEPTH, [diag_tickTime, __FILE__, __LINE__, ACE_CURRENT_FUNCTION, functionName, _this]]; ACE_STACK_DEPTH = ACE_STACK_DEPTH + 1; ACE_CURRENT_FUNCTION = functionName; private _ret = _this call ##function; ACE_STACK_DEPTH = ACE_STACK_DEPTH - 1; ACE_IS_ERRORED = false; _ret;}
#define DUMPSTACK ([__FILE__, __LINE__] call ACE_DUMPSTACK_FNC)
#define FUNC(var1) {if(ACE_IS_ERRORED) then { ['AUTO','AUTO'] call ACE_DUMPSTACK_FNC; ACE_IS_ERRORED = false; }; ACE_IS_ERRORED = true; ACE_STACK_TRACE set [ACE_STACK_DEPTH, [diag_tickTime, __FILE__, __LINE__, ACE_CURRENT_FUNCTION, 'TRIPLES(ADDON,fnc,var1)', _this]]; ACE_STACK_DEPTH = ACE_STACK_DEPTH + 1; ACE_CURRENT_FUNCTION = 'TRIPLES(ADDON,fnc,var1)'; private _ret = _this call TRIPLES(ADDON,fnc,var1); ACE_STACK_DEPTH = ACE_STACK_DEPTH - 1; ACE_IS_ERRORED = false; _ret;}
#define EFUNC(var1,var2) {if(ACE_IS_ERRORED) then { ['AUTO','AUTO'] call ACE_DUMPSTACK_FNC; ACE_IS_ERRORED = false; }; ACE_IS_ERRORED = true; ACE_STACK_TRACE set [ACE_STACK_DEPTH, [diag_tickTime, __FILE__, __LINE__, ACE_CURRENT_FUNCTION, 'TRIPLES(DOUBLES(PREFIX,var1),fnc,var2)', _this]]; ACE_STACK_DEPTH = ACE_STACK_DEPTH + 1; ACE_CURRENT_FUNCTION = 'TRIPLES(DOUBLES(PREFIX,var1),fnc,var2)'; private _ret = _this call TRIPLES(DOUBLES(PREFIX,var1),fnc,var2); ACE_STACK_DEPTH = ACE_STACK_DEPTH - 1; ACE_IS_ERRORED = false; _ret;}
#else
#define CALLSTACK(function) function
#define CALLSTACK_NAMED(function, functionName) function
#define DUMPSTACK ; /* disabled */
#endif
/**
PERFORMANCE COUNTERS SECTION
**/
//#define ENABLE_PERFORMANCE_COUNTERS
// To Use: [] call ace_common_fnc_dumpPerformanceCounters;
#ifdef ENABLE_PERFORMANCE_COUNTERS
#define CBA_fnc_addPerFrameHandler { private _ret = call CBA_fnc_addPerFrameHandler; if(isNil "ACE_PFH_COUNTER" ) then { ACE_PFH_COUNTER=[]; }; ACE_PFH_COUNTER pushBack [[_ret, __FILE__, __LINE__], _this]; _ret }
#define CREATE_COUNTER(x) if(isNil "ACE_COUNTERS" ) then { ACE_COUNTERS=[]; }; GVAR(DOUBLES(x,counter))=[]; GVAR(DOUBLES(x,counter)) set[0, QUOTE(GVAR(DOUBLES(x,counter)))]; GVAR(DOUBLES(x,counter)) set[1, diag_tickTime]; ACE_COUNTERS pushBack GVAR(DOUBLES(x,counter));
#define BEGIN_COUNTER(x) if(isNil QUOTE(GVAR(DOUBLES(x,counter)))) then { CREATE_COUNTER(x) }; GVAR(DOUBLES(x,counter)) set[2, diag_tickTime];
#define END_COUNTER(x) GVAR(DOUBLES(x,counter)) pushBack [(GVAR(DOUBLES(x,counter)) select 2), diag_tickTime];
#define DUMP_COUNTERS ([__FILE__, __LINE__] call ACE_DUMPCOUNTERS_FNC)
#else
#define CREATE_COUNTER(x) ; /* disabled */
#define BEGIN_COUNTER(x) ; /* disabled */
#define END_COUNTER(x) ; /* disabled */
#define DUMP_COUNTERS ; /* disabled */
#endif

View File

@@ -0,0 +1,179 @@
#define DEBUG_SYNCHRONOUS
#include "\x\cba\addons\main\script_macros_common.hpp"
#include "\x\cba\addons\xeh\script_xeh.hpp"
// Default versioning level
#define DEFAULT_VERSIONING_LEVEL 2
#define DGVAR(varName) if(isNil "ACE_DEBUG_NAMESPACE") then { ACE_DEBUG_NAMESPACE = []; }; if(!(QUOTE(GVAR(varName)) in ACE_DEBUG_NAMESPACE)) then { PUSH(ACE_DEBUG_NAMESPACE, QUOTE(GVAR(varName))); }; GVAR(varName)
#define DVAR(varName) if(isNil "ACE_DEBUG_NAMESPACE") then { ACE_DEBUG_NAMESPACE = []; }; if(!(QUOTE(varName) in ACE_DEBUG_NAMESPACE)) then { PUSH(ACE_DEBUG_NAMESPACE, QUOTE(varName)); }; varName
#define DFUNC(var1) TRIPLES(ADDON,fnc,var1)
#define DEFUNC(var1,var2) TRIPLES(DOUBLES(PREFIX,var1),fnc,var2)
#undef QFUNC
#undef QEFUNC
#define QFUNC(var1) QUOTE(DFUNC(var1))
#define QEFUNC(var1,var2) QUOTE(DEFUNC(var1,var2))
#define GETVAR_SYS(var1,var2) getVariable [ARR_2(QUOTE(var1),var2)]
#define SETVAR_SYS(var1,var2) setVariable [ARR_2(QUOTE(var1),var2)]
#define SETPVAR_SYS(var1,var2) setVariable [ARR_3(QUOTE(var1),var2,true)]
#undef GETVAR
#define GETVAR(var1,var2,var3) (var1 GETVAR_SYS(var2,var3))
#define GETMVAR(var1,var2) (missionNamespace GETVAR_SYS(var1,var2))
#define GETUVAR(var1,var2) (uiNamespace GETVAR_SYS(var1,var2))
#define GETPRVAR(var1,var2) (profileNamespace GETVAR_SYS(var1,var2))
#define GETPAVAR(var1,var2) (parsingNamespace GETVAR_SYS(var1,var2))
#undef SETVAR
#define SETVAR(var1,var2,var3) var1 SETVAR_SYS(var2,var3)
#define SETPVAR(var1,var2,var3) var1 SETPVAR_SYS(var2,var3)
#define SETMVAR(var1,var2) missionNamespace SETVAR_SYS(var1,var2)
#define SETUVAR(var1,var2) uiNamespace SETVAR_SYS(var1,var2)
#define SETPRVAR(var1,var2) profileNamespace SETVAR_SYS(var1,var2)
#define SETPAVAR(var1,var2) parsingNamespace SETVAR_SYS(var1,var2)
#define GETGVAR(var1,var2) GETMVAR(GVAR(var1),var2)
#define GETEGVAR(var1,var2,var3) GETMVAR(EGVAR(var1,var2),var3)
#define ARR_SELECT(ARRAY,INDEX,DEFAULT) (if (count ARRAY > INDEX) then {ARRAY select INDEX} else {DEFAULT})
#define ANY_OF(ARRAY,CONDITION) (ARRAY findIf {CONDITION} != -1)
// ACEX Merge
#define ACEX_PREFIX acex
#define XADDON DOUBLES(ACEX_PREFIX,COMPONENT)
#define XGVAR(var) DOUBLES(XADDON,var)
#define EXGVAR(var1,var2) TRIPLES(ACEX_PREFIX,var1,var2)
#define QXGVAR(var) QUOTE(XGVAR(var))
#define QEXGVAR(var1,var2) QUOTE(EXGVAR(var1,var2))
#define QQXGVAR(var) QUOTE(QXGVAR(var))
#define QQEXGVAR(var1,var2) QUOTE(QEXGVAR(var1,var2))
#define ACEX_PREP(func) PREP(func); TRIPLES(XADDON,fnc,func) = DFUNC(func)
#define MACRO_ADDWEAPON(WEAPON,COUNT) class _xx_##WEAPON { \
weapon = #WEAPON; \
count = COUNT; \
}
#define MACRO_ADDITEM(ITEM,COUNT) class _xx_##ITEM { \
name = #ITEM; \
count = COUNT; \
}
#define MACRO_ADDMAGAZINE(MAGAZINE,COUNT) class _xx_##MAGAZINE { \
magazine = #MAGAZINE; \
count = COUNT; \
}
#define MACRO_ADDBACKPACK(BACKPACK,COUNT) class _xx_##BACKPACK { \
backpack = #BACKPACK; \
count = COUNT; \
}
// weapon types
#define TYPE_WEAPON_PRIMARY 1
#define TYPE_WEAPON_HANDGUN 2
#define TYPE_WEAPON_SECONDARY 4
// magazine types
#define TYPE_MAGAZINE_HANDGUN_AND_GL 16 // mainly
#define TYPE_MAGAZINE_PRIMARY_AND_THROW 256
#define TYPE_MAGAZINE_SECONDARY_AND_PUT 512 // mainly
#define TYPE_MAGAZINE_MISSILE 768
// more types
#define TYPE_BINOCULAR_AND_NVG 4096
#define TYPE_WEAPON_VEHICLE 65536
#define TYPE_ITEM 131072
// item types
#define TYPE_DEFAULT 0
#define TYPE_MUZZLE 101
#define TYPE_OPTICS 201
#define TYPE_FLASHLIGHT 301
#define TYPE_BIPOD 302
#define TYPE_FIRST_AID_KIT 401
#define TYPE_FINS 501 // not implemented
#define TYPE_BREATHING_BOMB 601 // not implemented
#define TYPE_NVG 602
#define TYPE_GOGGLE 603
#define TYPE_SCUBA 604 // not implemented
#define TYPE_HEADGEAR 605
#define TYPE_FACTOR 607
#define TYPE_MAP 608
#define TYPE_COMPASS 609
#define TYPE_WATCH 610
#define TYPE_RADIO 611
#define TYPE_GPS 612
#define TYPE_HMD 616
#define TYPE_BINOCULAR 617
#define TYPE_MEDIKIT 619
#define TYPE_TOOLKIT 620
#define TYPE_UAV_TERMINAL 621
#define TYPE_VEST 701
#define TYPE_UNIFORM 801
#define TYPE_BACKPACK 901
#ifdef DISABLE_COMPILE_CACHE
#undef PREP
#define PREP(fncName) DFUNC(fncName) = compile preprocessFileLineNumbers QPATHTOF(functions\DOUBLES(fnc,fncName).sqf)
#else
#undef PREP
#define PREP(fncName) [QPATHTOF(functions\DOUBLES(fnc,fncName).sqf), QFUNC(fncName)] call CBA_fnc_compileFunction
#endif
#define PREP_MODULE(folder) [] call compile preprocessFileLineNumbers QPATHTOF(folder\__PREP__.sqf)
#define ACE_isHC (!hasInterface && !isDedicated)
#define IDC_STAMINA_BAR 193
#define ACE_DEPRECATED(arg1,arg2,arg3) WARNING_3("%1 is deprecated. Support will be dropped in version %2. Replaced by: %3",arg1,arg2,arg3)
#define PFORMAT_10(MESSAGE,A,B,C,D,E,F,G,H,I,J) \
format ['%1: A=%2, B=%3, C=%4, D=%5, E=%6, F=%7, G=%8, H=%9, I=%10 J=%11', MESSAGE, RETNIL(A), RETNIL(B), RETNIL(C), RETNIL(D), RETNIL(E), RETNIL(F), RETNIL(G), RETNIL(H), RETNIL(I), RETNIL(J)]
#ifdef DEBUG_MODE_FULL
#define TRACE_10(MESSAGE,A,B,C,D,E,F,G,H,I,J) LOG_SYS_FILELINENUMBERS('TRACE',PFORMAT_10(str diag_frameNo + ' ' + (MESSAGE),A,B,C,D,E,F,G,H,I,J))
#else
#define TRACE_10(MESSAGE,A,B,C,D,E,F,G,H,I,J) /* disabled */
#endif
#define GRAVITY 9.8066
#define SD_TO_MIN_MAX(d) ((d) * 3.371) // Standard deviation -> min / max of random [min, mid, max]
// Angular unit conversion
// Conversion factor: 54 / (5 * PI)
#define MRAD_TO_MOA(d) ((d) * 3.43774677)
// Conversion factor: (5 * PI) / 54
#define MOA_TO_MRAD(d) ((d) * 0.29088821)
// Conversion factor: 60
#define DEG_TO_MOA(d) ((d) * 60)
// Conversion factor: 1 / 60
#define MOA_TO_DEG(d) ((d) / 60)
// Conversion factor: (50 * PI) / 9
#define DEG_TO_MRAD(d) ((d) * 17.45329252)
// Conversion factor: 9 / (50 * PI)
#define MRAD_TO_DEG(d) ((d) / 17.45329252)
// Conversion factor: PI / 10800
#define MOA_TO_RAD(d) ((d) * 0.00029088)
#define ZEUS_ACTION_CONDITION ([_target, {QUOTE(QUOTE(ADDON)) in curatorAddons _this}, missionNamespace, QUOTE(QGVAR(zeusCheck)), 1E11, 'ace_interactMenuClosed'] call EFUNC(common,cachedCall))
#define SUBSKILLS ["aimingAccuracy", "aimingShake", "aimingSpeed", "spotDistance", "spotTime", "courage", "reloadSpeed", "commanding", "general"]
// macro add a dummy cfgPatch and notLoaded entry
#define ACE_PATCH_NOT_LOADED(NAME,CAUSE) \
class CfgPatches { \
class DOUBLES(NAME,notLoaded) { \
units[] = {}; \
weapons[] = {}; \
requiredVersion = REQUIRED_VERSION; \
requiredAddons[] = {"ace_main"}; \
VERSION_CONFIG; \
}; \
}; \
class ace_notLoaded { \
NAME = CAUSE; \
};
#include "\z\ace\addons\main\script_debug.hpp"

View File

@@ -7,7 +7,7 @@ tooltipOwned = "ARMATAK";
picture = "picture.paa"; picture = "picture.paa";
actionName = "GitHub"; actionName = "GitHub";
action = "https://github.com/valmojr/armatak"; action = "https://github.com/valmojr/armatak";
overview = "ARMATAK Addons is Full Stack Project to handle Arma 3 Sessions as real loc entities on TAK Clients"; overview = "ARMATAK Addons allows Arma 3 sessions to be parsed to TAK Clients";
hideName = 0; hideName = 0;
hidePicture = 0; hidePicture = 0;
dlcColor[] = { 0.23, 0.39, 0.30, 1 }; dlcColor[] = { 0.23, 0.39, 0.30, 1 };

109
src/cot/cot.rs Normal file
View File

@@ -0,0 +1,109 @@
use uuid::Uuid;
use chrono::{Duration, SecondsFormat, Utc};
pub struct CursorOverTime {
pub uuid: Option<String>,
pub r#type: Option<String>,
pub point_lat: f64,
pub point_lon: f64,
pub point_hae: f32,
pub point_ce: Option<f32>,
pub point_le: Option<f32>,
pub contact_callsign: String,
pub group_name: Option<String>,
pub group_role: Option<String>,
pub track_course: Option<i32>,
pub track_speed: Option<f32>,
pub link_uid: Option<String>,
}
impl CursorOverTime {
pub fn convert_to_xml(&self) -> String {
let uuid = match &self.uuid {
Some(uuid) => uuid,
None => &Uuid::new_v4().to_string(),
};
let marker_type = match &self.r#type {
Some(marker_type) => marker_type,
None => &"a-f-G-U-C-I".to_string(),
};
let created_time = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true);
let stale_time =
(Utc::now() + Duration::seconds(360)).to_rfc3339_opts(SecondsFormat::Millis, true);
let mut xml = String::new();
xml.push_str("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
xml.push_str(
format!("<event type=\"{}\" version=\"2.0\" how=\"m-g\" uid=\"{}\" time=\"{}\" start=\"{}\" stale=\"{}\">",
marker_type, uuid, created_time, created_time, stale_time).as_str());
let point_ce = match &self.point_ce {
Some(point_ce) => point_ce,
None => &9999999.0,
};
let point_le = match &self.point_le {
Some(point_le) => point_le,
None => &9999999.0,
};
xml.push_str(
format!(
"<point ce=\"{}\" le=\"{}\" hae=\"{}\" lat=\"{}\" lon=\"{}\" />",
point_ce, point_le, self.point_hae, self.point_lat, self.point_lon
)
.as_str(),
);
xml.push_str("<detail>");
xml.push_str("<takv device=\"Samsung S24\" os=\"Arma 3\" platform=\"ARMATAK\" version=\"0.9.0.0\" />");
if let Some(linked_uid) = &self.link_uid {
xml.push_str("<precisionlocation altsrc=\"DTED0\" />");
xml.push_str(
format!(
"<link uid=\"{}\" type=\"a-f-G-U-C\" relation=\"p-p\" />",
linked_uid,
)
.as_str(),
);
xml.push_str("<hideLabel />");
}
xml.push_str(format!("<contact callsign=\"{}\" />", self.contact_callsign).as_str());
xml.push_str(format!("<uid Droid=\"{}\"/>", self.contact_callsign).as_str());
if let (Some(track_course), Some(track_speed)) = (&self.track_course, &self.track_speed) {
xml.push_str(
format!(
"<track course=\"{}\" speed=\"{}\" />",
track_course, track_speed
)
.as_str(),
);
xml.push_str("<status battery=\"89\" />");
}
if let (Some(group_name), Some(group_role)) = (&self.group_name, &self.group_role) {
xml.push_str(
format!(
"<__group name=\"{}\" role=\"{}\" />",
group_name, group_role
)
.as_str(),
);
}
xml.push_str("</detail></event>");
return xml;
}
}

View File

@@ -0,0 +1,46 @@
use arma_rs::{FromArma, FromArmaError};
use super::cot::CursorOverTime;
pub struct DigitalPointerPayload {
pub link_uid: String,
pub contact_callsign: String,
pub point_lat: f64,
pub point_lon: f64,
pub point_hae: f32,
}
impl FromArma for DigitalPointerPayload {
fn from_arma(data: String) -> Result<DigitalPointerPayload, FromArmaError> {
let (link_uid, contact_callsign, point_lat, point_lon, point_hae) =
<(String, String, f64, f64, f32)>::from_arma(data)?;
Ok(Self {
link_uid,
contact_callsign,
point_lat,
point_lon,
point_hae,
})
}
}
impl DigitalPointerPayload {
pub fn to_cot(&self) -> CursorOverTime {
CursorOverTime {
uuid: Some(format!("{}{}", self.link_uid.clone(), ".SPI1")),
r#type: Some("b-m-p-s-p-i".to_string()),
point_lat: self.point_lat,
point_lon: self.point_lon,
point_hae: self.point_hae,
point_ce: None,
point_le: None,
contact_callsign: self.contact_callsign.clone(),
group_name: None,
group_role: None,
track_course: None,
track_speed: None,
link_uid: Some(self.link_uid.clone()),
}
}
}

62
src/cot/eud.rs Normal file
View File

@@ -0,0 +1,62 @@
use arma_rs::{FromArma, FromArmaError};
use super::cot::CursorOverTime;
pub struct EudCoTPayload {
pub uuid: String,
pub point_lat: f64,
pub point_lon: f64,
pub point_hae: f32,
pub contact_callsign: String,
pub group_name: String,
pub group_role: String,
pub track_course: i32,
pub track_speed: f32,
}
impl FromArma for EudCoTPayload {
fn from_arma(data: String) -> Result<EudCoTPayload, FromArmaError> {
let (
uuid,
point_lat,
point_lon,
point_hae,
contact_callsign,
group_name,
group_role,
track_course,
track_speed,
) = <(String, f64, f64, f32, String, String, String, i32, f32)>::from_arma(data)?;
Ok(Self {
uuid,
point_lat,
point_lon,
point_hae,
contact_callsign,
group_name,
group_role,
track_course,
track_speed,
})
}
}
impl EudCoTPayload {
pub fn to_cot(&self) -> CursorOverTime {
CursorOverTime {
uuid: Some(self.uuid.clone()),
r#type: None,
point_lat: self.point_lat,
point_lon: self.point_lon,
point_hae: self.point_hae,
point_ce: None,
point_le: None,
contact_callsign: self.contact_callsign.clone(),
group_name: Some(self.group_name.clone()),
group_role: Some(self.group_role.clone()),
track_course: Some(self.track_course),
track_speed: Some(self.track_speed),
link_uid: None,
}
}
}

4
src/cot/mod.rs Normal file
View File

@@ -0,0 +1,4 @@
pub mod cot;
pub mod digital_pointer;
pub mod eud;
pub mod nato;

59
src/cot/nato.rs Normal file
View File

@@ -0,0 +1,59 @@
use arma_rs::{FromArma, FromArmaError};
use super::cot::CursorOverTime;
pub struct MarkerCoTPayload {
pub uuid: String,
pub r#type: String,
pub point_lat: f64,
pub point_lon: f64,
pub point_hae: f32,
pub contact_callsign: String,
pub track_course: i32,
pub track_speed: f32,
}
impl FromArma for MarkerCoTPayload {
fn from_arma(data: String) -> Result<MarkerCoTPayload, FromArmaError> {
let (
uuid,
r#type,
point_lat,
point_lon,
point_hae,
contact_callsign,
track_course,
track_speed,
) = <(String, String, f64, f64, f32, String, i32, f32)>::from_arma(data)?;
Ok(Self {
uuid,
r#type,
point_lat,
point_lon,
point_hae,
contact_callsign,
track_course,
track_speed,
})
}
}
impl MarkerCoTPayload {
pub fn to_cot(&self) -> CursorOverTime {
CursorOverTime {
uuid: Some(self.uuid.clone()),
r#type: Some(self.r#type.clone()),
point_lat: self.point_lat,
point_lon: self.point_lon,
point_hae: self.point_hae,
point_ce: None,
point_le: None,
contact_callsign: self.contact_callsign.clone(),
group_name: None,
group_role: None,
track_course: Some(self.track_course),
track_speed: Some(self.track_speed),
link_uid: None,
}
}
}

View File

@@ -1,267 +0,0 @@
use arma_rs::{FromArma, FromArmaError};
use chrono::{Duration, SecondsFormat, Utc};
use uuid::Uuid;
pub struct CursorOverTime {
pub uuid: Option<String>,
pub r#type: Option<String>,
pub point_lat: f64,
pub point_lon: f64,
pub point_hae: f32,
pub point_ce: Option<f32>,
pub point_le: Option<f32>,
pub contact_callsign: String,
pub group_name: Option<String>,
pub group_role: Option<String>,
pub track_course: Option<i32>,
pub track_speed: Option<f32>,
pub link_uid: Option<String>,
}
impl CursorOverTime {
pub fn convert_to_xml(&self) -> String {
let uuid = match &self.uuid {
Some(uuid) => uuid,
None => &Uuid::new_v4().to_string(),
};
let marker_type = match &self.r#type {
Some(marker_type) => marker_type,
None => &"a-f-G-U-C-I".to_string(),
};
let created_time = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true);
let stale_time =
(Utc::now() + Duration::seconds(360)).to_rfc3339_opts(SecondsFormat::Millis, true);
let mut xml = String::new();
xml.push_str("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
xml.push_str(
format!("<event type=\"{}\" version=\"2.0\" how=\"m-g\" uid=\"{}\" time=\"{}\" start=\"{}\" stale=\"{}\">",
marker_type, uuid, created_time, created_time, stale_time).as_str());
let point_ce = match &self.point_ce {
Some(point_ce) => point_ce,
None => &9999999.0,
};
let point_le = match &self.point_le {
Some(point_le) => point_le,
None => &9999999.0,
};
xml.push_str(
format!(
"<point ce=\"{}\" le=\"{}\" hae=\"{}\" lat=\"{}\" lon=\"{}\" />",
point_ce, point_le, self.point_hae, self.point_lat, self.point_lon
)
.as_str(),
);
xml.push_str("<detail>");
xml.push_str("<takv device=\"Samsung S24\" os=\"Arma 3\" platform=\"ARMATAK\" version=\"0.9.0.0\" />");
if let Some(linked_uid) = &self.link_uid {
xml.push_str("<precisionlocation altsrc=\"DTED0\" />");
xml.push_str(
format!(
"<link uid=\"{}\" type=\"a-f-G-U-C\" relation=\"p-p\" />",
linked_uid,
)
.as_str(),
);
xml.push_str("<hideLabel />");
}
xml.push_str(format!("<contact callsign=\"{}\" />", self.contact_callsign).as_str());
xml.push_str(format!("<uid Droid=\"{}\"/>", self.contact_callsign).as_str());
if let (Some(track_course), Some(track_speed)) = (&self.track_course, &self.track_speed) {
xml.push_str(
format!(
"<track course=\"{}\" speed=\"{}\" />",
track_course, track_speed
)
.as_str(),
);
xml.push_str("<status battery=\"89\" />");
}
if let (Some(group_name), Some(group_role)) = (&self.group_name, &self.group_role) {
xml.push_str(
format!(
"<__group name=\"{}\" role=\"{}\" />",
group_name, group_role
)
.as_str(),
);
}
xml.push_str("</detail></event>");
return xml;
}
}
pub struct HumanCoTPayload {
pub uuid: String,
pub point_lat: f64,
pub point_lon: f64,
pub point_hae: f32,
pub contact_callsign: String,
pub group_name: String,
pub group_role: String,
pub track_course: i32,
pub track_speed: f32,
}
impl FromArma for HumanCoTPayload {
fn from_arma(data: String) -> Result<HumanCoTPayload, FromArmaError> {
let (
uuid,
point_lat,
point_lon,
point_hae,
contact_callsign,
group_name,
group_role,
track_course,
track_speed,
) = <(String, f64, f64, f32, String, String, String, i32, f32)>::from_arma(data)?;
Ok(Self {
uuid,
point_lat,
point_lon,
point_hae,
contact_callsign,
group_name,
group_role,
track_course,
track_speed,
})
}
}
impl HumanCoTPayload {
pub fn to_cot(&self) -> CursorOverTime {
CursorOverTime {
uuid: Some(self.uuid.clone()),
r#type: None,
point_lat: self.point_lat,
point_lon: self.point_lon,
point_hae: self.point_hae,
point_ce: None,
point_le: None,
contact_callsign: self.contact_callsign.clone(),
group_name: Some(self.group_name.clone()),
group_role: Some(self.group_role.clone()),
track_course: Some(self.track_course),
track_speed: Some(self.track_speed),
link_uid: None,
}
}
}
pub struct MarkerCoTPayload {
pub uuid: String,
pub r#type: String,
pub point_lat: f64,
pub point_lon: f64,
pub point_hae: f32,
pub contact_callsign: String,
pub track_course: i32,
pub track_speed: f32,
}
impl FromArma for MarkerCoTPayload {
fn from_arma(data: String) -> Result<MarkerCoTPayload, FromArmaError> {
let (
uuid,
r#type,
point_lat,
point_lon,
point_hae,
contact_callsign,
track_course,
track_speed,
) = <(String, String, f64, f64, f32, String, i32, f32)>::from_arma(data)?;
Ok(Self {
uuid,
r#type,
point_lat,
point_lon,
point_hae,
contact_callsign,
track_course,
track_speed,
})
}
}
impl MarkerCoTPayload {
pub fn to_cot(&self) -> CursorOverTime {
CursorOverTime {
uuid: Some(self.uuid.clone()),
r#type: Some(self.r#type.clone()),
point_lat: self.point_lat,
point_lon: self.point_lon,
point_hae: self.point_hae,
point_ce: None,
point_le: None,
contact_callsign: self.contact_callsign.clone(),
group_name: None,
group_role: None,
track_course: Some(self.track_course),
track_speed: Some(self.track_speed),
link_uid: None,
}
}
}
pub struct DigitalPointerPayload {
pub link_uid: String,
pub contact_callsign: String,
pub point_lat: f64,
pub point_lon: f64,
pub point_hae: f32,
}
impl FromArma for DigitalPointerPayload {
fn from_arma(data: String) -> Result<DigitalPointerPayload, FromArmaError> {
let (link_uid, contact_callsign, point_lat, point_lon, point_hae) =
<(String, String, f64, f64, f32)>::from_arma(data)?;
Ok(Self {
link_uid,
contact_callsign,
point_lat,
point_lon,
point_hae,
})
}
}
impl DigitalPointerPayload {
pub fn to_cot(&self) -> CursorOverTime {
CursorOverTime {
uuid: Some(format!("{}{}", self.link_uid.clone(), ".SPI1")),
r#type: Some("b-m-p-s-p-i".to_string()),
point_lat: self.point_lat,
point_lon: self.point_lon,
point_hae: self.point_hae,
point_ce: None,
point_le: None,
contact_callsign: self.contact_callsign.clone(),
group_name: None,
group_role: None,
track_course: None,
track_speed: None,
link_uid: Some(self.link_uid.clone()),
}
}
}

View File

@@ -7,7 +7,7 @@ use std::sync::mpsc::{self, Receiver, Sender};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::thread; use std::thread;
use crate::cot_generator::{DigitalPointerPayload, HumanCoTPayload, MarkerCoTPayload}; use crate::cot;
pub enum TcpCommand { pub enum TcpCommand {
SendMessage(String, Context), SendMessage(String, Context),
@@ -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("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", "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", "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", "TCP SOCKET ERROR",
"tak_socket_not_active", "TAK Socket is not active",
); );
} }
} }
@@ -107,49 +109,47 @@ 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("TCP SOCKET ERROR", "TCP Client is not running");
info!("TCP client is not running."); info!("TCP client is not running.");
} }
"Sending payload to TCP server" "Sending payload to TCP server"
} }
pub fn send_human_cot(ctx: Context, cursor_over_time: HumanCoTPayload) -> &'static str { pub fn send_eud_cot(ctx: Context, cursor_over_time: cot::eud::EudCoTPayload) -> &'static str {
let payload = cursor_over_time.to_cot().convert_to_xml(); let payload = cursor_over_time.to_cot().convert_to_xml();
send_payload(ctx, payload); send_payload(ctx, payload);
"Sending Human Cursor Over Time to TCP server" "Sending End User Device Cursor Over Time to TCP server"
} }
pub fn send_marker_cot(ctx: Context, cursor_over_time: MarkerCoTPayload) -> &'static str { pub fn send_marker_cot(ctx: Context, cursor_over_time: cot::nato::MarkerCoTPayload) -> &'static str {
let payload = cursor_over_time.to_cot().convert_to_xml(); let payload = cursor_over_time.to_cot().convert_to_xml();
send_payload(ctx, payload); send_payload(ctx, payload);
"Sending Marker Cursor Over Time to TCP server" "Sending Marker Cursor Over Time to TCP server"
} }
pub fn send_digital_pointer_cot(ctx: Context, cursor_over_time: DigitalPointerPayload) -> &'static str { pub fn send_digital_pointer_cot(ctx: Context, cursor_over_time: cot::digital_pointer::DigitalPointerPayload) -> &'static str {
let payload = cursor_over_time.to_cot().convert_to_xml(); let payload = cursor_over_time.to_cot().convert_to_xml();
send_payload(ctx, payload); send_payload(ctx, payload);
"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("TCP SOCKET", "TCP client stopped");
} else { } else {
info!("TCP client is not running."); let _ = ctx.callback_null("TCP SOCKET ERROR", "TCP client is not running");
} }
"Stopping TCP Client" "Stopping TCP Client"

View File

@@ -2,9 +2,11 @@ use arma_rs::{arma, Extension, Group};
mod structs; mod structs;
mod tests; mod tests;
mod websocket; mod websocket;
mod util;
mod cot_router; mod cot_router;
mod cot_generator; mod video_stream;
mod cot;
mod utils;
#[arma] #[arma]
pub fn init() -> Extension { pub fn init() -> Extension {
@@ -29,25 +31,31 @@ pub fn init() -> Extension {
log4rs::init_config(config).unwrap(); log4rs::init_config(config).unwrap();
websocket::start();
Extension::build() Extension::build()
.group("websocket", Group::new() .group("websocket", Group::new()
.command("start", websocket::start)
.command("stop", websocket::stop)
.command("message", websocket::message) .command("message", websocket::message)
.command("location", websocket::location) .command("location", websocket::location)
) )
.command("local_ip", util::get_local_address) .command("local_ip", utils::address::get_local_address)
.command("uuid", util::get_uuid) .command("uuid", utils::uuid::get_uuid)
.command("log", util::log_info) .command("log", utils::log::log_info)
.group( .group(
"cot_router", "tcp_socket",
Group::new() Group::new()
.command("start", cot_router::start) .command("start", cot_router::start)
.command("send_payload", cot_router::send_payload) .command("send_payload", cot_router::send_payload)
.command("send_human_cot", cot_router::send_human_cot) .command("send_eud_cot", cot_router::send_eud_cot)
.command("send_marker_cot", cot_router::send_marker_cot) .command("send_marker_cot", cot_router::send_marker_cot)
.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()
} }

View File

@@ -13,7 +13,7 @@ mod tests {
assert!(validation.is_ok()) assert!(validation.is_ok())
} }
#[test] #[test]
fn uuid_output_throws_if_passed_args() { fn uuid_output_throws_if_passed_args() {
let extension = init().testing(); let extension = init().testing();

View File

@@ -1,43 +0,0 @@
use crate::structs::LogPayload;
use log::{error, info, warn};
use std::net::{IpAddr, UdpSocket};
pub fn log_info(data: LogPayload) -> String {
match data.status.as_str() {
"info" => info!("{}", data.message),
"warn" => warn!("{}", data.message),
"error" => error!("{}", data.message),
_ => error!("{}", "Wrong log call"),
}
"logged".to_string()
}
pub fn get_uuid() -> String {
use uuid::Uuid;
let id = Uuid::new_v4().to_string();
return id;
}
pub fn get_local_address() -> String {
fn get_local_ip() -> Result<IpAddr, String> {
let socket = UdpSocket::bind("0.0.0.0:0").map_err(|e| e.to_string())?;
socket.connect("8.8.8.8:80").map_err(|e| e.to_string())?;
socket
.local_addr()
.map(|addr| addr.ip())
.map_err(|e| e.to_string())
}
let parsed_data = get_local_ip();
match parsed_data {
Ok(ip) => {
return format!("ws://{}:4152", ip.to_string());
}
Err(_) => {
return "not provided".to_string();
}
}
}

23
src/utils/address.rs Normal file
View File

@@ -0,0 +1,23 @@
use std::net::{IpAddr, UdpSocket};
pub fn get_local_address() -> String {
fn get_local_ip() -> Result<IpAddr, String> {
let socket = UdpSocket::bind("0.0.0.0:0").map_err(|e| e.to_string())?;
socket.connect("8.8.8.8:80").map_err(|e| e.to_string())?;
socket
.local_addr()
.map(|addr| addr.ip())
.map_err(|e| e.to_string())
}
let parsed_data = get_local_ip();
match parsed_data {
Ok(ip) => {
return format!("ws://{}:4152", ip.to_string());
}
Err(_) => {
return "not provided".to_string();
}
}
}

12
src/utils/log.rs Normal file
View File

@@ -0,0 +1,12 @@
use crate::structs::LogPayload;
use log::{error, info, warn};
pub fn log_info(data: LogPayload) -> String {
match data.status.as_str() {
"info" => info!("{}", data.message),
"warn" => warn!("{}", data.message),
"error" => error!("{}", data.message),
_ => error!("{}", "Wrong log call"),
}
"logged".to_string()
}

3
src/utils/mod.rs Normal file
View File

@@ -0,0 +1,3 @@
pub mod uuid;
pub mod address;
pub mod log;

7
src/utils/uuid.rs Normal file
View File

@@ -0,0 +1,7 @@
pub fn get_uuid() -> String {
use uuid::Uuid;
let id = Uuid::new_v4().to_string();
return id;
}

148
src/video_stream.rs Normal file
View File

@@ -0,0 +1,148 @@
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;
use std::time::Duration;
#[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(
"VIDEO ERROR",
"Screen capture is only supported on Windows",
);
return "screen capture unsupported";
}
#[cfg(target_os = "windows")]
{
let (stop_tx, stop_rx): (Sender<()>, Receiver<()>) = mpsc::channel();
let (status_tx, status_rx): (Sender<Result<(), String>>, Receiver<Result<(), String>>) = mpsc::channel();
let rtsp_url = if username.is_empty() || password.is_empty() {
format!("rtsp://{}:{}/{}", address, port, stream_path)
} else {
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,
]);
// Try to spawn ffmpeg process
let child_result = cmd.creation_flags(CREATE_NO_WINDOW).spawn();
match child_result {
Ok(mut child) => {
let _ = status_tx.send(Ok(()));
if stop_rx.recv().is_err() {}
let _ = child.kill();
}
Err(e) => {
let _ = status_tx.send(Err(format!("Failed to start FFmpeg: {}", e)));
// Return early, nothing else to do
}
}
});
// Save the stop channel so we can stop later
match STREAM_CTRL.lock() {
Ok(mut lock) => *lock = Some(stop_tx),
Err(e) => {
let _ = ctx.callback_data(
"VIDEO ERROR",
"Failed to acquire lock for stream control",
e.to_string(),
);
return "stream control lock error";
}
}
// Wait up to 2 seconds to see if ffmpeg started correctly
match status_rx.recv_timeout(Duration::from_secs(2)) {
Ok(Ok(())) => {
let _ = ctx.callback_null("VIDEO", "FFmpeg started successfully");
"starting video stream"
}
Ok(Err(e)) => {
let _ = ctx.callback_data("VIDEO ERROR", "FFmpeg failed to start", e);
"ffmpeg failed to start"
}
Err(_) => {
let _ = ctx.callback_null("VIDEO ERROR", "FFmpeg did not respond in time");
"ffmpeg did not respond"
}
}
}
#[cfg(not(any(target_os = "windows", target_os = "linux")))]
{
ctx.callback_null("VIDEO ERROR", "Screen capture is only supported on Windows");
"unsupported platform"
}
}
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(
"VIDEO ERROR",
"Failed to send stop signal",
e.to_string(),
);
"error sending stop"
} else {
let _ = ctx.callback_null("VIDEO", "Sent stop signal to FFmpeg");
"stopping video stream"
}
} else {
let _ = ctx.callback_null("VIDEO ERROR", "Tried to stop a nonexistent stream");
"no stream to stop"
}
}
Err(e) => {
let _ = ctx.callback_data(
"VIDEO ERROR",
"Failed to acquire lock for stop",
e.to_string(),
);
"lock error"
}
}
}

View File

@@ -1,114 +1,173 @@
use arma_rs::Context;
use lazy_static::lazy_static;
use log::info; use log::info;
use std::sync::mpsc::{self, Receiver, Sender}; use std::sync::mpsc::{self, Receiver, Sender};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::thread; use std::thread;
use ws::{listen, Message, Result as WsResult}; use ws::{listen, CloseCode, Handler, Handshake, Message, Result as WsResult, Sender as WsSender};
use crate::structs::{IntoMessage, LocationPayload}; use crate::structs::{IntoMessage, LocationPayload};
use lazy_static::lazy_static;
pub enum WsCommand { pub enum WsCommand {
SendMessage(String), SendMessage(String),
Stop, Stop,
}
enum WsEvent {
FirstClientConnected,
LastClientDisconnected,
} }
pub struct WsServer { pub struct WsServer {
pub(crate) tx: Sender<WsCommand>, pub(crate) tx: Sender<WsCommand>,
}
struct WsHandler {
out: WsSender,
clients: Arc<Mutex<Vec<WsSender>>>,
event_tx: Sender<WsEvent>,
}
impl Handler for WsHandler {
fn on_open(&mut self, _: Handshake) -> WsResult<()> {
let mut clients = self.clients.lock().unwrap();
clients.push(self.out.clone());
let count = clients.len();
info!("New client connected. Total clients: {}", count);
if count == 1 {
let _ = self.event_tx.send(WsEvent::FirstClientConnected);
}
Ok(())
}
fn on_close(&mut self, _: CloseCode, _: &str) {
let mut clients = self.clients.lock().unwrap();
clients.retain(|client| client.token() != self.out.token());
let count = clients.len();
info!("Client disconnected. Total clients: {}", count);
if count == 0 {
let _ = self.event_tx.send(WsEvent::LastClientDisconnected);
}
}
fn on_message(&mut self, msg: Message) -> WsResult<()> {
info!("Received: {}", msg);
Ok(())
}
} }
impl WsServer { impl WsServer {
pub fn start(&self, rx: Receiver<WsCommand>) { pub fn start(&self, ctx: Context, rx: Receiver<WsCommand>) {
if let Some(ref server) = *WEBSOCKET_SERVER.lock().unwrap() { if let Some(ref server) = *WEBSOCKET_SERVER.lock().unwrap() {
server.stop(); server.stop();
} }
let clients = Arc::new(Mutex::new(Vec::new())); let clients = Arc::new(Mutex::new(Vec::new()));
let clients_clone = Arc::clone(&clients); let clients_clone = Arc::clone(&clients);
let (event_tx, event_rx): (Sender<WsEvent>, Receiver<WsEvent>) = mpsc::channel();
thread::spawn(move || { // Start WebSocket listener in a background thread
let mut running = true; thread::spawn(move || {
listen("0.0.0.0:4152", |out| WsHandler {
out,
clients: Arc::clone(&clients_clone),
event_tx: event_tx.clone(),
}).expect("WebSocket server failed to start");
});
let ws_thread = thread::spawn(move || { // This thread owns ctx and reacts to commands and events
listen("0.0.0.0:4152", |out| { thread::spawn(move || {
let clients_inner = Arc::clone(&clients_clone); loop {
{ // WebSocket message commands
let mut clients_guard = clients_inner.lock().unwrap(); match rx.try_recv() {
clients_guard.push(out.clone()); Ok(WsCommand::SendMessage(message)) => {
} let clients_guard = clients.lock().unwrap();
for client in clients_guard.iter() {
let _ = client.send(message.clone());
}
}
Ok(WsCommand::Stop) => {
info!("Stopping WebSocket server.");
break;
}
Err(_) => {}
}
move |msg: Message| -> WsResult<()> { // Handle event callbacks with valid Context
info!("Received: {}", msg); match event_rx.try_recv() {
Ok(()) Ok(WsEvent::FirstClientConnected) => {
} let _ = ctx.callback_null("WEBSOCKET", "EUD connected");
}) }
.unwrap(); Ok(WsEvent::LastClientDisconnected) => {
}); let _ = ctx.callback_null("WEBSOCKET WARNING", "EUD disconnected");
}
Err(_) => {}
}
while running { thread::sleep(std::time::Duration::from_millis(10));
match rx.recv() { }
Ok(WsCommand::SendMessage(message)) => { });
let clients_guard = clients.lock().unwrap(); }
for client in clients_guard.iter() {
client.send(message.clone()).unwrap();
}
}
Ok(WsCommand::Stop) => {
running = false;
info!("Stopping WebSocket server.");
}
Err(error) => {
info!("Error receiving command: {}", error.to_string());
}
}
}
ws_thread.join().unwrap(); pub fn send_message<T: IntoMessage>(&self, payload: T) {
}); let message = payload.into_message();
} let _ = self.tx.send(WsCommand::SendMessage(message));
}
pub fn send_message<T: IntoMessage>(&self, payload: T) { pub fn stop(&self) {
let message = payload.into_message(); let _ = self.tx.send(WsCommand::Stop);
self.tx.send(WsCommand::SendMessage(message)).unwrap(); }
}
pub fn stop(&self) {
self.tx.send(WsCommand::Stop).unwrap();
}
} }
lazy_static! { lazy_static! {
static ref WEBSOCKET_SERVER: Arc<Mutex<Option<WsServer>>> = Arc::new(Mutex::new(None)); static ref WEBSOCKET_SERVER: Arc<Mutex<Option<WsServer>>> = Arc::new(Mutex::new(None));
} }
pub fn start() -> &'static str { pub fn start(ctx: Context) -> &'static str {
let (tx, rx): (Sender<WsCommand>, Receiver<WsCommand>) = mpsc::channel(); let (tx, rx): (Sender<WsCommand>, Receiver<WsCommand>) = mpsc::channel();
let server = WsServer { tx }; let server = WsServer { tx };
server.start(rx); server.start(ctx, rx);
let mut server_guard = WEBSOCKET_SERVER.lock().unwrap(); let mut server_guard = WEBSOCKET_SERVER.lock().unwrap();
*server_guard = Some(server); *server_guard = Some(server);
info!("WebSocket server started."); "Starting WebSocket Server"
"Starting WebSocket Server"
} }
pub fn message(payload: String) -> &'static str { pub fn message(ctx: Context, payload: String) -> &'static str {
if let Some(ref server) = *WEBSOCKET_SERVER.lock().unwrap() { if let Some(ref server) = *WEBSOCKET_SERVER.lock().unwrap() {
info!("Broadcasting message: {}", payload); info!("Broadcasting message: {}", payload); // why the fuck i did this?
server.send_message(payload); server.send_message(payload);
} else { } else {
info!("WebSocket server is not running."); let _ = ctx.callback_null("WEBSOCKET ERROR", "Websocket is not running");
} }
"Sending message to all WebSocket clients" "Sending message to all WebSocket clients"
} }
pub fn location(payload: LocationPayload) -> &'static str { pub fn location(ctx: Context, payload: LocationPayload) -> &'static str {
if let Some(ref server) = *WEBSOCKET_SERVER.lock().unwrap() { if let Some(ref server) = *WEBSOCKET_SERVER.lock().unwrap() {
server.send_message(payload); server.send_message(payload);
} else { } else {
info!("WebSocket server is not running."); let _ = ctx.callback_null("WEBSOCKET ERROR", "Websocket is not running");
} }
"sending location to all WebSocket clients"
"Sending location to all WebSocket clients"
}
pub fn stop(ctx: Context) -> &'static str {
if let Some(ref server) = *WEBSOCKET_SERVER.lock().unwrap() {
server.stop();
let _ = ctx.callback_null("WEBSOCKET WARNING", "Websocket stopped");
} else {
let _ = ctx.callback_null("WEBSOCKET ERROR", "Websocket is not running");
}
"Stopping WebSocket Server"
} }