diff --git a/addons/common/XEH_PREP.hpp b/addons/common/XEH_PREP.hpp
index 5a2893eeaf3..9d887283b35 100644
--- a/addons/common/XEH_PREP.hpp
+++ b/addons/common/XEH_PREP.hpp
@@ -30,6 +30,7 @@ PREP(changeProjectileDirection);
PREP(checkFiles);
PREP(checkFiles_diagnoseACE);
PREP(checkPBOs);
+PREP(checkVersionNumber);
PREP(claim);
PREP(claimSafeServer);
PREP(codeToString);
diff --git a/addons/common/functions/fnc_checkFiles.sqf b/addons/common/functions/fnc_checkFiles.sqf
index 39e2bac3ace..7b90a1b0a85 100644
--- a/addons/common/functions/fnc_checkFiles.sqf
+++ b/addons/common/functions/fnc_checkFiles.sqf
@@ -15,15 +15,20 @@
* Public: No
*/
+// Don't execute in scheduled environment
+if (canSuspend) exitWith {
+ [FUNC(checkFiles), nil] call CBA_fnc_directCall;
+};
+
///////////////
-// check addons
+// Check addons
///////////////
-private _mainCfg = configFile >> "CfgPatches" >> "ace_main";
-private _mainVersion = getText (_mainCfg >> "versionStr");
-private _mainSource = configSourceMod _mainCfg;
+private _cfgPatches = configFile >> "CfgPatches";
+private _mainVersion = getText (_cfgPatches >> "ace_main" >> "versionStr");
+private _mainSource = configSourceMod (_cfgPatches >> "ace_main");
-//CBA Versioning check - close main display if using incompatible version
-private _cbaVersionAr = getArray (configFile >> "CfgPatches" >> "cba_main" >> "versionAr");
+// CBA Versioning check - close main display if using incompatible version
+private _cbaVersionAr = getArray (_cfgPatches >> "cba_main" >> "versionAr");
private _cbaRequiredAr = getArray (configFile >> "CfgSettings" >> "CBA" >> "Versioning" >> "ACE" >> "dependencies" >> "CBA") select 1;
private _cbaVersionStr = _cbaVersionAr joinString ".";
@@ -31,53 +36,62 @@ private _cbaRequiredStr = _cbaRequiredAr joinString ".";
INFO_3("ACE is version %1 - CBA is version %2 (min required %3)",_mainVersion,_cbaVersionStr,_cbaRequiredStr);
-if ([_cbaRequiredAr, _cbaVersionAr] call cba_versioning_fnc_version_compare) then {
+if ([_cbaRequiredAr, _cbaVersionAr] call CBA_versioning_fnc_version_compare) then {
private _errorMsg = format ["CBA version %1 is outdated (required %2)", _cbaVersionStr, _cbaRequiredStr];
ERROR(_errorMsg);
+
if (hasInterface) then {
- ["[ACE] ERROR", _errorMsg, {findDisplay 46 closeDisplay 0}] call FUNC(errorMessage);
+ ["[ACE] ERROR", _errorMsg] call FUNC(errorMessage);
};
};
-//private _addons = activatedAddons; // broken with High-Command module, see #2134
-private _addons = (cba_common_addons select {(_x select [0,4]) == "ace_"}) apply {toLowerANSI _x};
+//private _addons = activatedAddons; // Broken with High-Command module, see #2134
+private _addons = (CBA_common_addons select {(_x select [0, 4]) == "ace_"}) apply {toLowerANSI _x};
+
private _oldAddons = [];
private _oldSources = [];
private _oldCompats = [];
+
{
private _addonCfg = configFile >> "CfgPatches" >> _x;
private _addonVersion = getText (_addonCfg >> "versionStr");
+
if (_addonVersion != _mainVersion) then {
private _addonSource = configSourceMod _addonCfg;
+
_oldSources pushBackUnique _addonSource;
+
+ // Check ACE install
call FUNC(checkFiles_diagnoseACE);
+ // Don't block game if it's just an old compat pbo
if ((_x select [0, 10]) != "ace_compat") then {
- if (hasInterface) then {
- _oldAddons pushBack _x;
- };
+ _oldAddons pushBack _x;
} else {
- _oldCompats pushBack [_x, _addonVersion]; // Don't block game if it's just an old compat pbo
+ _oldCompats pushBack [_x, _addonVersion];
};
};
} forEach _addons;
if (_oldAddons isNotEqualTo []) then {
- _oldAddons = _oldAddons apply { format ["%1.pbo", _x] };
- private _errorMsg = "";
- if (count _oldAddons > 3) then {
- _errorMsg = format ["The following files are outdated: %1, and %2 more.
ACE Main version is %3 from %4.
Loaded mods with outdated ACE files: %5", (_oldAddons select [0, 3]) joinString ", ", (count _oldAddons) -3, _mainVersion, _mainSource, (_oldSources joinString ", ")];
+ _oldAddons = _oldAddons apply {format ["%1.pbo", _x]};
+
+ private _errorMsg = if (count _oldAddons > 3) then {
+ format ["The following files are outdated: %1, and %2 more.
ACE Main version is %3 from %4.
Loaded mods with outdated ACE files: %5", (_oldAddons select [0, 3]) joinString ", ", (count _oldAddons) - 3, _mainVersion, _mainSource, _oldSources joinString ", "];
} else {
- _errorMsg = format ["The following files are outdated: %1.
ACE Main version is %2 from %3.
Loaded mods with outdated ACE files: %4", (_oldAddons) joinString ", ", _mainVersion, _mainSource, (_oldSources) joinString ", "];
+ format ["The following files are outdated: %1.
ACE Main version is %2 from %3.
Loaded mods with outdated ACE files: %4", _oldAddons joinString ", ", _mainVersion, _mainSource, _oldSources joinString ", "];
};
+
if (hasInterface) then {
- ["[ACE] ERROR", _errorMsg, {findDisplay 46 closeDisplay 0}] call FUNC(errorMessage);
+ ["[ACE] ERROR", _errorMsg] call FUNC(errorMessage);
};
+
ERROR(_errorMsg);
};
if (_oldCompats isNotEqualTo []) then {
_oldCompats = _oldCompats apply {format ["%1 (%2)", _x select 0, _x select 1]};
+
[{
// Lasts for ~10 seconds
ERROR_WITH_TITLE_3("The following ACE compatiblity PBOs are outdated","%1. ACE Main version is %2 from %3.",_this select 0,_this select 1,_this select 2);
@@ -85,9 +99,10 @@ if (_oldCompats isNotEqualTo []) then {
};
///////////////
-// check extensions
+// Check extensions
///////////////
private _platform = toLowerANSI (productVersion select 6);
+
if (!isServer && {_platform in ["linux", "osx"]}) then {
// Linux and OSX client ports do not support extensions at all
INFO("Operating system does not support extensions");
@@ -101,8 +116,10 @@ if (!isServer && {_platform in ["linux", "osx"]}) then {
if ((_isWindows || _isLinux) && {_isClient || _isServer}) then {
private _versionEx = _extension callExtension "version";
+
if (_versionEx == "") then {
private _extensionFile = _extension;
+
if (productVersion select 7 == "x64") then {
_extensionFile = format ["%1_x64", _extensionFile];
};
@@ -114,7 +131,7 @@ if (!isServer && {_platform in ["linux", "osx"]}) then {
ERROR(_errorMsg);
if (hasInterface) then {
- ["[ACE] ERROR", _errorMsg, {findDisplay 46 closeDisplay 0}] call FUNC(errorMessage);
+ ["[ACE] ERROR", _errorMsg] call FUNC(errorMessage);
};
} else {
// Print the current extension version
@@ -123,54 +140,66 @@ if (!isServer && {_platform in ["linux", "osx"]}) then {
};
} forEach ("true" configClasses (configFile >> "ACE_Extensions"));
};
+
if (isArray (configFile >> "ACE_Extensions" >> "extensions")) then {
WARNING("extensions[] array no longer supported");
};
///////////////
-// check server version/addons
+// Check server version/addons
///////////////
if (isMultiplayer) then {
- // don't check optional addons
- _addons = _addons select {getNumber (configFile >> "CfgPatches" >> _x >> "ACE_isOptional") != 1};
+ // Don't check optional addons
+ _addons = _addons select {getNumber (_cfgPatches >> _x >> "ACE_isOptional") != 1};
if (isServer) then {
- // send servers version of ACE to all clients
- GVAR(ServerVersion) = _mainVersion;
- GVAR(ServerAddons) = _addons;
- publicVariable QGVAR(ServerVersion);
- publicVariable QGVAR(ServerAddons);
+ // Send server's version of ACE to all clients
+ GVAR(serverVersion) = _mainVersion;
+ GVAR(serverAddons) = _addons;
+ GVAR(serverSource) = _mainSource;
+
+ publicVariable QGVAR(serverVersion);
+ publicVariable QGVAR(serverAddons);
+ publicVariable QGVAR(serverSource);
} else {
- // clients have to wait for the variables
- [{
- if (isNil QGVAR(ServerVersion) || isNil QGVAR(ServerAddons)) exitWith {};
-
- (_this select 0) params ["_mainVersion", "_addons"];
+ GVAR(clientVersion) = _version;
+ GVAR(clientAddons) = _addons;
- if (_mainVersion != GVAR(ServerVersion)) then {
- private _errorMsg = format ["Client/Server Version Mismatch. Server: %1, Client: %2.", GVAR(ServerVersion), _mainVersion];
+ private _fnc_check = {
+ if (GVAR(clientVersion) != GVAR(serverVersion)) then {
+ private _errorMsg = format ["Client/Server Version Mismatch. Server: %1, Client: %2. Server modDir: %3", GVAR(serverVersion), GVAR(clientVersion), GVAR(serverSource)];
+ // Check ACE install
call FUNC(checkFiles_diagnoseACE);
+
ERROR(_errorMsg);
if (hasInterface) then {
- ["[ACE] ERROR", _errorMsg, {findDisplay 46 closeDisplay 0}] call FUNC(errorMessage);
+ ["[ACE] ERROR", _errorMsg] call FUNC(errorMessage);
};
};
- _addons = _addons - GVAR(ServerAddons);
+ private _addons = GVAR(clientAddons) - GVAR(serverAddons);
+
if (_addons isNotEqualTo []) then {
- private _errorMsg = format ["Client/Server Addon Mismatch. Client has extra addons: %1.",_addons];
+ private _errorMsg = format ["Client/Server Addon Mismatch. Client has additional addons: %1. Server modDir: %2", _addons, GVAR(serverSource)];
+ // Check ACE install
call FUNC(checkFiles_diagnoseACE);
+
ERROR(_errorMsg);
if (hasInterface) then {
- ["[ACE] ERROR", _errorMsg, {findDisplay 46 closeDisplay 0}] call FUNC(errorMessage);
+ ["[ACE] ERROR", _errorMsg] call FUNC(errorMessage);
};
};
+ };
- [_this select 1] call CBA_fnc_removePerFrameHandler;
- }, 1, [_mainVersion,_addons]] call CBA_fnc_addPerFrameHandler;
+ // Clients have to wait for the variables
+ if (isNil QGVAR(serverVersion) || isNil QGVAR(serverAddons)) then {
+ GVAR(serverVersion) addPublicVariableEventHandler _fnc_check;
+ } else {
+ call _fnc_check;
+ };
};
};
diff --git a/addons/common/functions/fnc_checkFiles_diagnoseACE.sqf b/addons/common/functions/fnc_checkFiles_diagnoseACE.sqf
index 5b7f80198b5..f9271ca2137 100644
--- a/addons/common/functions/fnc_checkFiles_diagnoseACE.sqf
+++ b/addons/common/functions/fnc_checkFiles_diagnoseACE.sqf
@@ -1,13 +1,13 @@
#include "..\script_component.hpp"
/*
* Author: PabstMirror
- * Diagnose ACE install problems, this will only be called if there is a known problem
+ * Diagnoses ACE install problems, this will only be called if there is a known problem.
*
* Arguments:
* None
*
* Return Value:
- * None
+ * ACE addons' WS IDs
*
* Example:
* [] call ace_common_fnc_checkFiles_diagnoseACE
@@ -16,43 +16,59 @@
*/
// Only run once
-if (missionNameSpace getVariable [QGVAR(checkFiles_diagnoseACE), false]) exitWith {};
+if (missionNameSpace getVariable [QGVAR(checkFiles_diagnoseACE), false]) exitWith {
+ createHashMap // return
+};
+
GVAR(checkFiles_diagnoseACE) = true;
-private _addons = cba_common_addons select {(_x select [0,4]) == "ace_"};
+private _addons = CBA_common_addons select {(_x select [0, 4]) == "ace_"};
private _cfgPatches = configFile >> "CfgPatches";
private _allMods = createHashMap;
+private _getLoadedModsInfo = getLoadedModsInfo;
-// Check ACE_ADDONs are in expected mod DIR
+// Check if ACE_ADDONs are in expected mod DIR
{
- private _cfg = (_cfgPatches >> _x);
+ private _cfg = _cfgPatches >> _x;
private _actualModDir = configSourceMod _cfg;
private _expectedModDir = getText (_cfg >> "ACE_expectedModDir");
- if (_expectedModDir == "") then { _expectedModDir = "@ace" };
+
+ if (_expectedModDir == "") then {
+ _expectedModDir = "@ace";
+ };
+
private _expectedSteamID = getText (_cfg >> "ACE_expectedSteamID");
- if (_expectedSteamID == "") then { _expectedSteamID = "463939057" };
+
+ if (_expectedSteamID == "") then {
+ _expectedSteamID = "463939057"
+ };
(_allMods getOrDefault [_actualModDir, [], true]) pushBackUnique _expectedSteamID;
+
if (_actualModDir != _expectedModDir) then {
- private _errorMsg = format ["%1 loading from unexpected modDir [%2]",_x,_actualModDir];
+ private _errorMsg = format ["%1 loading from unexpected modDir [%2]", _x, _actualModDir];
systemChat _errorMsg;
WARNING_1("%1",_errorMsg);
};
} forEach _addons;
-// Check all ACE ModDirs have expected steam WS ID
+// Check if all ACE ModDirs have expected steam WS ID
{
private _modDir = _x;
- if ((count _y) != 1) then { ERROR_2("Unexpected multiple steamIDs %1 - %2",_modDir,_y) };
- private _expectedSteamID = _y # 0;
- private _index = getLoadedModsInfo findIf {_x#1 == _modDir};
- (getLoadedModsInfo param [_index, []]) params [["_modName", "$Error$"], "", "", "", "", "", "", ["_actualID", ""]];
+
+ if (count _y != 1) then {
+ ERROR_2("Unexpected multiple steamIDs %1 - %2",_modDir,_y);
+ };
+
+ private _expectedSteamID = _y select 0;
+ private _index = _getLoadedModsInfo findIf {_x select 1 == _modDir};
+ (_getLoadedModsInfo param [_index, []]) params [["_modName", "$Error$"], "", "", "", "", "", "", ["_actualID", ""]];
if (_actualID != _expectedSteamID) then {
- private _errorMsg = format ["%1 [%2] unexpected workshopID [%3]",_modDir,_modName,_actualID];
+ private _errorMsg = format ["%1 [%2] unexpected workshopID [%3]", _modDir, _modName, _actualID];
systemChat _errorMsg;
WARNING_1("%1",_errorMsg);
};
} forEach _allMods;
-_allMods
+_allMods // return
diff --git a/addons/common/functions/fnc_checkPBOs.sqf b/addons/common/functions/fnc_checkPBOs.sqf
index cb192c66675..4f2e3f4fa6b 100644
--- a/addons/common/functions/fnc_checkPBOs.sqf
+++ b/addons/common/functions/fnc_checkPBOs.sqf
@@ -1,6 +1,6 @@
#include "..\script_component.hpp"
/*
- * Author: commy2
+ * Author: commy2, johnb43
* Used to execute the checkPBOs module without placing the module. Don't use this together with the module.
* Checks PBO versions and compares to the one running on server.
*
@@ -9,8 +9,8 @@
* 0 = Warn once
* 1 = Warn permanently
* 2 = Kick
- * 1: Check all PBOs? (default: false)
- * 2: Whitelist (default: "")
+ * 1: Check all PBOs? (default: false)
+ * 2: Whitelist (default: "")
*
* Return Value:
* None
@@ -24,7 +24,7 @@
params ["_mode", ["_checkAll", false], ["_whitelist", "", [""]]];
TRACE_3("params",_mode,_checkAll,_whitelist);
-//lowercase and convert whiteList String into array of strings:
+// Lowercase and convert whiteList string into array of strings
_whitelist = toLowerANSI _whitelist;
_whitelist = _whitelist splitString "[,""']";
TRACE_1("Array",_whitelist);
@@ -32,75 +32,67 @@ TRACE_1("Array",_whitelist);
ACE_Version_CheckAll = _checkAll;
ACE_Version_Whitelist = _whitelist;
-if (!_checkAll) exitWith {}; //ACE is checked by FUNC(checkFiles)
+// ACE is checked by FUNC(checkFiles)
+if (!_checkAll) exitWith {};
if (!isServer) then {
- [{
- if (isNil "ACE_Version_ClientErrors") exitWith {};
-
- ACE_Version_ClientErrors params ["_missingAddon", "_missingAddonServer", "_oldVersionClient", "_oldVersionServer"];
-
- (_this select 0) params ["_mode", "_checkAll", "_whitelist"];
-
- // Display error message.
- if (_missingAddon || {_missingAddonServer} || {_oldVersionClient} || {_oldVersionServer}) then {
- private _text = "[ACE] Version mismatch:
";
- private _error = format ["ACE version mismatch: %1: ", profileName];
-
- if (_missingAddon) then {
- _text = _text + "Detected missing addon on client
";
- _error = _error + "Missing file(s); ";
+ ["ace_versioning_clientCheckDone", {
+ // Don't let this event get triggered again
+ [_thisType, _thisId] call CBA_fnc_removeEventHandler;
+
+ params ["_clientErrors"];
+ _clientErrors params ["_missingAddonClient", "_additionalAddonClient", "_olderVersionClient", "_newerVersionClient"];
+ _thisArgs params ["_mode"];
+
+ // Display error message(s)
+ if (_missingAddonClient || {_additionalAddonClient} || {_olderVersionClient} || {_newerVersionClient}) then {
+ private _errorMsg = "[ACE] Version mismatch:
";
+ private _error = [];
+
+ if (_missingAddonClient) then {
+ _errorMsg = _errorMsg + "Detected missing addon on client
";
+ _error pushBack "Missing file(s)";
};
- if (_missingAddonServer) then {
- _text = _text + "Detected missing addon on server
";
- _error = _error + "Additional file(s); ";
+
+ if (_additionalAddonClient) then {
+ _errorMsg = _errorMsg + "Detected additional addon on client
";
+ _error pushBack "Additional file(s)";
};
- if (_oldVersionClient) then {
- _text = _text + "Detected old client version
";
- _error = _error + "Older version; ";
+
+ if (_olderVersionClient) then {
+ _errorMsg = _errorMsg + "Detected older client version
";
+ _error pushBack "Older version";
};
- if (_oldVersionServer) then {
- _text = _text + "Detected old server version
";
- _error = _error + "Newer version; ";
+
+ if (_newerVersionClient) then {
+ _errorMsg = _errorMsg + "Detected newer client version
";
+ _error pushBack "Newer version";
};
- //[QGVAR(systemChatGlobal), _error] call CBA_fnc_globalEvent;
+ ERROR_2("[ACE] Version mismatch: %1: %2",profileName,_error joinString ", ");
- ERROR(_error);
+ _errorMsg = parseText format ["%1", _errorMsg];
+ // Warn
if (_mode < 2) then {
- _text = composeText [lineBreak, parseText format ["%1", _text]];
-
private _rscLayer = "ACE_RscErrorHint" call BIS_fnc_rscLayer;
_rscLayer cutRsc ["ACE_RscErrorHint", "PLAIN", 0, true];
- disableSerialization;
- private _ctrlHint = uiNamespace getVariable "ACE_ctrlErrorHint";
- _ctrlHint ctrlSetStructuredText _text;
+ (uiNamespace getVariable "ACE_ctrlErrorHint") ctrlSetStructuredText composeText [lineBreak, _errorMsg];
if (_mode == 0) then {
[{
- params ["_rscLayer"];
- TRACE_2("Hiding Error message after 10 seconds",time,_rscLayer);
- _rscLayer cutFadeOut 0.2;
- }, [_rscLayer], 10] call CBA_fnc_waitAndExecute;
+ TRACE_2("Hiding Error message after 10 seconds",time,_this);
+ _this cutFadeOut 0.2;
+ }, _rscLayer, 10] call CBA_fnc_waitAndExecute;
};
- };
-
- if (_mode == 2) then {
- [{alive player}, { // To be able to show list if using checkAll
- params ["_text"];
- TRACE_2("Player is alive, showing msg and exiting",time,_text);
- _text = composeText [parseText format ["%1", _text]];
- ["[ACE] ERROR", _text, {findDisplay 46 closeDisplay 0}] call FUNC(errorMessage);
- }, [_text]] call CBA_fnc_waitUntilAndExecute;
+ } else {
+ // Kick
+ ["[ACE] ERROR", composeText [_errorMsg]] call FUNC(errorMessage);
};
};
-
- [_this select 1] call CBA_fnc_removePerFrameHandler;
- }, 1, [_mode, _checkAll, _whitelist]] call CBA_fnc_addPerFrameHandler;
+ }, [_mode]] call CBA_fnc_addEventHandlerArgs;
};
-if (_checkAll) then {
- 0 spawn COMPILE_FILE(scripts\checkVersionNumber); // @todo
-};
+// Check file version numbers
+[_whitelist] call FUNC(checkVersionNumber);
diff --git a/addons/common/functions/fnc_checkVersionNumber.sqf b/addons/common/functions/fnc_checkVersionNumber.sqf
new file mode 100644
index 00000000000..a2861299174
--- /dev/null
+++ b/addons/common/functions/fnc_checkVersionNumber.sqf
@@ -0,0 +1,161 @@
+#include "..\script_component.hpp"
+/*
+ * Author: commy2, johnb43
+ * Compares version numbers from loaded addons.
+ *
+ * Arguments:
+ * 0: Lowercase addon whitelist (default: missionNamespace getVariable ["ACE_Version_Whitelist", []])
+ *
+ * Return Value:
+ * None
+ *
+ * Example:
+ * call ace_common_fnc_checkVersionNumber
+ *
+ * Public: No
+ */
+
+// Don't execute in scheduled environment
+if (canSuspend) exitWith {
+ [FUNC(checkVersionNumber), _this] call CBA_fnc_directCall;
+};
+
+params [["_whitelist", missionNamespace getVariable ["ACE_Version_Whitelist", []]]];
+
+private _files = CBA_common_addons select {
+ (_x select [0, 3] != "a3_") &&
+ {_x select [0, 4] != "ace_"} &&
+ {!((toLowerANSI _x) in _whitelist)}
+};
+
+private _cfgPatches = configFile >> "CfgPatches";
+private _versions = [];
+
+{
+ (getText (_cfgPatches >> _x >> "version") splitString ".") params [["_major", "0"], ["_minor", "0"]];
+ private _version = parseNumber _major + parseNumber _minor / 100;
+ _versions pushBack _version;
+} forEach _files;
+
+if (isServer) exitWith {
+ ACE_Version_ServerVersions = [_files, _versions];
+ publicVariable "ACE_Version_ServerVersions";
+
+ // Raise event when done
+ ["ace_versioning_serverCheckDone", [+ACE_Version_ServerVersions]] call CBA_fnc_localEvent;
+};
+
+// Begin client version check
+ACE_Version_ClientVersions = [_files, _versions];
+
+private _fnc_check = {
+ ACE_Version_ClientVersions params [["_files", []], ["_versions", []]];
+ ACE_Version_ServerVersions params [["_serverFiles", []], ["_serverVersions", []]];
+
+ // Compare client and server files and versions
+ private _client = profileName;
+ private _missingAddonsClient = [];
+ private _olderVersionsClient = [];
+ private _newerVersionsClient = [];
+
+ {
+ private _serverVersion = _serverVersions select _forEachIndex;
+
+ private _index = _files find _x;
+
+ if (_index == -1) then {
+ if (_x != "ace_server") then {
+ _missingAddonsClient pushBack _x;
+ };
+ } else {
+ private _clientVersion = _versions select _index;
+
+ if (_clientVersion < _serverVersion) then {
+ _olderVersionsClient pushBack [_x, _clientVersion, _serverVersion];
+ };
+
+ if (_clientVersion > _serverVersion) then {
+ _newerVersionsClient pushBack [_x, _clientVersion, _serverVersion];
+ };
+ };
+ } forEach _serverFiles;
+
+ // Find client files which the server doesn't have
+ private _additionalAddonsClient = _files select {!(_x in _serverFiles)};
+
+ // Check for client missing addons, server missing addons, client outdated addons and server outdated addons
+ private _clientErrors = [];
+
+ #define DISPLAY_NUMBER_ADDONS (10 + 1) // +1 to account for header
+
+ {
+ _x params ["_items", "_string"];
+
+ // Check if something is either missing or outdated
+ private _isMissingItems = _items isNotEqualTo [];
+
+ if (_isMissingItems) then {
+ // Generate error message
+ private _errorLog = +_items;
+ private _header = format ["[ACE] %1: ERROR %2 addon(s): ", _client, _string];
+
+ // Don't display all missing items, as they are logged
+ private _errorMsg = _header + ((_errorLog select [0, DISPLAY_NUMBER_ADDONS]) joinString ", ");
+ _errorLog = _header + (_errorLog joinString ", ");
+
+ private _count = count _items;
+
+ if (_count > DISPLAY_NUMBER_ADDONS) then {
+ _errorMsg = _errorMsg + format [", and %1 more.", _count - DISPLAY_NUMBER_ADDONS];
+ };
+
+ // Wait until in briefing screen
+ [{
+ getClientStateNumber >= 9 // "BRIEFING SHOWN"
+ }, {
+ params ["_errorLog", "_errorMsg"];
+
+ // Log and display error messages
+ diag_log text _errorLog;
+ [QGVAR(serverLog), _errorLog] call CBA_fnc_serverEvent;
+ [QGVAR(systemChatGlobal), _errorMsg] call CBA_fnc_globalEvent;
+
+ // Wait until after map screen
+ [{
+ !isNull (call BIS_fnc_displayMission)
+ }, {
+ params ["_errorMsg", "_timeOut"];
+
+ // If the briefing screen was shown for less than 5 seconds, display the error message again, but locally
+ if (_timeOut < CBA_missionTime) exitWith {};
+
+ // Make sure systemChat is ready by waiting a bit
+ [{
+ systemChat _this;
+ }, _errorMsg, 1] call CBA_fnc_waitAndExecute;
+ }, [_errorMsg, CBA_missionTime + 5]] call CBA_fnc_waitUntilAndExecute;
+ }, [_errorLog, _errorMsg]] call CBA_fnc_waitUntilAndExecute;
+ };
+
+ _clientErrors pushBack _isMissingItems;
+ } forEach [
+ [_missingAddonsClient, "client missing"],
+ [_additionalAddonsClient, "client additional"],
+ [_olderVersionsClient, "older client"],
+ [_newerVersionsClient, "newer client"]
+ ];
+
+ TRACE_4("",_missingAddonsClient,_additionalAddonsClient,_olderVersionsClient,_newerVersionsClient);
+
+ ACE_Version_ClientErrors = _clientErrors;
+
+ // Raise event when done
+ ["ace_versioning_clientCheckDone", [+ACE_Version_ClientErrors]] call CBA_fnc_localEvent;
+};
+
+// Wait for server to send the servers files and version numbers
+if (isNil "ACE_Version_ServerVersions") then {
+ ACE_Version_ServerVersions addPublicVariableEventHandler _fnc_check;
+} else {
+ call _fnc_check;
+};
diff --git a/addons/common/functions/fnc_errorMessage.sqf b/addons/common/functions/fnc_errorMessage.sqf
index 72344299f39..e98a5baf8f9 100644
--- a/addons/common/functions/fnc_errorMessage.sqf
+++ b/addons/common/functions/fnc_errorMessage.sqf
@@ -1,147 +1,141 @@
#include "..\script_component.hpp"
+#include "\a3\ui_f\hpp\defineResincl.inc"
+#include "\a3\ui_f\hpp\defineDIKCodes.inc"
/*
- * Author: commy2, based on BIS_fnc_errorMsg and BIS_fnc_guiMessage by Karel Moricky (BI)
- * Stops simulation and opens a textbox with error message.
+ * Author: commy2, johnb43, based on BIS_fnc_errorMsg and BIS_fnc_guiMessage by Karel Moricky (BI)
+ * Opens a textbox with an error message, used for PBO checking.
*
* Arguments:
- * ?
+ * 0: Header
+ * 1: Text
*
* Return Value:
* None
*
* Example:
- * call ace_common_fnc_errorMessage
+ * ["[ACE] ERROR", "Test"] call ace_common_fnc_errorMessage
*
* Public: No
*/
-disableSerialization;
+// Force stop any loading screens
endLoadingScreen;
-// no message without player possible
+// No message without interface possible
if (!hasInterface) exitWith {};
-// wait for display
-if (isNull (call BIS_fnc_displayMission)) exitWith {
- [{
- if (isNull (call BIS_fnc_displayMission)) exitWith {};
-
- (_this select 0) call FUNC(errorMessage);
- [_this select 1] call CBA_fnc_removePerFrameHandler;
-
- }, 1, _this] call CBA_fnc_addPerFrameHandler;
-};
-
-params ["_textHeader", "_textMessage", ["_onOK", {}], ["_onCancel", {}]];
-
-if (_textMessage isEqualType "") then {
- _textMessage = parseText _textMessage;
-};
-
-ARR_SELECT(_this,4,call BIS_fnc_displayMission) createDisplay "RscDisplayCommonMessagePause";
-
-private _display = uiNamespace getVariable "RscDisplayCommonMessage_display";
-private _ctrlRscMessageBox = _display displayCtrl 2351;
-private _ctrlBcgCommonTop = _display displayCtrl 235100;
-private _ctrlBcgCommon = _display displayCtrl 235101;
-private _ctrlText = _display displayCtrl 235102;
-private _ctrlBackgroundButtonOK = _display displayCtrl 235103;
-private _ctrlBackgroundButtonMiddle = _display displayCtrl 235104;
-private _ctrlBackgroundButtonCancel = _display displayCtrl 235105;
-private _ctrlButtonOK = _display displayCtrl 235106;
-private _ctrlButtonCancel = _display displayCtrl 235107;
-
-_ctrlBcgCommonTop ctrlSetText _textHeader;
-
-private _ctrlButtonOKPos = ctrlPosition _ctrlButtonOK;
-private _ctrlBcgCommonPos = ctrlPosition _ctrlBcgCommon;
-private _bottomSpaceY = (_ctrlButtonOKPos select 1) - ((_ctrlBcgCommonPos select 1) + (_ctrlBcgCommonPos select 3));
-
-private _ctrlTextPos = ctrlPosition _ctrlText;
-private _marginX = (_ctrlTextPos select 0) - (_ctrlBcgCommonPos select 0);
-private _marginY = (_ctrlTextPos select 1) - (_ctrlBcgCommonPos select 1);
-
-_ctrlText ctrlSetStructuredText _textMessage;
-private _ctrlTextPosH = ctrlTextHeight _ctrlText;
-
-_ctrlBcgCommon ctrlSetPosition [
- _ctrlBcgCommonPos select 0,
- _ctrlBcgCommonPos select 1,
- _ctrlBcgCommonPos select 2,
- _ctrlTextPosH + _marginY * 2
-];
-_ctrlBcgCommon ctrlCommit 0;
-
-_ctrlText ctrlSetPosition [
- (_ctrlBcgCommonPos select 0) + _marginX,
- (_ctrlBcgCommonPos select 1) + _marginY,
- (_ctrlBcgCommonPos select 2) - _marginX * 2,
- _ctrlTextPosH
-];
-_ctrlText ctrlCommit 0;
-
-private _bottomPosY = (_ctrlBcgCommonPos select 1) + _ctrlTextPosH + (_marginY * 2) + _bottomSpaceY;
-
-{
- private _xPos = ctrlPosition _x;
-
- _xPos set [1, _bottomPosY];
- _x ctrlSetPosition _xPos;
- _x ctrlCommit 0;
-} forEach [
- _ctrlBackgroundButtonOK,
- _ctrlBackgroundButtonMiddle,
- _ctrlBackgroundButtonCancel,
- _ctrlButtonOK,
- _ctrlButtonCancel
-];
-
-private _ctrlRscMessageBoxPos = ctrlPosition _ctrlRscMessageBox;
-private _ctrlRscMessageBoxPosH = _bottomPosY + (_ctrlButtonOKPos select 3);
-
-_ctrlRscMessageBox ctrlSetPosition [
- 0.5 - (_ctrlBcgCommonPos select 2) / 2,
- 0.5 - _ctrlRscMessageBoxPosH / 2,
- (_ctrlBcgCommonPos select 2) + 0.5,
- _ctrlRscMessageBoxPosH
-];
-
-_ctrlRscMessageBox ctrlEnable true;
-_ctrlRscMessageBox ctrlCommit 0;
-
-if (_onOK isEqualTo {}) then {
- _ctrlButtonOK ctrlEnable false;
- _ctrlButtonOK ctrlSetFade 0;
- _ctrlButtonOK ctrlSetText "";
- _ctrlButtonOK ctrlCommit 0;
-} else {
+[{
+ !isNull (call BIS_fnc_displayMission)
+}, {
+ params ["_textHeader", "_textMessage"];
+
+ disableSerialization;
+
+ // Use curator display if present
+ private _curatorDisplay = findDisplay 312;
+
+ private _mainDisplay = if (!isNull _curatorDisplay) then {
+ _curatorDisplay
+ } else {
+ call BIS_fnc_displayMission
+ };
+
+ if (_textMessage isEqualType "") then {
+ _textMessage = parseText _textMessage;
+ };
+
+ private _display = _mainDisplay createDisplay "RscDisplayCommonMessagePause";
+
+ if (isNull _display) exitWith {};
+
+ private _ctrlRscMessageBox = _display displayCtrl 2351;
+ private _ctrlBcgCommonTop = _display displayCtrl 235100;
+ private _ctrlBcgCommon = _display displayCtrl 235101;
+ private _ctrlText = _display displayCtrl 235102;
+ private _ctrlBackgroundButtonOK = _display displayCtrl 235103;
+ private _ctrlBackgroundButtonMiddle = _display displayCtrl 235104;
+ private _ctrlBackgroundButtonCancel = _display displayCtrl 235105;
+ private _ctrlButtonOK = _display displayCtrl 235106;
+ private _ctrlButtonCancel = _display displayCtrl 235107;
+
+ _ctrlBcgCommonTop ctrlSetText _textHeader;
+
+ private _ctrlButtonOKPos = ctrlPosition _ctrlButtonOK;
+ private _ctrlBcgCommonPos = ctrlPosition _ctrlBcgCommon;
+ private _bottomSpaceY = (_ctrlButtonOKPos select 1) - ((_ctrlBcgCommonPos select 1) + (_ctrlBcgCommonPos select 3));
+
+ private _ctrlTextPos = ctrlPosition _ctrlText;
+ private _marginX = (_ctrlTextPos select 0) - (_ctrlBcgCommonPos select 0);
+ private _marginY = (_ctrlTextPos select 1) - (_ctrlBcgCommonPos select 1);
+
+ _ctrlText ctrlSetStructuredText _textMessage;
+ private _ctrlTextPosH = ctrlTextHeight _ctrlText;
+
+ _ctrlBcgCommon ctrlSetPosition [
+ _ctrlBcgCommonPos select 0,
+ _ctrlBcgCommonPos select 1,
+ _ctrlBcgCommonPos select 2,
+ _ctrlTextPosH + _marginY * 2
+ ];
+ _ctrlBcgCommon ctrlCommit 0;
+
+ _ctrlText ctrlSetPosition [
+ (_ctrlBcgCommonPos select 0) + _marginX,
+ (_ctrlBcgCommonPos select 1) + _marginY,
+ (_ctrlBcgCommonPos select 2) - _marginX * 2,
+ _ctrlTextPosH
+ ];
+ _ctrlText ctrlCommit 0;
+
+ private _bottomPosY = (_ctrlBcgCommonPos select 1) + _ctrlTextPosH + (_marginY * 2) + _bottomSpaceY;
+
+ {
+ private _xPos = ctrlPosition _x;
+
+ _xPos set [1, _bottomPosY];
+ _x ctrlSetPosition _xPos;
+ _x ctrlCommit 0;
+ } forEach [
+ _ctrlBackgroundButtonOK,
+ _ctrlBackgroundButtonMiddle,
+ _ctrlBackgroundButtonCancel,
+ _ctrlButtonOK,
+ _ctrlButtonCancel
+ ];
+
+ private _ctrlRscMessageBoxPos = ctrlPosition _ctrlRscMessageBox;
+ private _ctrlRscMessageBoxPosH = _bottomPosY + (_ctrlButtonOKPos select 3);
+
+ _ctrlRscMessageBox ctrlSetPosition [
+ 0.5 - (_ctrlBcgCommonPos select 2) / 2,
+ 0.5 - _ctrlRscMessageBoxPosH / 2,
+ (_ctrlBcgCommonPos select 2) + 0.5,
+ _ctrlRscMessageBoxPosH
+ ];
+
+ _ctrlRscMessageBox ctrlEnable true;
+ _ctrlRscMessageBox ctrlCommit 0;
+
+ // Enable ok button
_ctrlButtonOK ctrlEnable true;
_ctrlButtonOK ctrlSetFade 0;
_ctrlButtonOK ctrlSetText localize "STR_DISP_OK";
_ctrlButtonOK ctrlCommit 0;
ctrlSetFocus _ctrlButtonOK;
-};
-if (_onCancel isEqualTo {}) then {
+ // Disable cancel button
_ctrlButtonCancel ctrlEnable false;
_ctrlButtonCancel ctrlSetFade 0;
_ctrlButtonCancel ctrlSetText "";
_ctrlButtonCancel ctrlCommit 0;
-} else {
- _ctrlButtonCancel ctrlEnable true;
- _ctrlButtonCancel ctrlSetFade 0;
- _ctrlButtonCancel ctrlSetText localize "STR_DISP_CANCEL";
- _ctrlButtonCancel ctrlCommit 0;
-
- ctrlSetFocus _ctrlButtonCancel;
-};
-_ctrlButtonOK ctrlAddEventHandler ["ButtonClick", {(ctrlParent (_this select 0)) closeDisplay 1; true}];
-_ctrlButtonCancel ctrlAddEventHandler ["ButtonClick", {(ctrlParent (_this select 0)) closeDisplay 2; true}];
+ _ctrlButtonOK ctrlAddEventHandler ["ButtonClick", {(ctrlParent (_this select 0)) closeDisplay IDC_OK; true}];
-GVAR(errorOnOK) = _onOK;
-GVAR(errorOnCancel) = _onCancel;
+ // Intercept all keystrokes except the enter keys
+ _display displayAddEventHandler ["KeyDown", {!((_this select 1) in [DIK_RETURN, DIK_NUMPADENTER])}];
-_display displayAddEventHandler ["Unload", {call ([{}, GVAR(errorOnOK), GVAR(errorOnCancel)] select (_this select 1))}];
-_display displayAddEventHandler ["KeyDown", {_this select 1 == 1}];
+ // Close curator and mission displays (because of the message display, it doesn't quit the mission yet)
+ findDisplay 312 closeDisplay 0;
+ findDisplay 46 closeDisplay 0;
+}, _this] call CBA_fnc_waitUntilAndExecute;
diff --git a/addons/common/scripts/checkVersionNumber.sqf b/addons/common/scripts/checkVersionNumber.sqf
deleted file mode 100644
index 3ed51120b65..00000000000
--- a/addons/common/scripts/checkVersionNumber.sqf
+++ /dev/null
@@ -1,160 +0,0 @@
-// by commy2
-#include "..\script_component.hpp"
-
-private _aceWhitelist = missionNamespace getVariable ["ACE_Version_Whitelist", []];
-private _files = CBA_common_addons select {
- (_x select [0,3] != "a3_") &&
- {_x select [0,4] != "ace_"} &&
- {!((toLowerANSI _x) in _aceWhitelist)}
-};
-
-private _versions = [];
-{
- getText (configFile >> "CfgPatches" >> _x >> "version") splitString "." params [["_major", "0"], ["_minor", "0"]];
- private _version = parseNumber _major + parseNumber _minor/100;
- _versions set [_forEachIndex, _version];
-} forEach _files;
-
-if (isServer) then {
- ACE_Version_ServerVersions = [_files, _versions];
- publicVariable "ACE_Version_ServerVersions";
-} else {
- ACE_Version_ClientVersions = [_files, _versions];
-};
-
-// Begin client version check
-if (!isServer) then {
- // Wait for server to send the servers files and version numbers
- waitUntil {
- sleep 1;
- !isNil "ACE_Version_ClientVersions" && {!isNil "ACE_Version_ServerVersions"}
- };
-
- private _client = profileName;
-
- _files = ACE_Version_ClientVersions select 0;
- _versions = ACE_Version_ClientVersions select 1;
-
- private _serverFiles = ACE_Version_ServerVersions select 0;
- private _serverVersions = ACE_Version_ServerVersions select 1;
-
- // Compare client and server files and versions
- private _missingAddons = [];
- private _oldVersionsClient = [];
- private _oldVersionsServer = [];
- {
- private _serverVersion = _serverVersions select _forEachIndex;
-
- private _index = _files find _x;
- if (_index == -1) then {
- if (_x != "ace_server") then {_missingAddons pushBack _x;};
- } else {
-
- private _clientVersion = _versions select _index;
-
- if (_clientVersion < _serverVersion) then {
- _oldVersionsClient pushBack [_x, _clientVersion, _serverVersion];
- };
-
- if (_clientVersion > _serverVersion) then {
- _oldVersionsServer pushBack [_x, _clientVersion, _serverVersion];
- };
- };
- } forEach _serverFiles;
-
- // find client files which the server doesn't have
- private _missingAddonsServer = [];
- {
- private _index = _serverFiles find _x;
- if (_index == -1) then {
- _missingAddonsServer pushBack _x;
- }
- } forEach _files;
-
- // display and log error messages
- private _fnc_cutComma = {
- private _string = _this;
- _string = toArray _string;
-
- private _count = count _string;
- _string set [_count - 2, toArray "." select 0];
- _string set [_count - 1, -1];
- _string = _string - [-1];
-
- toString _string;
- };
-
- private _missingAddon = false;
- if (count _missingAddons > 0) then {
- _missingAddon = true;
-
- private _error = format ["[ACE] %1: ERROR client missing addon(s): ", _client];
- {
- _error = _error + format ["%1, ", _x];
-
- if (_forEachIndex > 9) exitWith {};
- } forEach _missingAddons;
-
- _error = _error call _fnc_cutComma;
-
- diag_log text _error;
- [QGVAR(systemChatGlobal), _error] call CBA_fnc_globalEvent;
- [QGVAR(serverLog), _error] call CBA_fnc_serverEvent;
- };
-
- private _missingAddonServer = false;
- if (count _missingAddonsServer > 0) then {
- _missingAddonServer = true;
-
- private _error = format ["[ACE] %1: ERROR server missing addon(s): ", _client];
- {
- _error = _error + format ["%1, ", _x];
-
- if (_forEachIndex > 9) exitWith {};
- } forEach _missingAddonsServer;
-
- _error = _error call _fnc_cutComma;
-
- diag_log text _error;
- [QGVAR(systemChatGlobal), _error] call CBA_fnc_globalEvent;
- [QGVAR(serverLog), _error] call CBA_fnc_serverEvent;
- };
-
- private _oldVersionClient = false;
- if (count _oldVersionsClient > 0) then {
- _oldVersionClient = true;
-
- private _error = format ["[ACE] %1: ERROR outdated client addon(s): ", _client];
- {
- _error = _error + format ["%1 (client: %2, server: %3), ", _x select 0, _x select 1, _x select 2];
-
- if (_forEachIndex > 9) exitWith {};
- } forEach _oldVersionsClient;
-
- _error = _error call _fnc_cutComma;
-
- diag_log text _error;
- [QGVAR(systemChatGlobal), _error] call CBA_fnc_globalEvent;
- [QGVAR(serverLog), _error] call CBA_fnc_serverEvent;
- };
-
- private _oldVersionServer = false;
- if (count _oldVersionsServer > 0) then {
- _oldVersionServer = true;
-
- private _error = format ["[ACE] %1: ERROR outdated server addon(s): ", _client];
- {
- _error = _error + format ["%1 (client: %2, server: %3), ", _x select 0, _x select 1, _x select 2];
-
- if (_forEachIndex > 9) exitWith {};
- } forEach _oldVersionsServer;
-
- _error = _error call _fnc_cutComma;
-
- diag_log text _error;
- [QGVAR(systemChatGlobal), _error] call CBA_fnc_globalEvent;
- [QGVAR(serverLog), _error] call CBA_fnc_serverEvent;
- };
-
- ACE_Version_ClientErrors = [_missingAddon, _missingAddonServer, _oldVersionClient, _oldVersionServer];
-};
diff --git a/docs/wiki/framework/events-framework.md b/docs/wiki/framework/events-framework.md
index 860cd900684..5155cf75937 100644
--- a/docs/wiki/framework/events-framework.md
+++ b/docs/wiki/framework/events-framework.md
@@ -30,6 +30,8 @@ The vehicle events will also have the following local variables available `_gunn
|`ace_firedPlayerVehicle` | [_vehicle, _weapon, _muzzle, _mode, _ammo, _magazine, _projectile] | Local | Listen | ACE_player turret fires |
|`ace_firedPlayerVehicleNonLocal` | [_vehicle, _weapon, _muzzle, _mode, _ammo, _magazine, _projectile] | Local | Listen | Any other player turret fires |
|`ace_firedNonPlayerVehicle` | [_vehicle, _weapon, _muzzle, _mode, _ammo, _magazine, _projectile] | Local | Listen | AI turret fires |
+|`ace_versioning_clientCheckDone` | [[_missingAddonsClient, _additionalAddonsClient, _olderVersionsClient, _newerVersionsClient]] | Local | Listen | When PBO checking has finished on a client |
+|`ace_versioning_serverCheckDone` | [[_serverFiles, _serverVersions]] | Local | Listen | When PBO checking has finished on the server |
### 2.2 Medical (`ace_medical`)