Skip to content

Commit

Permalink
Refactoring of game features for better management. (ModOrganizer2#149)
Browse files Browse the repository at this point in the history
* Refactoring following uibase change for game features.
* Switch to ruff for linting and formatting.
* Bump stubs to 2.5.1a0.
  • Loading branch information
Holt59 authored Jun 9, 2024
1 parent 1fff6d4 commit 71dbb8c
Show file tree
Hide file tree
Showing 41 changed files with 217 additions and 566 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/linters.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
6 changes: 3 additions & 3 deletions basic_features/basic_mod_data_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion basic_features/basic_save_game_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,10 @@ def __init__(
"""
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

Expand Down
31 changes: 8 additions & 23 deletions basic_game.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,13 @@ def __init__(
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
Expand Down Expand Up @@ -360,19 +360,6 @@ def ids_apply(v: list[int] | list[str] | int | str) -> list[str]:
)


_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
Expand Down Expand Up @@ -408,20 +395,19 @@ def setup():
# 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__()

if not hasattr(self, "_fromName"):
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()
Expand All @@ -442,7 +428,9 @@ def is_eadesktop(self) -> bool:

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

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

def savesDirectory(self) -> QDir:
return self._mappings.savesDirectory.get()

def _featureList(self):
return self._featureMap
5 changes: 3 additions & 2 deletions epic_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
17 changes: 7 additions & 10 deletions games/game_blackandwhite2.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,8 @@ def __init__(self, filepath: Path):
# 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)
)
Expand Down Expand Up @@ -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

Expand Down
6 changes: 3 additions & 3 deletions games/game_bladeandsorcery.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def __init__(self, filepath: Path):
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:
Expand Down Expand Up @@ -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

Expand Down
14 changes: 7 additions & 7 deletions games/game_cyberpunk2077.py
Original file line number Diff line number Diff line change
Expand Up @@ -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._featureMap[mobase.SaveGameInfo] = BasicGameSaveGameInfo(
lambda p: Path(p or "", "screenshot.png"),
parse_cyberpunk_save_metadata,
self._register_feature(BasicLocalSavegames(self.savesDirectory()))
self._register_feature(
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,
Expand Down
4 changes: 2 additions & 2 deletions games/game_da2.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def version(self):

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
2 changes: 1 addition & 1 deletion games/game_daggerfallunity.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def dataLooksValid(
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"
Expand Down
4 changes: 2 additions & 2 deletions games/game_dao.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
4 changes: 2 additions & 2 deletions games/game_darkestdungeon.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def loadBinarySaveFile(self, dataPath: Path):
"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")
Expand Down Expand Up @@ -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):
Expand Down
4 changes: 1 addition & 3 deletions games/game_darkmessiahofmightandmagic.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,5 @@ def _read_save_tga(self, filepath: Path) -> QImage | None:

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
6 changes: 4 additions & 2 deletions games/game_divinityoriginalsin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
8 changes: 2 additions & 6 deletions games/game_divinityoriginalsinee.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,8 @@ def __init__(self):

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]:
Expand Down
2 changes: 1 addition & 1 deletion games/game_dungeonsiege1.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
2 changes: 1 addition & 1 deletion games/game_dungeonsiege2.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
2 changes: 1 addition & 1 deletion games/game_gta-3-de.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
4 changes: 1 addition & 3 deletions games/game_gta-san-andreas-de.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
4 changes: 1 addition & 3 deletions games/game_gta-vice-city-de.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
8 changes: 5 additions & 3 deletions games/game_kerbalspaceprogram.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,11 @@ class KerbalSpaceProgramGame(BasicGame):

def init(self, organizer: mobase.IOrganizer):
super().init(organizer)
self._featureMap[mobase.SaveGameInfo] = BasicGameSaveGameInfo(
lambda s: str(
Path(s).parent.joinpath("banners").joinpath(f"{Path(s).stem}.png")
self._register_feature(
BasicGameSaveGameInfo(
lambda s: str(
Path(s).parent.joinpath("banners").joinpath(f"{Path(s).stem}.png")
)
)
)
return True
Expand Down
2 changes: 1 addition & 1 deletion games/game_mountandblade2.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class MountAndBladeIIGame(BasicGame):

def init(self, organizer: mobase.IOrganizer):
super().init(organizer)
self._featureMap[mobase.ModDataChecker] = MountAndBladeIIModDataChecker()
self._register_feature(MountAndBladeIIModDataChecker())
return True

def executables(self):
Expand Down
1 change: 0 additions & 1 deletion games/game_nfshs.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@


class NFSHSGame(BasicGame):

Name = "Need for Speed: High Stakes Support Plugin"
Author = "uwx"
Version = "1.0.0"
Expand Down
6 changes: 3 additions & 3 deletions games/game_stalkeranomaly.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,9 +238,9 @@ def __init__(self):

def init(self, organizer: mobase.IOrganizer):
BasicGame.init(self, organizer)
self._featureMap[mobase.ModDataChecker] = StalkerAnomalyModDataChecker()
self._featureMap[mobase.ModDataContent] = StalkerAnomalyModDataContent()
self._featureMap[mobase.SaveGameInfo] = StalkerAnomalySaveGameInfo()
self._register_feature(StalkerAnomalyModDataChecker())
self._register_feature(StalkerAnomalyModDataContent())
self._register_feature(StalkerAnomalySaveGameInfo())
organizer.onAboutToRun(lambda _str: self.aboutToRun(_str))
return True

Expand Down
2 changes: 1 addition & 1 deletion games/game_stardewvalley.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class StardewValleyGame(BasicGame):

def init(self, organizer: mobase.IOrganizer):
super().init(organizer)
self._featureMap[mobase.ModDataChecker] = StardewValleyModDataChecker()
self._register_feature(StardewValleyModDataChecker())
return True

def executables(self):
Expand Down
Loading

0 comments on commit 71dbb8c

Please sign in to comment.