From a1866a561b6eb43001b71d9f02bec851179d2f3d Mon Sep 17 00:00:00 2001
From: jonpas <jonpas33@gmail.com>
Date: Sat, 2 Jan 2016 14:23:14 +0100
Subject: [PATCH 1/4] Add Headless

---
 addons/headless/$PBOPREFIX$                   |   1 +
 addons/headless/ACE_Settings.hpp              |  20 +++
 addons/headless/CfgEventHandlers.hpp          |  19 +++
 addons/headless/CfgVehicles.hpp               |  37 +++++
 addons/headless/README.md                     |  16 ++
 .../headless/UI/Icon_Module_Headless_ca.paa   | Bin 0 -> 5625 bytes
 addons/headless/XEH_postInit.sqf              |  16 ++
 addons/headless/XEH_preInit.sqf               |  18 +++
 addons/headless/config.cpp                    |  17 ++
 .../functions/fnc_handleConnectHC.sqf         |  31 ++++
 .../functions/fnc_handleDisconnect.sqf        |  34 ++++
 .../headless/functions/fnc_handleInitPost.sqf |  31 ++++
 addons/headless/functions/fnc_moduleInit.sqf  |  27 ++++
 addons/headless/functions/fnc_rebalance.sqf   |  29 ++++
 .../headless/functions/fnc_transferGroups.sqf | 148 ++++++++++++++++++
 .../headless/functions/script_component.hpp   |   1 +
 addons/headless/script_component.hpp          |  15 ++
 addons/headless/stringtable.xml               |  33 ++++
 18 files changed, 493 insertions(+)
 create mode 100644 addons/headless/$PBOPREFIX$
 create mode 100644 addons/headless/ACE_Settings.hpp
 create mode 100644 addons/headless/CfgEventHandlers.hpp
 create mode 100644 addons/headless/CfgVehicles.hpp
 create mode 100644 addons/headless/README.md
 create mode 100644 addons/headless/UI/Icon_Module_Headless_ca.paa
 create mode 100644 addons/headless/XEH_postInit.sqf
 create mode 100644 addons/headless/XEH_preInit.sqf
 create mode 100644 addons/headless/config.cpp
 create mode 100644 addons/headless/functions/fnc_handleConnectHC.sqf
 create mode 100644 addons/headless/functions/fnc_handleDisconnect.sqf
 create mode 100644 addons/headless/functions/fnc_handleInitPost.sqf
 create mode 100644 addons/headless/functions/fnc_moduleInit.sqf
 create mode 100644 addons/headless/functions/fnc_rebalance.sqf
 create mode 100644 addons/headless/functions/fnc_transferGroups.sqf
 create mode 100644 addons/headless/functions/script_component.hpp
 create mode 100644 addons/headless/script_component.hpp
 create mode 100644 addons/headless/stringtable.xml

