-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #92 from ZashIn/subnautica/feat/checker+root
Subnautica: feature update v2.1
- Loading branch information
Showing
3 changed files
with
182 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,37 @@ | ||
from ..basic_game import BasicGame | ||
from __future__ import annotations | ||
|
||
import mobase | ||
|
||
class SubnauticaGame(BasicGame): | ||
from . import game_subnautica # namespace to not load SubnauticaGame here, too! | ||
|
||
|
||
class SubnauticaBelowZeroGame(game_subnautica.SubnauticaGame): | ||
|
||
Name = "Subnautica Below Zero Support Plugin" | ||
Author = "dekart811" | ||
Version = "1.0" | ||
Author = "dekart811, Zash" | ||
Version = "2.1" | ||
|
||
GameName = "Subnautica: Below Zero" | ||
GameShortName = "subnauticabelowzero" | ||
GameNexusName = "subnauticabelowzero" | ||
GameSteamId = 848450 | ||
GameBinary = "SubnauticaZero.exe" | ||
GameDataPath = "QMods" | ||
GameDataPath = "_ROOT" | ||
GameDocumentsDirectory = "%GAME_PATH%" | ||
GameSupportURL = ( | ||
r"https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/" | ||
"Game:-Subnautica:-Below-Zero" | ||
) | ||
GameSavesDirectory = r"%GAME_PATH%\SNAppData\SavedGames" | ||
|
||
_game_extra_save_paths = [ | ||
r"%USERPROFILE%\Appdata\LocalLow\Unknown Worlds" | ||
r"\Subnautica Below Zero\SubnauticaZero\SavedGames" | ||
] | ||
|
||
def iniFiles(self): | ||
return ["doorstop_config.ini"] | ||
def init(self, organizer: mobase.IOrganizer) -> bool: | ||
super().init(organizer) | ||
checker = game_subnautica.SubnauticaModDataChecker() | ||
checker.update_patterns({"unfold": ["BepInExPack_BelowZero"]}) | ||
self._featureMap[mobase.ModDataChecker] = checker | ||
return True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,175 @@ | ||
from __future__ import annotations | ||
from enum import Enum | ||
|
||
import os | ||
from collections.abc import Iterable | ||
from pathlib import Path | ||
|
||
from PyQt6.QtCore import QDir, qWarning | ||
|
||
import mobase | ||
|
||
from ..basic_features import BasicModDataChecker | ||
from ..basic_features.basic_save_game_info import ( | ||
BasicGameSaveGame, | ||
BasicGameSaveGameInfo, | ||
) | ||
from ..basic_game import BasicGame | ||
|
||
|
||
class SubnauticaGame(BasicGame): | ||
class SubnauticaModDataChecker(BasicModDataChecker): | ||
default_file_patterns = { | ||
"unfold": ["BepInExPack_Subnautica"], | ||
"valid": ["winhttp.dll", "doorstop_config.ini", "BepInEx", "QMods"], | ||
"delete": [ | ||
"*.txt", | ||
"*.md", | ||
"icon.png", | ||
"license", | ||
"manifest.json", | ||
], | ||
"move": {"plugins": "BepInEx/", "patchers": "BepInEx/", "*": "QMods/"}, | ||
} | ||
|
||
|
||
class SubnauticaGame(BasicGame, mobase.IPluginFileMapper): | ||
|
||
Name = "Subnautica Support Plugin" | ||
Author = "dekart811" | ||
Version = "1.0" | ||
Author = "dekart811, Zash" | ||
Version = "2.1.1" | ||
|
||
GameName = "Subnautica" | ||
GameShortName = "subnautica" | ||
GameNexusName = "subnautica" | ||
GameSteamId = 264710 | ||
GameEpicId = "Jaguar" | ||
GameBinary = "Subnautica.exe" | ||
GameDataPath = "QMods" | ||
GameDocumentsDirectory = "%GAME_PATH%" | ||
GameDataPath = "_ROOT" # Custom mappings to actual root folders below. | ||
GameDocumentsDirectory = r"%GAME_PATH%" | ||
GameSupportURL = ( | ||
r"https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/" | ||
"Game:-Subnautica" | ||
) | ||
GameSavesDirectory = r"%GAME_PATH%\SNAppData\SavedGames" | ||
|
||
_game_extra_save_paths = [ | ||
r"%USERPROFILE%\Appdata\LocalLow\Unknown Worlds" | ||
r"\Subnautica\Subnautica\SavedGames" | ||
] | ||
|
||
_forced_libraries = ["winhttp.dll"] | ||
|
||
_root_blacklist = {GameDataPath.casefold()} | ||
|
||
class MapType(Enum): | ||
FILE = 0 | ||
FOLDER = 1 | ||
|
||
_root_extra_overwrites: dict[str, MapType] = { | ||
"qmodmanager_log-Subnautica.txt": MapType.FILE, | ||
"qmodmanager-config.json": MapType.FILE, | ||
"BepInEx_Shim_Backup": MapType.FOLDER, | ||
} | ||
"""Extra files & folders created in game root by mods / BepInEx after game launch, | ||
but not included in the mod archives. | ||
""" | ||
|
||
def __init__(self): | ||
super().__init__() | ||
mobase.IPluginFileMapper.__init__(self) | ||
|
||
def init(self, organizer: mobase.IOrganizer) -> bool: | ||
super().init(organizer) | ||
self._featureMap[mobase.ModDataChecker] = SubnauticaModDataChecker() | ||
self._featureMap[mobase.SaveGameInfo] = BasicGameSaveGameInfo( | ||
lambda s: os.path.join(s, "screenshot.jpg") | ||
) | ||
return True | ||
|
||
def listSaves(self, folder: QDir) -> list[mobase.ISaveGame]: | ||
return [ | ||
BasicGameSaveGame(folder) | ||
for save_path in ( | ||
folder.absolutePath(), | ||
*(os.path.expandvars(p) for p in self._game_extra_save_paths), | ||
) | ||
for folder in Path(save_path).glob("slot*") | ||
] | ||
|
||
def executableForcedLoads(self) -> list[mobase.ExecutableForcedLoadSetting]: | ||
return [ | ||
mobase.ExecutableForcedLoadSetting(self.binaryName(), lib).withEnabled(True) | ||
for lib in self._forced_libraries | ||
] | ||
|
||
def mappings(self) -> list[mobase.Mapping]: | ||
game = self._organizer.managedGame() | ||
game_path = Path(game.gameDirectory().absolutePath()) | ||
overwrite_path = Path(self._organizer.overwritePath()) | ||
|
||
return [ | ||
*( | ||
# Extra overwrites | ||
self._overwrite_mapping( | ||
overwrite_path / name, | ||
dest, | ||
is_dir=(map_type is self.MapType.FOLDER), | ||
) | ||
for name, map_type in self._root_extra_overwrites.items() | ||
if not (dest := game_path / name).exists() | ||
), | ||
*self._root_mappings(game_path, overwrite_path), | ||
] | ||
|
||
def _root_mappings( | ||
self, game_path: Path, overwrite_path: Path | ||
) -> Iterable[mobase.Mapping]: | ||
for mod_path in self._active_mod_paths(): | ||
mod_name = mod_path.name | ||
|
||
for child in mod_path.iterdir(): | ||
# Check blacklist | ||
if child.name.casefold() in self._root_blacklist: | ||
qWarning(f"Skipping {child.name} ({mod_name})") | ||
continue | ||
destination = game_path / child.name | ||
# Check existing | ||
if destination.exists(): | ||
qWarning( | ||
f"Overwriting of existing game files/folders is not supported! " | ||
f"{destination.as_posix()} ({mod_name})" | ||
) | ||
continue | ||
# Mapping: mod -> root | ||
yield mobase.Mapping( | ||
source=str(child), | ||
destination=str(destination), | ||
is_directory=child.is_dir(), | ||
create_target=False, | ||
) | ||
if child.is_dir(): | ||
# Mapping: overwrite <-> root | ||
yield self._overwrite_mapping( | ||
overwrite_path / child.name, destination, is_dir=True | ||
) | ||
|
||
def _active_mod_paths(self) -> Iterable[Path]: | ||
mods_parent_path = Path(self._organizer.modsPath()) | ||
modlist = self._organizer.modList().allModsByProfilePriority() | ||
for mod in modlist: | ||
if self._organizer.modList().state(mod) & mobase.ModState.ACTIVE: | ||
yield mods_parent_path / mod | ||
|
||
def iniFiles(self): | ||
return ["doorstop_config.ini"] | ||
def _overwrite_mapping( | ||
self, overwrite_source: Path, destination: Path, is_dir: bool | ||
) -> mobase.Mapping: | ||
"""Mapping: overwrite <-> root""" | ||
if is_dir: | ||
# Root folders in overwrite need to exits. | ||
overwrite_source.mkdir(parents=True, exist_ok=True) | ||
return mobase.Mapping( | ||
str(overwrite_source), | ||
str(destination), | ||
is_dir, | ||
create_target=True, | ||
) |