Skip to content

Commit

Permalink
Common - Improve PBO checking (acemod#9266)
Browse files Browse the repository at this point in the history
* Update PBO checking

* Added kicking of clients without ACE loaded

* Update fnc_errorMessage.sqf

* Update fnc_checkVersionNumber.sqf

* More compatibility for acemod#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>
  • Loading branch information
3 people authored and blake8090 committed Aug 18, 2024
1 parent 3d6c2c7 commit e49fe03
Show file tree
Hide file tree
Showing 8 changed files with 425 additions and 390 deletions.
1 change: 1 addition & 0 deletions addons/common/XEH_PREP.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ PREP(changeProjectileDirection);
PREP(checkFiles);
PREP(checkFiles_diagnoseACE);
PREP(checkPBOs);
PREP(checkVersionNumber);
PREP(claim);
PREP(claimSafeServer);
PREP(codeToString);
Expand Down
115 changes: 72 additions & 43 deletions addons/common/functions/fnc_checkFiles.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -15,79 +15,94 @@
* 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 ".";
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.<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 ", ")];
_oldAddons = _oldAddons apply {format ["%1.pbo", _x]};

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);
}, [_oldCompats, _mainVersion, _mainSource], 1] call CBA_fnc_waitAndExecute;
};

///////////////
// 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");
Expand All @@ -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];
};
Expand All @@ -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
Expand All @@ -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;
};
};
};
48 changes: 32 additions & 16 deletions addons/common/functions/fnc_checkFiles_diagnoseACE.sqf
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Loading

0 comments on commit e49fe03

Please sign in to comment.