Common - Improve PBO checking (#9266)

* Update PBO checking

* Added kicking of clients without ACE loaded

* Update fnc_errorMessage.sqf

* Update fnc_checkVersionNumber.sqf

* More compatibility for #9568

* Cleanup

* Minor cleanup + added server source

* update outdated/not present error message

* check version number fixes

* Update fnc_errorMessage.sqf

* Changed error names

Server is always right, client has either older or newer versions, or missing or additional addons

* Improved ACE detection method

* Tweaks and fixes

* Try another approach

* Update events-framework.md

* Update XEH_postInit.sqf

* Update fnc_checkVersionNumber.sqf

* Removed check for non-ACE clients

* Update XEH_postInit.sqf

* Cleanup

* Remove rogue change

* Improved message display in systemChat

* Update fnc_checkPBOs.sqf

* Removed loop variable initialisers

* Fixed header

* Updated headers

---------

Co-authored-by: Grim <69561145+LinkIsGrim@users.noreply.github.com>
Co-authored-by: LinkIsGrim <salluci.lovi@gmail.com>
This commit is contained in:
johnb432 2024-06-22 15:52:59 +02:00 committed by GitHub
parent db6c4a72a6
commit 7ea2aab2c9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 408 additions and 373 deletions

View File

@ -30,6 +30,7 @@ PREP(changeProjectileDirection);
PREP(checkFiles);
PREP(checkFiles_diagnoseACE);
PREP(checkPBOs);
PREP(checkVersionNumber);
PREP(claim);
PREP(claimSafeServer);
PREP(codeToString);

View File

@ -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");
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;
};
} 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.<br/>ACE Main version is %3 from %4.<br/>Loaded mods with outdated ACE files: %5", (_oldAddons select [0, 3]) joinString ", ", (count _oldAddons) -3, _mainVersion, _mainSource, (_oldSources joinString ", ")];
private _errorMsg = if (count _oldAddons > 3) then {
format ["The following files are outdated: %1, and %2 more.<br/>ACE Main version is %3 from %4.<br/>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.<br/>ACE Main version is %2 from %3.<br/>Loaded mods with outdated ACE files: %4", (_oldAddons) joinString ", ", _mainVersion, _mainSource, (_oldSources) joinString ", "];
format ["The following files are outdated: %1.<br/>ACE Main version is %2 from %3.<br/>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 {};
GVAR(clientVersion) = _version;
GVAR(clientAddons) = _addons;
(_this select 0) params ["_mainVersion", "_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;
};
};
};

View File

@ -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 <HASHMAP>
*
* Example:
* [] call ace_common_fnc_checkFiles_diagnoseACE
@ -16,23 +16,35 @@
*/
// 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];
systemChat _errorMsg;
@ -40,13 +52,17 @@ private _allMods = createHashMap;
};
} 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];
@ -55,4 +71,4 @@ private _allMods = createHashMap;
};
} forEach _allMods;
_allMods
_allMods // return

View File

