mirror of
https://github.com/ModOrganizer2/modorganizer-basic_games.git
synced 2026-07-27 14:07:29 -07:00
Catch and show game list errors at startup (#177)
* Catch and log errors for failed EA games list parsing * Extend error logging for Epic Games * Add error message box for errors loading game list * Fix lint error in game_sims4.py
This commit is contained in:
+14
-2
@@ -8,6 +8,7 @@ from typing import Callable, Generic, TypeVar
|
||||
import mobase
|
||||
from PyQt6.QtCore import QDir, QFileInfo, QStandardPaths
|
||||
from PyQt6.QtGui import QIcon
|
||||
from PyQt6.QtWidgets import QMessageBox
|
||||
|
||||
from .basic_features.basic_save_game_info import (
|
||||
BasicGameSaveGame,
|
||||
@@ -380,11 +381,22 @@ class BasicGame(mobase.IPluginGame):
|
||||
from .origin_utils import find_games as find_origin_games
|
||||
from .steam_utils import find_games as find_steam_games
|
||||
|
||||
errors: list[tuple[str, Exception]] = []
|
||||
BasicGame.steam_games = find_steam_games()
|
||||
BasicGame.gog_games = find_gog_games()
|
||||
BasicGame.origin_games = find_origin_games()
|
||||
BasicGame.epic_games = find_epic_games()
|
||||
BasicGame.eadesktop_games = find_eadesktop_games()
|
||||
BasicGame.epic_games = find_epic_games(errors)
|
||||
BasicGame.eadesktop_games = find_eadesktop_games(errors)
|
||||
|
||||
if errors:
|
||||
QMessageBox.critical(
|
||||
None,
|
||||
"Errors loading game list",
|
||||
(
|
||||
"The following errors occurred while loading the list of available games:\n"
|
||||
f"\n- {'\n\n- '.join('\n '.join(str(e) for e in messageError) for messageError in errors)}"
|
||||
),
|
||||
)
|
||||
|
||||
# File containing the plugin:
|
||||
_fromName: str
|
||||
|
||||
+13
-2
@@ -2,13 +2,14 @@
|
||||
|
||||
import configparser
|
||||
import os
|
||||
import sys
|
||||
import xml.etree.ElementTree as et
|
||||
from configparser import NoOptionError
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
|
||||
|
||||
def find_games() -> Dict[str, Path]:
|
||||
def find_games(errors: list[tuple[str, Exception]] | None = None) -> Dict[str, Path]:
|
||||
"""
|
||||
Find the list of EA Desktop games installed.
|
||||
|
||||
@@ -37,7 +38,17 @@ def find_games() -> Dict[str, Path]:
|
||||
ini_content = "[mod_organizer]\n" + f.read()
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
config.read_string(ini_content)
|
||||
try:
|
||||
config.read_string(ini_content)
|
||||
except configparser.ParsingError as e:
|
||||
error_message = (
|
||||
f'Failed to parse EA Desktop games list file "{user_ini}",\n'
|
||||
" Try to run the launcher to recreate it."
|
||||
)
|
||||
print(error_message, e, file=sys.stderr)
|
||||
if errors is not None:
|
||||
errors.append((error_message, e))
|
||||
return games
|
||||
|
||||
try:
|
||||
install_path = Path(config.get("mod_organizer", "user.downloadinplacedir"))
|
||||
|
||||
+36
-12
@@ -9,8 +9,12 @@ import winreg
|
||||
from collections.abc import Iterable
|
||||
from pathlib import Path
|
||||
|
||||
ErrorList = list[tuple[str, Exception]]
|
||||
|
||||
def find_epic_games() -> Iterable[tuple[str, Path]]:
|
||||
|
||||
def find_epic_games(
|
||||
errors: ErrorList | None = None,
|
||||
) -> Iterable[tuple[str, Path]]:
|
||||
try:
|
||||
with winreg.OpenKey(
|
||||
winreg.HKEY_LOCAL_MACHINE,
|
||||
@@ -30,15 +34,23 @@ def find_epic_games() -> Iterable[tuple[str, Path]]:
|
||||
manifest_file_data["AppName"],
|
||||
Path(manifest_file_data["InstallLocation"]),
|
||||
)
|
||||
except (json.JSONDecodeError, KeyError):
|
||||
except (json.JSONDecodeError, KeyError) as e:
|
||||
error_message = (
|
||||
f'Unable to parse Epic Games manifest file: "{manifest_file_path}"\n'
|
||||
" Try to run the launcher recreate it."
|
||||
)
|
||||
print(
|
||||
"Unable to parse Epic Games manifest file",
|
||||
manifest_file_path,
|
||||
error_message,
|
||||
e,
|
||||
file=sys.stderr,
|
||||
)
|
||||
if errors is not None:
|
||||
errors.append((error_message, e))
|
||||
|
||||
|
||||
def find_legendary_games(config_path: str | None = None) -> Iterable[tuple[str, Path]]:
|
||||
def find_legendary_games(
|
||||
config_path: str | None = None, errors: ErrorList | None = None
|
||||
) -> Iterable[tuple[str, Path]]:
|
||||
# Based on legendary source:
|
||||
# https://github.com/derrod/legendary/blob/master/legendary/lfs/lgndry.py
|
||||
if config_path := config_path or os.environ.get("XDG_CONFIG_HOME"):
|
||||
@@ -53,21 +65,33 @@ def find_legendary_games(config_path: str | None = None) -> Iterable[tuple[str,
|
||||
installed_games = json.load(installed_file)
|
||||
for game in installed_games.values():
|
||||
yield game["app_name"], Path(game["install_path"])
|
||||
except (json.JSONDecodeError, AttributeError, KeyError):
|
||||
except (json.JSONDecodeError, AttributeError, KeyError) as e:
|
||||
error_message = (
|
||||
f'Unable to parse installed games from Legendary/Heroic launcher: "{installed_path}"\n'
|
||||
" Try to run the launcher to recrated the file."
|
||||
)
|
||||
print(
|
||||
"Unable to parse installed games from Legendary",
|
||||
installed_path,
|
||||
error_message,
|
||||
e,
|
||||
file=sys.stderr,
|
||||
)
|
||||
if errors is not None:
|
||||
errors.append((error_message, e))
|
||||
|
||||
|
||||
def find_heroic_games():
|
||||
return find_legendary_games(os.path.expandvars(r"%AppData%\heroic\legendaryConfig"))
|
||||
def find_heroic_games(errors: ErrorList | None = None):
|
||||
return find_legendary_games(
|
||||
os.path.expandvars(r"%AppData%\heroic\legendaryConfig"), errors
|
||||
)
|
||||
|
||||
|
||||
def find_games() -> dict[str, Path]:
|
||||
def find_games(errors: ErrorList | None = None) -> dict[str, Path]:
|
||||
return dict(
|
||||
itertools.chain(find_epic_games(), find_legendary_games(), find_heroic_games())
|
||||
itertools.chain(
|
||||
find_epic_games(errors=errors),
|
||||
find_legendary_games(errors=errors),
|
||||
find_heroic_games(errors=errors),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -111,6 +111,8 @@ class TS4ModDataChecker(ModDataChecker):
|
||||
ValidationResult.FIXABLE,
|
||||
lambda: tree.move(entry, innerPath),
|
||||
)
|
||||
case _:
|
||||
pass
|
||||
return walkReturn
|
||||
|
||||
tree.walk(fixOrValidateEntry)
|
||||
|
||||
Reference in New Issue
Block a user