diff --git a/addons/headless/$PBOPREFIX$ b/addons/headless/$PBOPREFIX$
new file mode 100644
index 00000000000..553c595da06
--- /dev/null
+++ b/addons/headless/$PBOPREFIX$
@@ -0,0 +1 @@
+z\ace\addons\headless
diff --git a/addons/headless/ACE_Settings.hpp b/addons/headless/ACE_Settings.hpp
new file mode 100644
index 00000000000..6c8246a06be
--- /dev/null
+++ b/addons/headless/ACE_Settings.hpp
@@ -0,0 +1,20 @@
+class ACE_Settings {
+    class GVAR(Enabled) {
+        value = 0;
+        typeName = "BOOL";
+        displayName = ECSTRING(common,Enabled);
+        description = CSTRING(EnabledDesc);
+    };
+    class GVAR(Delay) {
+        value = DELAY_DEFAULT;
+        typeName = "SCALAR";
+        displayName = CSTRING(Delay);
+        description = CSTRING(DelayDesc);
+    };
+    class GVAR(Log) {
+        value = 0;
+        typeName = "BOOL";
+        displayName = CSTRING(Log);
+        description = CSTRING(LogDesc);
+    };
+};
diff --git a/addons/headless/CfgEventHandlers.hpp b/addons/headless/CfgEventHandlers.hpp
new file mode 100644
index 00000000000..864912227c8
--- /dev/null
+++ b/addons/headless/CfgEventHandlers.hpp
@@ -0,0 +1,19 @@
+class Extended_PreInit_EventHandlers {
+    class ADDON {
+        init = QUOTE(call COMPILE_FILE(XEH_preInit));
+    };
+};
+
+class Extended_PostInit_EventHandlers {
+    class ADDON {
+        init = QUOTE(call COMPILE_FILE(XEH_postInit));
+    };
+};
+
+class Extended_InitPost_EventHandlers {
+    class AllVehicles {
+        class ADDON {
+            serverInit = QUOTE(_this call FUNC(handleInitPost));
+        };
+    };
+};
diff --git a/addons/headless/CfgVehicles.hpp b/addons/headless/CfgVehicles.hpp
new file mode 100644
index 00000000000..aa3837f6aed
--- /dev/null
+++ b/addons/headless/CfgVehicles.hpp
@@ -0,0 +1,37 @@
+class CfgVehicles {
+    class ACE_Module;
+    class GVAR(module): ACE_Module {
+        author = ECSTRING(common,ACETeam);
+        category = "ACE_missionModules";
+        displayName = CSTRING(Module);
+        function = QFUNC(moduleInit);
+        scope = 2;
+        isGlobal = 1; // Global
+        isTriggerActivated = 0;
+        isDisposable = 0;
+        icon = QUOTE(PATHTOF(UI\Icon_Module_Headless_ca.paa));
+        class Arguments {
+            class Enabled {
+                displayName = ECSTRING(common,Enabled);
+                description = CSTRING(EnabledDesc);
+                typeName = "BOOL";
+                defaultValue = 0;
+            };
+            class Delay {
+                displayName = CSTRING(Delay);
+                description = CSTRING(DelayDesc);
+                typeName = "NUMBER";
+                defaultValue = DELAY_DEFAULT;
+            };
+            class Log {
+                displayName = CSTRING(Log);
+                description = CSTRING(LogDesc);
+                typeName = "BOOL";
+                defaultValue = 0;
+            };
+        };
+        class ModuleDescription {
+            description = CSTRING(ModuleDesc);
+        };
+    };
+};
diff --git a/addons/headless/README.md b/addons/headless/README.md
new file mode 100644
index 00000000000..ab3d1ef4034
--- /dev/null
+++ b/addons/headless/README.md
@@ -0,0 +1,16 @@
+ace_headless
+============
+
+Adds automatic passing of AI groups to (up to 3) Headless Clients.
+- Automatic Headless Client recognition
+- Event-based transferring (on unit spawn, Headless Client connect and disconnect)
+- Round-robin transferring when more than 1 Headless Client is present
+- Mission makers can use the following to prevent a group from transferring to a Headless Client:
+    `this setVariable ["ace_headless_blacklist", true, true];`
+
+
+## Maintainers
+
+The people responsible for merging changes to this component or answering potential questions.
+
+- [Jonpas](http://github.com/jonpas)
diff --git a/addons/headless/UI/Icon_Module_Headless_ca.paa b/addons/headless/UI/Icon_Module_Headless_ca.paa
new file mode 100644
index 0000000000000000000000000000000000000000..a3e23a8537d9246e68096d9513e9248ef86da652
GIT binary patch
literal 5625
zcmeHLO>7%Q6n^8MxJ8X^D!UQ~$cjdS3q2w($g@IqVu>n7*afMoL|t)!L&&KT0?C_J
zZIwfb6VYQ-P8Ent+k-i9uu;#5ic_Q@(u$v3AWae4s;Z~od$SvAyS8iLQb>8;AAdVD
z@6Gq-&F;L3o2e<c^xD*6H%3Ipn70JK?H&rSlux;@=NT)UT>fT0Kcn&A!+ADM^ht*3
ze3s}3oWqGGO_YXV+YB9$f5T!*H2z5BHlxx`7mJ2=*Z5!@hYYK)t3K(-Bo2OFgp6K4
z)g}weqMyp;GR$oEp9ysKhoUA$(ZFxzQpu2B4%-){eFuNI*P)0bdbHN8Kdc|x>%eJJ
zfqtbF^bf~>u>Lmtok26mzfqb#IeF-N@mC2lusu<P>o9-KcPeb-@&hr4x~<RuhUG7f
z|Hfqyf5rT#ZM=X!Q)rW}_9*>l#~;BAo9yp0dIx{^#ciGEc6R>C_#*syK0s=Aen6$b
zK9>iyk2qxgh46of_6UAu?IHYOpX1-Q$Mgr5`PH+3o&6n*;U9#g^;g;Y>xshc<2%@2
zJF9-5P##H<%)i3kFuw3U?;mfJAd1oEgv8^)vK~n%9;XuJFXsID9P?4iRTMWDjNb@W
z8+gNG<&ZNsg)hDk8hB~KpBJpP>psq|kHYrYzSc&rbz`0RyKn$K<AeXA@K^8%DJFj_
z;7i(&+Uwwu`tE<9e>C0NsJ4#n5BSF^3i7uD{$L+Zo4XU{W{%4>93FkDb#C3?Se5!(
zzcGq2_*dzO`lrJ9Ry@AZ+w;Ix;qPL8>~QIdZ{?rL;b8vw-v7^khmSuc@pAW7_<qaM
zn16c0f11{L{9U&zpLo>n;J9Nk5PKK+#9)UUUT}~H{)OcAz#Un|xVm*Uz&hfct=_r(
z!5zlwOeX6!P@bBT*EAef6s%`|3hKuVO?xpMj583?U){pTUff%>_Uy77Pq3^n)M7Ze
zJPV;nbu(q`wgS=yd=}fwy+&%Y-M(&_KK6q&62mIn2wls3Y^WriAkE2Uk)?RKTA21A
z>i}*q*WTi@tMwI0L%3XfCua8t7+DkQC!P!SWipJ-g~{zSsu$)kWd9&>llr}}=JNVE
zvCmnV#Vg`JcrdWnyM32_85T}e|C$Q?s2BFWnND6Oju+cwuQ@YEi8=J&Vs6Xg;Jp9Y
zsBq3JB!4+kdBIwX$F(=JMGGrIJQj;EE8^h#DC~P3+gx79HjTCZ@cbn$kiN?3+xYB*
z5pNYtCJ1k{Vd>;8jDx&jW<RXHG`0c>%dyW)<Ky3M{Ak^(l+*k<mH+j4KE(QWb7uD2
zGqctZn>AJX>TVE*CDP5qd#F%Z<N7X`=;pq0V43#7aoxNa!v>HHlfq2YO{CgEeXzy6
zu;SpxHY(TttbF<M`$sIzc-FhNbaeIfxah}j!1mW7tS_|h%ifQf`nyN!OVU4ntGr{b
zytH=7PFvo*>a4$ucJV6oyH`%0&wn`kbx8AS@x_Y=3a|=6&q#k!KmJkOy~2pq>d!B~
dx_0U4GGkU7!w>#E`00DkFO2QkZ5x5V$UQJVxmy4L

literal 0
HcmV?d00001

diff --git a/addons/headless/XEH_postInit.sqf b/addons/headless/XEH_postInit.sqf
new file mode 100644
index 00000000000..b91bb537b5b
--- /dev/null
+++ b/addons/headless/XEH_postInit.sqf
@@ -0,0 +1,16 @@
+#include "script_component.hpp"
+
+// Exit on player clients that are not hosts
+if (hasInterface && !isServer) exitWith {};
+
+["SettingsInitialized", {
+    // Exit if HC transferring disabled
+    if (!GVAR(Enabled)) exitWith {};
+
+    if (isServer) then {
+        addMissionEventHandler ["HandleDisconnect", {_this call FUNC(handleDisconnect)}];
+    } else {
+        // Register HC on server (this part happens on HC only)
+        ["ACE_HeadlessClientJoined", [player]] call EFUNC(common,serverEvent);
+    };
+}] call EFUNC(common,addEventHandler);
diff --git a/addons/headless/XEH_preInit.sqf b/addons/headless/XEH_preInit.sqf
new file mode 100644
index 00000000000..23e1541c6ca
--- /dev/null
+++ b/addons/headless/XEH_preInit.sqf
@@ -0,0 +1,18 @@
+#include "script_component.hpp"
+
+ADDON = false;
+
+PREP(handleConnectHC);
+PREP(handleDisconnect);
+PREP(handleInitPost);
+PREP(moduleInit);
+PREP(rebalance);
+PREP(transferGroups);
+
+if (isServer) then {
+    GVAR(headlessClients) = [];
+    GVAR(inRebalance) = false;
+    ["ACE_HeadlessClientJoined", FUNC(handleConnectHC)] call EFUNC(common,addEventHandler);
+};
+
+ADDON = true;
diff --git a/addons/headless/config.cpp b/addons/headless/config.cpp
new file mode 100644
index 00000000000..9c4c6b91204
--- /dev/null
+++ b/addons/headless/config.cpp
@@ -0,0 +1,17 @@
+#include "script_component.hpp"
+
+class CfgPatches {
+    class ADDON {
+        units[] = {};
+        weapons[] = {};
+        requiredVersion = REQUIRED_VERSION;
+        requiredAddons[] = {"ace_common"};
+        author[]= {"Jonpas"};
+        authorUrl = "https://github.com/jonpas";
+        VERSION_CONFIG;
+    };
+};
+
+#include "ACE_Settings.hpp"
+#include "CfgEventHandlers.hpp"
+#include "CfgVehicles.hpp"
diff --git a/addons/headless/functions/fnc_handleConnectHC.sqf b/addons/headless/functions/fnc_handleConnectHC.sqf
new file mode 100644
index 00000000000..f0273b8fda6
--- /dev/null
+++ b/addons/headless/functions/fnc_handleConnectHC.sqf
@@ -0,0 +1,31 @@
+/*
+ * Author: Jonpas
+ * Registers connected Headless Client for use.
+ *
+ * Arguments:
+ * 0: Headless Client <OBJECT>
+ *
+ * Return Value:
+ * None
+ *
+ * Example:
+ * [headlessClient] call ace_headless_handleConnectHC;
+ *
+ * Public: No
+ */
+#include "script_component.hpp"
+
+params ["_headlessClient"];
+
+// Exit if already registered
+if (_headlessClient in GVAR(headlessClients)) exitWith {};
+
+// Register for use
+GVAR(headlessClients) pushBack _headlessClient;
+
+if (GVAR(Log)) then {
+    ACE_LOGINFO_1("Registered HC: %1",_headlessClient);
+};
+
+// Rebalance
+[true] call FUNC(rebalance);
diff --git a/addons/headless/functions/fnc_handleDisconnect.sqf b/addons/headless/functions/fnc_handleDisconnect.sqf
new file mode 100644
index 00000000000..dc203794587
--- /dev/null
+++ b/addons/headless/functions/fnc_handleDisconnect.sqf
@@ -0,0 +1,34 @@
+/*
+ * Author: Jonpas
+ * Removes Headless Client from use.
+ *
+ * Arguments:
+ * 0: Object <OBJECT>
+ *
+ * Return Value:
+ * Transfer To Server <BOOL>
+ *
+ * Example:
+ * [unit] call ace_headless_handleDisconnect;
+ *
+ * Public: No
+ */
+#include "script_component.hpp"
+
+params ["_object"];
+
+// Exit if not HC
+if !(_object in GVAR(headlessClients)) exitWith {};
+
+// Remove HC
+GVAR(headlessClients) deleteAt (GVAR(headlessClients) find _object);
+
+if (GVAR(Log)) then {
+    ACE_LOGINFO_1("Removed HC: %1",_object);
+};
+
+// Rebalance
+[true] call FUNC(rebalance);
+
+// Prevent transferring of HC to server
+false
diff --git a/addons/headless/functions/fnc_handleInitPost.sqf b/addons/headless/functions/fnc_handleInitPost.sqf
new file mode 100644
index 00000000000..fd75367ce75
--- /dev/null
+++ b/addons/headless/functions/fnc_handleInitPost.sqf
@@ -0,0 +1,31 @@
+/*
+ * Author: Jonpas
+ * Request a rebalance.
+ *
+ * Arguments:
+ * 0: Object <OBJECT>
+ *
+ * Return Value:
+ * None
+ *
+ * Example:
+ * [object] call ace_headless_handleInitPost;
+ *
+ * Public: No
+ */
+#include "script_component.hpp"
+
+params ["_object"];
+
+TRACE_1("InitPost",_object);
+
+// Delay until settings are initialized (for checking if HC trasnferring is enabled)
+if (!EGVAR(common,settingsInitFinished)) exitWith {
+    EGVAR(common,runAtSettingsInitialized) pushBack [FUNC(handleInitPost), _this];
+};
+
+// Exit if HC transferring disabled or object not a unit (including unit inside vehicle) or is player
+if (!GVAR(Enabled) || {!(_object in allUnits)} || {isPlayer _object}) exitWith {};
+
+// Rebalance
+[false] call FUNC(rebalance);
diff --git a/addons/headless/functions/fnc_moduleInit.sqf b/addons/headless/functions/fnc_moduleInit.sqf
new file mode 100644
index 00000000000..dc06d95b3b4
--- /dev/null
+++ b/addons/headless/functions/fnc_moduleInit.sqf
@@ -0,0 +1,27 @@
+/*
+ * Author: Jonpas
+ * Initializes the Headless module.
+ *
+ * Arguments:
+ * 0: The module logic <LOGIC>
+ * 1: Units <ARRAY> (Unused)
+ * 2: Activated <BOOL>
+ *
+ * Return Value:
+ * None
+ *
+ * Public: No
+ */
+#include "script_component.hpp"
+
+if (!isServer) exitWith {};
+
+params ["_logic", "", "_activated"];
+
+if (!_activated) exitWith {};
+
+[_logic, QGVAR(Enabled), "Enabled"] call EFUNC(common,readSettingFromModule);
+[_logic, QGVAR(Delay), "Delay"] call EFUNC(common,readSettingFromModule);
+[_logic, QGVAR(Log), "Log"] call EFUNC(common,readSettingFromModule);
+
+ACE_LOGINFO("Headless Module Initialized.");
diff --git a/addons/headless/functions/fnc_rebalance.sqf b/addons/headless/functions/fnc_rebalance.sqf
new file mode 100644
index 00000000000..1c792ff94a9
--- /dev/null
+++ b/addons/headless/functions/fnc_rebalance.sqf
@@ -0,0 +1,29 @@
+/*
+ * Author: Jonpas
+ * Rebalance AI groups accross HCs.
+ *
+ * Arguments:
+ * 0: Force <BOOL>
+ *
+ * Return Value:
+ * None
+ *
+ * Example:
+ * [false] call ace_headless_rebalance;
+ *
+ * Public: No
+ */
+#include "script_component.hpp"
+
+params ["_force"];
+
+TRACE_3("Rebalance",GVAR(inRebalance),GVAR(headlessClients),_force);
+
+// Exit if waiting for rebalance or no HCs present
+if (GVAR(inRebalance) || {GVAR(headlessClients) isEqualTo []}) exitWith {};
+
+// Transfer after rebalance delay
+[FUNC(transferGroups), [_force], GVAR(Delay)] call EFUNC(common,waitAndExecute);
+
+// Currently in rebalance flag
+GVAR(inRebalance) = true;
diff --git a/addons/headless/functions/fnc_transferGroups.sqf b/addons/headless/functions/fnc_transferGroups.sqf
new file mode 100644
index 00000000000..7dbae156f18
--- /dev/null
+++ b/addons/headless/functions/fnc_transferGroups.sqf
@@ -0,0 +1,148 @@
+/*
+ * Author: Jonpas
+ * Transfers AI groups to Headess Client(s).
+ *
+ * Arguments:
+ * 0: Force <BOOL>
+ *
+ * Return Value:
+ * None
+ *
+ * Example:
+ * [false] call ace_headless_fnc_transferGroups;
+ *
+ * Public: No
+ */
+#include "script_component.hpp"
+
+params ["_force"];
+
+GVAR(headlessClients) params [
+    ["_HC1", objNull, [objNull] ],
+    ["_HC2", objNull, [objNull] ],
+    ["_HC3", objNull, [objNull] ]
+];
+
+if (GVAR(Log)) then {
+    ACE_LOGINFO_2("Present HCs: %1 - Full Rebalance: %2",GVAR(headlessClients),_force);
+};
+
+
+// Enable round-robin load balancing if more than one HC is present
+private _loadBalance = [false, true] select (count GVAR(headlessClients) > 1);
+
+
+// Get IDs and determine first HC to start with
+private _idHC1 = -1;
+private _idHC2 = -1;
+private _idHC3 = -1;
+private _currentHC = 0;
+
+if (!local _HC1) then {
+    _idHC1 = owner _HC1;
+    _currentHC = 1;
+};
+
+if (!local _HC2) then {
+    _idHC2 = owner _HC2;
+
+    if (_currentHC == 0) then {
+        _currentHC = 2;
+    };
+};
+
+if (!local _HC3) then {
+    _idHC3 = owner _HC3;
+
+    if (_currentHC == 0) then {
+        _currentHC = 3;
+    };
+};
+
+
+// Prepare statistics
+private _numTransferredHC1 = 0;
+private _numTransferredHC2 = 0;
+private _numTransferredHC3 = 0;
+
+
+// Transfer AI groups
+{
+    private _transfer = true;
+
+    // No transfer if empty group
+    if (_x isEqualTo []) then {
+        _transfer = false;
+    };
+
+    if (_transfer) then {
+        {
+            // No transfer if already transferred
+            if (!_force && {(owner _x) in [_idHC1, _idHC2, _idHC3]}) exitWith {
+                _transfer = false;
+            };
+
+            // No transfer if player in this group
+            if (isPlayer _x) exitWith {
+                _transfer = false;
+            };
+
+            // No transfer if any unit in group is blacklisted
+            if (_x getVariable [QGVAR(blacklist), false]) exitWith {
+                _transfer = false;
+            };
+
+            // No transfer if vehicle unit is in or crew in that vehicle is blacklisted
+            if (vehicle _x != _x && {(vehicle _x) getVariable [QGVAR(blacklist), false]}) exitWith {
+                _transfer = false;
+            };
+        } forEach (units _x);
+    };
+
+
+    // Round robin between HCs if load balance enabled, else pass all to one HC
+    if (_transfer) then {
+        switch (_currentHC) do {
+            case 1: {
+                private _transferred = _x setGroupOwner _idHC1;
+                if (_loadBalance) then {
+                    _currentHC = [3, 2] select (!local _HC2);
+                };
+                if (_transferred) then {
+                    _numTransferredHC1 = _numTransferredHC1 + 1;
+                };
+            };
+            case 2: {
+                private _transferred = _x setGroupOwner _idHC2;
+                if (_loadBalance) then {
+                    _currentHC = [1, 3] select (!local _HC3);
+                };
+                if (_transferred) then {
+                    _numTransferredHC2 = _numTransferredHC2 + 1;
+                };
+            };
+            case 3: {
+                private _transferred = _x setGroupOwner _idHC3;
+                if (_loadBalance) then {
+                    _currentHC = [2, 1] select (!local _HC1);
+                };
+                if (_transferred) then {
+                    _numTransferredHC3 = _numTransferredHC3 + 1;
+                };
+            };
+            default {
+                TRACE_1("No Valid HC to transfer to",_currentHC);
+            };
+        };
+    };
+} forEach allGroups;
+
+
+if (GVAR(Log)) then {
+    private _numTransferredTotal = _numTransferredHC1 + _numTransferredHC2 + _numTransferredHC3;
+    ACE_LOGINFO_4("Groups Transferred: Total: %1 - HC1: %2 - HC2: %3 - HC3: %4",_numTransferredTotal,_numTransferredHC1,_numTransferredHC2,_numTransferredHC3);
+};
+
+
+// Allow rebalance flag
+GVAR(inRebalance) = false;
diff --git a/addons/headless/functions/script_component.hpp b/addons/headless/functions/script_component.hpp
new file mode 100644
index 00000000000..a38efad3a9f
--- /dev/null
+++ b/addons/headless/functions/script_component.hpp
@@ -0,0 +1 @@
+#include "\z\ace\addons\headless\script_component.hpp"
diff --git a/addons/headless/script_component.hpp b/addons/headless/script_component.hpp
new file mode 100644
index 00000000000..c69bd780c90
--- /dev/null
+++ b/addons/headless/script_component.hpp
@@ -0,0 +1,15 @@
+#define COMPONENT headless
+#include "\z\ace\addons\main\script_mod.hpp"
+
+#ifdef DEBUG_ENABLED_HEADLESS
+    #define DEBUG_MODE_FULL
+#endif
+
+#ifdef DEBUG_SETTINGS_HEADLESS
+    #define DEBUG_SETTINGS DEBUG_SETTINGS_HEADLESS
+#endif
+
+#include "\z\ace\addons\main\script_macros.hpp"
+
+
+#define DELAY_DEFAULT 15
diff --git a/addons/headless/stringtable.xml b/addons/headless/stringtable.xml
new file mode 100644
index 00000000000..4bea985f9fd
--- /dev/null
+++ b/addons/headless/stringtable.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project name="ACE">
+    <Package name="Headless">
+        <Key ID="STR_ACE_Headless_Module">
+            <English>Headless</English>
+            <German>Headless</German>
+        </Key>
+        <Key ID="STR_ACE_Headless_ModuleDesc">
+            <English>This module allows you to setup automatic transferring of AI to Headless Clients. (Default: No)</English>
+            <German>Dieses Modul erlaubt es dir, die KI automatisch auf einen Headless Client zu transferieren. (Standard: Nein) </German>
+        </Key>
+        <Key ID="STR_ACE_Headless_EnabledDesc">
+            <English>Enables transferring of AI to Headless Clients.</English>
+            <German>Aktiviert denTransfer der KI auf Headless Clients.</German>
+        </Key>
+        <Key ID="STR_ACE_Headless_Delay">
+            <English>Delay</English>
+            <German>Verzögerung</German>
+        </Key>
+        <Key ID="STR_ACE_Headless_DelayDesc">
+            <English>Minimal delay between transfers, in seconds. (Default: 15)</English>
+            <German>Minimale Verzögerung zwischen Transfers in Sekunden. (Standard: 15)</German>
+        </Key>
+        <Key ID="STR_ACE_Headless_Log">
+            <English>Log</English>
+            <German>Protokolldatei anlegen</German>
+        </Key>
+        <Key ID="STR_ACE_Headless_LogDesc">
+            <English>Log transfer statistics and Headless Client (dis)connections to RPT. (Default: No)</English>
+            <German>Zeichnet Transferstatistiken, Verbindungen und Verbindungsabbrüche in einer RPT-Datei auf. (Standard: Nein)</German>
+        </Key>
+    </Package>
+</Project>

From 971d8d1e02b1d144e3210e6be7555f2018fb14a4 Mon Sep 17 00:00:00 2001
From: jonpas <jonpas33@gmail.com>
Date: Sat, 2 Jan 2016 14:35:39 +0100
Subject: [PATCH 2/4] Add @System98 to AUTHORS.txt

---
 AUTHORS.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/AUTHORS.txt b/AUTHORS.txt
index 5eee93a7f62..023cdf18ead 100644
--- a/AUTHORS.txt
+++ b/AUTHORS.txt
@@ -103,6 +103,7 @@ Robert Boklahánics <bokirobi@gmail.com>
 ruPaladin <happyworm24@rambler.ru>
 simon84 <badguy360th@gmail.com>
 Sniperwolf572 <tenga6@gmail.com>
+System98
 SzwedzikPL <szwedzikpl@gmail.com>
 Tachi <zaveruha007@gmail.com>
 Toaster <jonathan.pereira@gmail.com>

From 6edd7ab093b37eff58609d2f5a4fd46074a7dd8b Mon Sep 17 00:00:00 2001
From: jonpas <jonpas33@gmail.com>
Date: Sat, 2 Jan 2016 14:42:05 +0100
Subject: [PATCH 3/4] Change ACE_HeadlessClientJoined event to be global and
 always triggered

---
 addons/headless/XEH_postInit.sqf                  | 12 ++++++------
 addons/headless/functions/fnc_handleConnectHC.sqf |  9 +++++++--
 2 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/addons/headless/XEH_postInit.sqf b/addons/headless/XEH_postInit.sqf
index b91bb537b5b..7b8a4615dcc 100644
--- a/addons/headless/XEH_postInit.sqf
+++ b/addons/headless/XEH_postInit.sqf
@@ -4,13 +4,13 @@
 if (hasInterface && !isServer) exitWith {};
 
 ["SettingsInitialized", {
-    // Exit if HC transferring disabled
-    if (!GVAR(Enabled)) exitWith {};
-
     if (isServer) then {
-        addMissionEventHandler ["HandleDisconnect", {_this call FUNC(handleDisconnect)}];
+        // Add disconnect EH if HC transferring enabled
+        if (GVAR(Enabled)) then {
+            addMissionEventHandler ["HandleDisconnect", {_this call FUNC(handleDisconnect)}];
+        };
     } else {
-        // Register HC on server (this part happens on HC only)
-        ["ACE_HeadlessClientJoined", [player]] call EFUNC(common,serverEvent);
+        // Register HC (this part happens on HC only)
+        ["ACE_HeadlessClientJoined", [player]] call EFUNC(common,globalEvent);
     };
 }] call EFUNC(common,addEventHandler);
diff --git a/addons/headless/functions/fnc_handleConnectHC.sqf b/addons/headless/functions/fnc_handleConnectHC.sqf
index f0273b8fda6..19293e92294 100644
--- a/addons/headless/functions/fnc_handleConnectHC.sqf
+++ b/addons/headless/functions/fnc_handleConnectHC.sqf
@@ -17,8 +17,13 @@
 
 params ["_headlessClient"];
 
-// Exit if already registered
-if (_headlessClient in GVAR(headlessClients)) exitWith {};
+// Delay until settings are initialized (for checking if HC trasnferring is enabled)
+if (!EGVAR(common,settingsInitFinished)) exitWith {
+    EGVAR(common,runAtSettingsInitialized) pushBack [FUNC(handleConnectHC), _this];
+};
+
+// Exit if HC transferring disabled or HC already registered
+if (!GVAR(Enabled) || {_headlessClient in GVAR(headlessClients)}) exitWith {};
 
 // Register for use
 GVAR(headlessClients) pushBack _headlessClient;

From b93183fef92867331f9662e0a863335a06295ee8 Mon Sep 17 00:00:00 2001
From: jonpas <jonpas33@gmail.com>
Date: Sun, 3 Jan 2016 14:05:50 +0100
Subject: [PATCH 4/4] Shorten if check

---
 addons/headless/functions/fnc_transferGroups.sqf | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/addons/headless/functions/fnc_transferGroups.sqf b/addons/headless/functions/fnc_transferGroups.sqf
index 7dbae156f18..892e9b58ff9 100644
--- a/addons/headless/functions/fnc_transferGroups.sqf
+++ b/addons/headless/functions/fnc_transferGroups.sqf
@@ -68,12 +68,8 @@ private _numTransferredHC3 = 0;
 
 // Transfer AI groups
 {
-    private _transfer = true;
-
     // No transfer if empty group
-    if (_x isEqualTo []) then {
-        _transfer = false;
-    };
+    private _transfer = !(_x isEqualTo []);
 
     if (_transfer) then {
         {