mirror of
https://github.com/ModOrganizer2/modorganizer-basic_games.git
synced 2026-07-27 14:07:29 -07:00
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.
This commit is contained in:
@@ -12,13 +12,13 @@ jobs:
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.11
|
||||
python-version: 3.12
|
||||
- uses: abatilo/actions-poetry@v2
|
||||
- name: Install
|
||||
run: |
|
||||
cd basic_games
|
||||
poetry install
|
||||
poetry --no-root install
|
||||
- name: Lint
|
||||
run: |
|
||||
cd basic_games
|
||||
poetry run poe lint-all
|
||||
poetry run poe lint
|
||||
|
||||
@@ -160,11 +160,11 @@ class BasicModDataChecker(mobase.ModDataChecker):
|
||||
_regex_patterns: RegexPatterns
|
||||
"""The regex patterns derived from the file (glob) patterns."""
|
||||
|
||||
def __init__(self, file_patterns: GlobPatterns = GlobPatterns()):
|
||||
def __init__(self, file_patterns: GlobPatterns | None = None):
|
||||
super().__init__()
|
||||
|
||||
self._file_patterns = file_patterns
|
||||
self._regex_patterns = RegexPatterns(file_patterns)
|
||||
self._file_patterns = file_patterns or GlobPatterns()
|
||||
self._regex_patterns = RegexPatterns(self._file_patterns)
|
||||
|
||||
def dataLooksValid(
|
||||
self, filetree: mobase.IFileTree
|
||||
|
||||
@@ -80,7 +80,10 @@ class BasicGameSaveGameInfoWidget(mobase.ISaveGameInfoWidget):
|
||||
"""
|
||||
super().__init__(parent)
|
||||
|
||||
self._get_preview = get_preview or (lambda p: None)
|
||||
def _no_preview(p: Path) -> None:
|
||||
return None
|
||||
|
||||
self._get_preview = get_preview or _no_preview
|
||||
self._get_metadata = get_metadata or get_filedate_metadata
|
||||
self._max_width = max_width or 320
|
||||
|
||||
|
||||
+8
-23
@@ -83,13 +83,13 @@ class BasicGameMapping(Generic[_T]):
|
||||
if self._apply_fn is not None:
|
||||
try:
|
||||
value = self._apply_fn(value)
|
||||
except: # noqa
|
||||
except Exception as err:
|
||||
raise ValueError(
|
||||
"Basic game plugin from {} has an invalid {} property.".format(
|
||||
game._fromName, # pyright: ignore[reportPrivateUsage]
|
||||
self._exposed_name,
|
||||
)
|
||||
)
|
||||
) from err
|
||||
self._default = lambda game: value # type: ignore
|
||||
elif default is not None:
|
||||
self._default = default # type: ignore
|
||||
@@ -360,19 +360,6 @@ class BasicGameMappings:
|
||||
)
|
||||
|
||||
|
||||
_GameFeature = (
|
||||
mobase.BSAInvalidation
|
||||
| mobase.DataArchives
|
||||
| mobase.GamePlugins
|
||||
| mobase.LocalSavegames
|
||||
| mobase.ModDataChecker
|
||||
| mobase.ModDataContent
|
||||
| mobase.SaveGameInfo
|
||||
| mobase.ScriptExtender
|
||||
| mobase.UnmanagedMods
|
||||
)
|
||||
|
||||
|
||||
class BasicGame(mobase.IPluginGame):
|
||||
"""This class implements some methods from mobase.IPluginGame
|
||||
to make it easier to create game plugins without having to implement
|
||||
@@ -408,9 +395,6 @@ class BasicGame(mobase.IPluginGame):
|
||||
# Path to the game, as set by MO2:
|
||||
_gamePath: str
|
||||
|
||||
# The feature map:
|
||||
_featureMap: dict[type[_GameFeature], _GameFeature]
|
||||
|
||||
def __init__(self):
|
||||
super(BasicGame, self).__init__()
|
||||
|
||||
@@ -418,10 +402,12 @@ class BasicGame(mobase.IPluginGame):
|
||||
self._fromName = self.__class__.__name__
|
||||
|
||||
self._gamePath = ""
|
||||
self._featureMap = {}
|
||||
|
||||
self._mappings: BasicGameMappings = BasicGameMappings(self)
|
||||
|
||||
def _register_feature(self, feature: mobase.GameFeature) -> bool:
|
||||
return self._organizer.gameFeatures().registerFeature(self, feature, 0, True)
|
||||
|
||||
# Specific to BasicGame:
|
||||
def is_steam(self) -> bool:
|
||||
return self._mappings.steamAPPId.has_value()
|
||||
@@ -442,7 +428,9 @@ class BasicGame(mobase.IPluginGame):
|
||||
|
||||
def init(self, organizer: mobase.IOrganizer) -> bool:
|
||||
self._organizer = organizer
|
||||
self._featureMap[mobase.SaveGameInfo] = BasicGameSaveGameInfo()
|
||||
|
||||
self._register_feature(BasicGameSaveGameInfo())
|
||||
|
||||
if self._mappings.originWatcherExecutables.get():
|
||||
from .origin_utils import OriginWatcher
|
||||
|
||||
@@ -655,6 +643,3 @@ class BasicGame(mobase.IPluginGame):
|
||||
|
||||
def savesDirectory(self) -> QDir:
|
||||
return self._mappings.savesDirectory.get()
|
||||
|
||||
def _featureList(self):
|
||||
return self._featureMap
|
||||
|
||||
+3
-2
@@ -26,8 +26,9 @@ def find_epic_games() -> Iterable[tuple[str, Path]]:
|
||||
try:
|
||||
with open(manifest_file_path, encoding="utf-8") as manifest_file:
|
||||
manifest_file_data = json.load(manifest_file)
|
||||
yield manifest_file_data["AppName"], Path(
|
||||
manifest_file_data["InstallLocation"]
|
||||
yield (
|
||||
manifest_file_data["AppName"],
|
||||
Path(manifest_file_data["InstallLocation"]),
|
||||
)
|
||||
except (json.JSONDecodeError, KeyError):
|
||||
print(
|
||||
|
||||
@@ -191,10 +191,8 @@ class BlackAndWhite2SaveGame(BasicGameSaveGame):
|
||||
# to UNIX time and offset localtime
|
||||
self.lastsave = int(
|
||||
(
|
||||
(
|
||||
struct.unpack("q", self.readInf(info, "date"))[0] / 10000
|
||||
- 11644473600000
|
||||
)
|
||||
struct.unpack("q", self.readInf(info, "date"))[0] / 10000
|
||||
- 11644473600000
|
||||
)
|
||||
- (time.localtime().tm_gmtoff * 1000)
|
||||
)
|
||||
@@ -262,12 +260,11 @@ class BlackAndWhite2Game(BasicGame):
|
||||
|
||||
def init(self, organizer: mobase.IOrganizer) -> bool:
|
||||
BasicGame.init(self, organizer)
|
||||
self._featureMap[mobase.ModDataChecker] = BlackAndWhite2ModDataChecker()
|
||||
self._featureMap[mobase.LocalSavegames] = BasicLocalSavegames(
|
||||
self.savesDirectory()
|
||||
)
|
||||
self._featureMap[mobase.SaveGameInfo] = BasicGameSaveGameInfo(
|
||||
get_metadata=getMetadata, max_width=400
|
||||
|
||||
self._register_feature(BlackAndWhite2ModDataChecker())
|
||||
self._register_feature(BasicLocalSavegames(self.savesDirectory()))
|
||||
self._register_feature(
|
||||
BasicGameSaveGameInfo(get_metadata=getMetadata, max_width=400)
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ class BaSSaveGame(BasicGameSaveGame):
|
||||
h, m, s = save_data["playTime"].split(":")
|
||||
self._elapsed = (int(h), int(m), float(s))
|
||||
f_stat = self._filepath.stat()
|
||||
self._created = f_stat.st_ctime
|
||||
self._created = f_stat.st_birthtime
|
||||
self._modified = f_stat.st_mtime
|
||||
|
||||
def getName(self) -> str:
|
||||
@@ -83,8 +83,8 @@ class BaSGame(BasicGame):
|
||||
|
||||
def init(self, organizer: mobase.IOrganizer) -> bool:
|
||||
BasicGame.init(self, organizer)
|
||||
self._featureMap[mobase.SaveGameInfo] = BasicGameSaveGameInfo(
|
||||
get_metadata=bas_parse_metadata, max_width=400
|
||||
self._register_feature(
|
||||
BasicGameSaveGameInfo(get_metadata=bas_parse_metadata, max_width=400)
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
@@ -314,14 +314,14 @@ class Cyberpunk2077Game(BasicGame):
|
||||
|
||||
def init(self, organizer: mobase.IOrganizer) -> bool:
|
||||
super().init(organizer)
|
||||
self._featureMap[mobase.LocalSavegames] = BasicLocalSavegames(
|
||||
self.savesDirectory()
|
||||
self._register_feature(BasicLocalSavegames(self.savesDirectory()))
|
||||
self._register_feature(
|
||||
BasicGameSaveGameInfo(
|
||||
lambda p: Path(p or "", "screenshot.png"),
|
||||
parse_cyberpunk_save_metadata,
|
||||
)
|
||||
)
|
||||
self._featureMap[mobase.SaveGameInfo] = BasicGameSaveGameInfo(
|
||||
lambda p: Path(p or "", "screenshot.png"),
|
||||
parse_cyberpunk_save_metadata,
|
||||
)
|
||||
self._featureMap[mobase.ModDataChecker] = CyberpunkModDataChecker()
|
||||
self._register_feature(CyberpunkModDataChecker())
|
||||
|
||||
self._modlist_files = ModListFileManager[Literal["archive", "redmod"]](
|
||||
organizer,
|
||||
|
||||
+2
-2
@@ -29,7 +29,7 @@ class DA2Game(BasicGame):
|
||||
|
||||
def init(self, organizer: mobase.IOrganizer):
|
||||
super().init(organizer)
|
||||
self._featureMap[mobase.SaveGameInfo] = BasicGameSaveGameInfo(
|
||||
lambda s: s.parent.joinpath("screen.dds")
|
||||
self._register_feature(
|
||||
BasicGameSaveGameInfo(lambda s: s.parent.joinpath("screen.dds"))
|
||||
)
|
||||
return True
|
||||
|
||||
@@ -38,7 +38,7 @@ class DaggerfallUnityModDataChecker(mobase.ModDataChecker):
|
||||
class DaggerfallUnityGame(BasicGame):
|
||||
def init(self, organizer: mobase.IOrganizer) -> bool:
|
||||
super().init(organizer)
|
||||
self._featureMap[mobase.ModDataChecker] = DaggerfallUnityModDataChecker()
|
||||
self._register_feature(DaggerfallUnityModDataChecker())
|
||||
return True
|
||||
|
||||
Name = "Daggerfall Unity Support Plugin"
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@ class DAOriginsGame(BasicGame):
|
||||
|
||||
def init(self, organizer: mobase.IOrganizer):
|
||||
super().init(organizer)
|
||||
self._featureMap[mobase.SaveGameInfo] = BasicGameSaveGameInfo(
|
||||
lambda s: s.parent.joinpath("screen.dds")
|
||||
self._register_feature(
|
||||
BasicGameSaveGameInfo(lambda s: s.parent.joinpath("screen.dds"))
|
||||
)
|
||||
return True
|
||||
|
||||
@@ -125,7 +125,7 @@ class DarkestDungeonSaveGame(BasicGameSaveGame):
|
||||
"Meta2 has wrong number of bytes: " + str(meta2DataLength)
|
||||
)
|
||||
meta2List: list[tuple[int, int, int]] = []
|
||||
for x in range(numMeta2Entries):
|
||||
for _ in range(numMeta2Entries):
|
||||
entryHash = int.from_bytes(fp.read(4), "little")
|
||||
offset = int.from_bytes(fp.read(4), "little")
|
||||
fieldInfo = int.from_bytes(fp.read(4), "little")
|
||||
@@ -175,7 +175,7 @@ class DarkestDungeonGame(BasicGame):
|
||||
|
||||
def init(self, organizer: mobase.IOrganizer) -> bool:
|
||||
super().init(organizer)
|
||||
self._featureMap[mobase.ModDataChecker] = DarkestDungeonModDataChecker()
|
||||
self._register_feature(DarkestDungeonModDataChecker())
|
||||
return True
|
||||
|
||||
def executables(self):
|
||||
|
||||
@@ -43,7 +43,5 @@ class DarkMessiahOfMightAndMagicGame(BasicGame):
|
||||
|
||||
def init(self, organizer: mobase.IOrganizer):
|
||||
super().init(organizer)
|
||||
self._featureMap[mobase.SaveGameInfo] = BasicGameSaveGameInfo(
|
||||
self._read_save_tga
|
||||
)
|
||||
self._register_feature(BasicGameSaveGameInfo(self._read_save_tga))
|
||||
return True
|
||||
|
||||
@@ -31,7 +31,9 @@ class DivinityOriginalSinGame(BasicGame):
|
||||
|
||||
def init(self, organizer: mobase.IOrganizer):
|
||||
super().init(organizer)
|
||||
self._featureMap[mobase.SaveGameInfo] = BasicGameSaveGameInfo(
|
||||
lambda s: s.with_suffix(".png") # Not confirmed
|
||||
self._register_feature(
|
||||
BasicGameSaveGameInfo(
|
||||
lambda s: s.with_suffix(".png") # Not confirmed
|
||||
)
|
||||
)
|
||||
return True
|
||||
|
||||
@@ -89,12 +89,8 @@ class DivinityOriginalSinEnhancedEditionGame(BasicGame, mobase.IPluginFileMapper
|
||||
|
||||
def init(self, organizer: mobase.IOrganizer):
|
||||
super().init(organizer)
|
||||
self._featureMap[mobase.SaveGameInfo] = BasicGameSaveGameInfo(
|
||||
lambda s: s.with_suffix(".png")
|
||||
)
|
||||
self._featureMap[mobase.ModDataChecker] = (
|
||||
DivinityOriginalSinEnhancedEditionModDataChecker()
|
||||
)
|
||||
self._register_feature(BasicGameSaveGameInfo(lambda s: s.with_suffix(".png")))
|
||||
self._register_feature(DivinityOriginalSinEnhancedEditionModDataChecker())
|
||||
return True
|
||||
|
||||
def mappings(self) -> list[mobase.Mapping]:
|
||||
|
||||
@@ -74,7 +74,7 @@ class DungeonSiegeIGame(BasicGame):
|
||||
|
||||
def init(self, organizer: mobase.IOrganizer):
|
||||
super().init(organizer)
|
||||
self._featureMap[mobase.ModDataChecker] = DungeonSiegeIModDataChecker()
|
||||
self._register_feature(DungeonSiegeIModDataChecker())
|
||||
return True
|
||||
|
||||
def executables(self):
|
||||
|
||||
@@ -74,7 +74,7 @@ class DungeonSiegeIIGame(BasicGame):
|
||||
|
||||
def init(self, organizer: mobase.IOrganizer):
|
||||
super().init(organizer)
|
||||
self._featureMap[mobase.ModDataChecker] = DungeonSiegeIIModDataChecker()
|
||||
self._register_feature(DungeonSiegeIIModDataChecker())
|
||||
return True
|
||||
|
||||
def executables(self):
|
||||
|
||||
@@ -44,7 +44,7 @@ class GTA3DefinitiveEditionGame(BasicGame):
|
||||
|
||||
def init(self, organizer: mobase.IOrganizer) -> bool:
|
||||
super().init(organizer)
|
||||
self._featureMap[mobase.ModDataChecker] = GTA3DefinitiveEditionModDataChecker()
|
||||
self._register_feature(GTA3DefinitiveEditionModDataChecker())
|
||||
return True
|
||||
|
||||
def executables(self):
|
||||
|
||||
@@ -44,9 +44,7 @@ class GTASanAndreasDefinitiveEditionGame(BasicGame):
|
||||
|
||||
def init(self, organizer: mobase.IOrganizer) -> bool:
|
||||
super().init(organizer)
|
||||
self._featureMap[mobase.ModDataChecker] = (
|
||||
GTASanAndreasDefinitiveEditionModDataChecker()
|
||||
)
|
||||
self._register_feature(GTASanAndreasDefinitiveEditionModDataChecker())
|
||||
return True
|
||||
|
||||
def executables(self):
|
||||
|
||||
@@ -44,9 +44,7 @@ class GTAViceCityDefinitiveEditionGame(BasicGame):
|
||||
|
||||
def init(self, organizer: mobase.IOrganizer) -> bool:
|
||||
super().init(organizer)
|
||||
self._featureMap[mobase.ModDataChecker] = (
|
||||
GTAViceCitysDefinitiveEditionModDataChecker()
|
||||
)
|
||||
self._register_feature(GTAViceCitysDefinitiveEditionModDataChecker())
|
||||
return True
|
||||
|
||||
def executables(self):
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user