@ -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) <BOOL>
* 2: Whitelist (default: "") <STRING>
* 1: Check all PBOs? <BOOL> (default: false)
* 2: Whitelist <STRING> (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_versioning_clientCheckDone", {
// Don't let this event get triggered again
[_thisType, _thisId] call CBA_fnc_removeEventHandler;
ACE_Version_ClientErrors params ["_missingAddon", "_missingAddonServer", "_oldVersionClient", "_oldVersionServer"];
params ["_clientErrors"];
_clientErrors params ["_missingAddonClient", "_additionalAddonClient", "_olderVersionClient", "_newerVersionClient"];
_thisArgs params ["_mode"];
(_this select 0) params ["_mode", "_checkAll", "_whitelist"];
// Display error message(s)
if (_missingAddonClient || {_additionalAddonClient} || {_olderVersionClient} || {_newerVersionClient}) then {
private _errorMsg = "[ACE] Version mismatch:<br/><br/>";
private _error = [];
// Display error message.
if (_missingAddon || {_missingAddonServer} || {_oldVersionClient} || {_oldVersionServer}) then {
private _text = "[ACE] Version mismatch:<br/><br/>";
private _error = format ["ACE version mismatch: %1: ", profileName];
if (_missingAddon) then {
_text = _text + "Detected missing addon on client<br/>";
_error = _error + "Missing file(s); ";
};
if (_missingAddonServer) then {
_text = _text + "Detected missing addon on server<br/>";
_error = _error + "Additional file(s); ";
};
if (_oldVersionClient) then {
_text = _text + "Detected old client version<br/>";
_error = _error + "Older version; ";
};
if (_oldVersionServer) then {
_text = _text + "Detected old server version<br/>";
_error = _error + "Newer version; ";
if (_missingAddonClient) then {
_errorMsg = _errorMsg + "Detected missing addon on client<br/>";
_error pushBack "Missing file(s)";
};
//[QGVAR(systemChatGlobal), _error] call CBA_fnc_globalEvent;
if (_additionalAddonClient) then {
_errorMsg = _errorMsg + "Detected additional addon on client<br/>";
_error pushBack "Additional file(s)";
};
ERROR(_error);
if (_olderVersionClient) then {
_errorMsg = _errorMsg + "Detected older client version<br/>";
_error pushBack "Older version";
};
if (_newerVersionClient) then {
_errorMsg = _errorMsg + "Detected newer client version<br/>";
_error pushBack "Newer version";
};
ERROR_2("[ACE] Version mismatch: %1: %2",profileName,_error joinString ", ");
_errorMsg = parseText format ["<t align='center'>%1</t>", _errorMsg];
// Warn
if (_mode < 2) then {
_text = composeText [lineBreak, parseText format ["<t align='center'>%1</t>", _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;
};
} else {
// Kick
["[ACE] ERROR", composeText [_errorMsg]] call FUNC(errorMessage);
};
};
}, [_mode]] call CBA_fnc_addEventHandlerArgs;
};
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 ["<t align='center'>%1</t>", _text]];
["[ACE] ERROR", _text, {findDisplay 46 closeDisplay 0}] call FUNC(errorMessage);
}, [_text]] call CBA_fnc_waitUntilAndExecute;
};
};
[_this select 1] call CBA_fnc_removePerFrameHandler;
}, 1, [_mode, _checkAll, _whitelist]] call CBA_fnc_addPerFrameHandler;
};
if (_checkAll) then {
0 spawn COMPILE_FILE(scripts\checkVersionNumber); // @todo
};
// Check file version numbers
[_whitelist] call FUNC(checkVersionNumber);

View File

@ -0,0 +1,161 @@
#include "..\script_component.hpp"
/*
* Author: commy2, johnb43
* Compares version numbers from loaded addons.
*
* Arguments:
* 0: Lowercase addon whitelist <ARRAY> (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;
};

View File

@ -1,46 +1,53 @@
#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 <STRING>
* 1: Text <STRING|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 {};
!isNull (call BIS_fnc_displayMission)
}, {
params ["_textHeader", "_textMessage"];
(_this select 0) call FUNC(errorMessage);
[_this select 1] call CBA_fnc_removePerFrameHandler;
disableSerialization;
}, 1, _this] call CBA_fnc_addPerFrameHandler;
// Use curator display if present
private _curatorDisplay = findDisplay 312;
private _mainDisplay = if (!isNull _curatorDisplay) then {
_curatorDisplay
} else {
call BIS_fnc_displayMission
};
params ["_textHeader", "_textMessage", ["_onOK", {}], ["_onCancel", {}]];
if (_textMessage isEqualType "") then {
_textMessage = parseText _textMessage;
};
ARR_SELECT(_this,4,call BIS_fnc_displayMission) createDisplay "RscDisplayCommonMessagePause";
private _display = _mainDisplay createDisplay "RscDisplayCommonMessagePause";
if (isNull _display) exitWith {};
private _display = uiNamespace getVariable "RscDisplayCommonMessage_display";
private _ctrlRscMessageBox = _display displayCtrl 2351;
private _ctrlBcgCommonTop = _display displayCtrl 235100;
private _ctrlBcgCommon = _display displayCtrl 235101;
@ -109,39 +116,26 @@ _ctrlRscMessageBox ctrlSetPosition [
_ctrlRscMessageBox ctrlEnable true;
_ctrlRscMessageBox ctrlCommit 0;
if (_onOK isEqualTo {}) then {
_ctrlButtonOK ctrlEnable false;
_ctrlButtonOK ctrlSetFade 0;
_ctrlButtonOK ctrlSetText "";
_ctrlButtonOK ctrlCommit 0;
} else {
// 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 IDC_OK; true}];
_ctrlButtonOK ctrlAddEventHandler ["ButtonClick", {(ctrlParent (_this select 0)) closeDisplay 1; true}];
_ctrlButtonCancel ctrlAddEventHandler ["ButtonClick", {(ctrlParent (_this select 0)) closeDisplay 2; true}];
// Intercept all keystrokes except the enter keys
_display displayAddEventHandler ["KeyDown", {!((_this select 1) in [DIK_RETURN, DIK_NUMPADENTER])}];
GVAR(errorOnOK) = _onOK;
GVAR(errorOnCancel) = _onCancel;
_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;

View File

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

View File

@ -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`)