Skip to content

Commit 71dbb8c

Browse files
authored
Refactoring of game features for better management. (#149)
* Refactoring following uibase change for game features. * Switch to ruff for linting and formatting. * Bump stubs to 2.5.1a0.
1 parent 1fff6d4 commit 71dbb8c

41 files changed

Lines changed: 217 additions & 566 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/linters.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ jobs:
1212
- name: Set up Python
1313
uses: actions/setup-python@v2
1414
with:
15-
python-version: 3.11
15+
python-version: 3.12
1616
- uses: abatilo/actions-poetry@v2
1717
- name: Install
1818
run: |
1919
cd basic_games
20-
poetry install
20+
poetry --no-root install
2121
- name: Lint
2222
run: |
2323
cd basic_games
24-
poetry run poe lint-all
24+
poetry run poe lint

basic_features/basic_mod_data_checker.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,11 +160,11 @@ class BasicModDataChecker(mobase.ModDataChecker):
160160
_regex_patterns: RegexPatterns
161161
"""The regex patterns derived from the file (glob) patterns."""
162162

163-
def __init__(self, file_patterns: GlobPatterns = GlobPatterns()):
163+
def __init__(self, file_patterns: GlobPatterns | None = None):
164164
super().__init__()
165165

166-
self._file_patterns = file_patterns
167-
self._regex_patterns = RegexPatterns(file_patterns)
166+
self._file_patterns = file_patterns or GlobPatterns()
167+
self._regex_patterns = RegexPatterns(self._file_patterns)
168168

169169
def dataLooksValid(
170170
self, filetree: mobase.IFileTree

basic_features/basic_save_game_info.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,10 @@ def __init__(
8080
"""
8181
super().__init__(parent)
8282

83-
self._get_preview = get_preview or (lambda p: None)
83+
def _no_preview(p: Path) -> None:
84+
return None
85+
86+
self._get_preview = get_preview or _no_preview
8487
self._get_metadata = get_metadata or get_filedate_metadata
8588
self._max_width = max_width or 320
8689

basic_game.py

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,13 @@ def __init__(
8383
if self._apply_fn is not None:
8484
try:
8585
value = self._apply_fn(value)
86-
except: # noqa
86+
except Exception as err:
8787
raise ValueError(
8888
"Basic game plugin from {} has an invalid {} property.".format(
8989
game._fromName, # pyright: ignore[reportPrivateUsage]
9090
self._exposed_name,
9191
)
92-
)
92+
) from err
9393
self._default = lambda game: value # type: ignore
9494
elif default is not None:
9595
self._default = default # type: ignore
@@ -360,19 +360,6 @@ def ids_apply(v: list[int] | list[str] | int | str) -> list[str]:
360360
)
361361

362362

363-
_GameFeature = (
364-
mobase.BSAInvalidation
365-
| mobase.DataArchives
366-
| mobase.GamePlugins
367-
| mobase.LocalSavegames
368-
| mobase.ModDataChecker
369-
| mobase.ModDataContent
370-
| mobase.SaveGameInfo
371-
| mobase.ScriptExtender
372-
| mobase.UnmanagedMods
373-
)
374-
375-
376363
class BasicGame(mobase.IPluginGame):
377364
"""This class implements some methods from mobase.IPluginGame
378365
to make it easier to create game plugins without having to implement
@@ -408,20 +395,19 @@ def setup():
408395
# Path to the game, as set by MO2:
409396
_gamePath: str
410397

411-
# The feature map:
412-
_featureMap: dict[type[_GameFeature], _GameFeature]
413-
414398
def __init__(self):
415399
super(BasicGame, self).__init__()
416400

417401
if not hasattr(self, "_fromName"):
418402
self._fromName = self.__class__.__name__
419403

420404
self._gamePath = ""
421-
self._featureMap = {}
422405

423406
self._mappings: BasicGameMappings = BasicGameMappings(self)
424407

408+
def _register_feature(self, feature: mobase.GameFeature) -> bool:
409+
return self._organizer.gameFeatures().registerFeature(self, feature, 0, True)
410+
425411
# Specific to BasicGame:
426412
def is_steam(self) -> bool:
427413
return self._mappings.steamAPPId.has_value()
@@ -442,7 +428,9 @@ def is_eadesktop(self) -> bool:
442428

443429
def init(self, organizer: mobase.IOrganizer) -> bool:
444430
self._organizer = organizer
445-
self._featureMap[mobase.SaveGameInfo] = BasicGameSaveGameInfo()
431+
432+
self._register_feature(BasicGameSaveGameInfo())
433+
446434
if self._mappings.originWatcherExecutables.get():
447435
from .origin_utils import OriginWatcher
448436

@@ -655,6 +643,3 @@ def documentsDirectory(self) -> QDir:
655643

656644
def savesDirectory(self) -> QDir:
657645
return self._mappings.savesDirectory.get()
658-
659-
def _featureList(self):
660-
return self._featureMap

epic_utils.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ def find_epic_games() -> Iterable[tuple[str, Path]]:
2626
try:
2727
with open(manifest_file_path, encoding="utf-8") as manifest_file:
2828
manifest_file_data = json.load(manifest_file)
29-
yield manifest_file_data["AppName"], Path(
30-
manifest_file_data["InstallLocation"]
29+
yield (
30+
manifest_file_data["AppName"],
31+
Path(manifest_file_data["InstallLocation"]),
3132
)
3233
except (json.JSONDecodeError, KeyError):
3334
print(

games/game_blackandwhite2.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -191,10 +191,8 @@ def __init__(self, filepath: Path):
191191
# to UNIX time and offset localtime
192192
self.lastsave = int(
193193
(
194-
(
195-
struct.unpack("q", self.readInf(info, "date"))[0] / 10000
196-
- 11644473600000
197-
)
194+
struct.unpack("q", self.readInf(info, "date"))[0] / 10000
195+
- 11644473600000
198196
)
199197
- (time.localtime().tm_gmtoff * 1000)
200198
)
@@ -262,12 +260,11 @@ class BlackAndWhite2Game(BasicGame):
262260

263261
def init(self, organizer: mobase.IOrganizer) -> bool:
264262
BasicGame.init(self, organizer)
265-
self._featureMap[mobase.ModDataChecker] = BlackAndWhite2ModDataChecker()
266-
self._featureMap[mobase.LocalSavegames] = BasicLocalSavegames(
267-
self.savesDirectory()
268-
)
269-
self._featureMap[mobase.SaveGameInfo] = BasicGameSaveGameInfo(
270-
get_metadata=getMetadata, max_width=400
263+
264+
self._register_feature(BlackAndWhite2ModDataChecker())
265+
self._register_feature(BasicLocalSavegames(self.savesDirectory()))
266+
self._register_feature(
267+
BasicGameSaveGameInfo(get_metadata=getMetadata, max_width=400)
271268
)
272269
return True
273270

games/game_bladeandsorcery.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def __init__(self, filepath: Path):
2626
h, m, s = save_data["playTime"].split(":")
2727
self._elapsed = (int(h), int(m), float(s))
2828
f_stat = self._filepath.stat()
29-
self._created = f_stat.st_ctime
29+
self._created = f_stat.st_birthtime
3030
self._modified = f_stat.st_mtime
3131

3232
def getName(self) -> str:
@@ -83,8 +83,8 @@ class BaSGame(BasicGame):
8383

8484
def init(self, organizer: mobase.IOrganizer) -> bool:
8585
BasicGame.init(self, organizer)
86-
self._featureMap[mobase.SaveGameInfo] = BasicGameSaveGameInfo(
87-
get_metadata=bas_parse_metadata, max_width=400
86+
self._register_feature(
87+
BasicGameSaveGameInfo(get_metadata=bas_parse_metadata, max_width=400)
8888
)
8989
return True
9090

games/game_cyberpunk2077.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -314,14 +314,14 @@ class Cyberpunk2077Game(BasicGame):
314314

315315
def init(self, organizer: mobase.IOrganizer) -> bool:
316316
super().init(organizer)
317-
self._featureMap[mobase.LocalSavegames] = BasicLocalSavegames(
318-
self.savesDirectory()
319-
)
320-
self._featureMap[mobase.SaveGameInfo] = BasicGameSaveGameInfo(
321-
lambda p: Path(p or "", "screenshot.png"),
322-
parse_cyberpunk_save_metadata,
317+
self._register_feature(BasicLocalSavegames(self.savesDirectory()))
318+
self._register_feature(
319+
BasicGameSaveGameInfo(
320+
lambda p: Path(p or "", "screenshot.png"),
321+
parse_cyberpunk_save_metadata,
322+
)
323323
)
324-
self._featureMap[mobase.ModDataChecker] = CyberpunkModDataChecker()
324+
self._register_feature(CyberpunkModDataChecker())
325325

326326
self._modlist_files = ModListFileManager[Literal["archive", "redmod"]](
327327
organizer,

games/game_da2.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def version(self):
2929

3030
def init(self, organizer: mobase.IOrganizer):
3131
super().init(organizer)
32-
self._featureMap[mobase.SaveGameInfo] = BasicGameSaveGameInfo(
33-
lambda s: s.parent.joinpath("screen.dds")
32+
self._register_feature(
33+
BasicGameSaveGameInfo(lambda s: s.parent.joinpath("screen.dds"))
3434
)
3535
return True

games/game_daggerfallunity.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def dataLooksValid(
3838
class DaggerfallUnityGame(BasicGame):
3939
def init(self, organizer: mobase.IOrganizer) -> bool:
4040
super().init(organizer)
41-
self._featureMap[mobase.ModDataChecker] = DaggerfallUnityModDataChecker()
41+
self._register_feature(DaggerfallUnityModDataChecker())
4242
return True
4343

4444
Name = "Daggerfall Unity Support Plugin"

0 commit comments

Comments
 (0)