Merge pull request #1008 from Furglitch/feat/remove-archive

fix: remove archiving step
This commit is contained in:
Furglitch
2026-06-13 23:58:23 -05:00
committed by GitHub
4 changed files with 43 additions and 141 deletions

View File

@@ -146,6 +146,33 @@ MO2-LINT currently only supports Proton 10.0. Ensure your game is configured to
Launch the game at least once through Steam/Heroic before installing MO2-LINT. This allows the launcher to set up the Proton prefix with default dependencies.
### Preserving Save Data Across a Prefix Reset
If you need to delete and recreate your game's prefix, your save files will be lost unless you back them up first. Save data is stored inside the prefix under the `users` folder:
- **Steam**: `<prefix>/pfx/drive_c/users/`
- **GOG / Epic (Heroic)**: `<prefix>/drive_c/users/`
To find your prefix path, run:
```bash
mo2-lint list
```
and note the launcher for your instance, then refer to your launcher's library settings for the exact prefix location.
**To preserve your saves:**
1. Copy the `users` folder somewhere safe before deleting the prefix:
```bash
cp -r /path/to/prefix/drive_c/users ~/.tmp/mo2-lint/users-backup
```
2. Delete the old prefix and launch the game once to generate a fresh one, then exit.
3. Restore your saves by copying the `users` folder back:
```bash
cp -r ~/.tmp/mo2-lint/users-backup/. /path/to/new/prefix/drive_c/users/
```
> **Note:** Overwriting the entire `users` folder should be safe for save data, but be aware that some per-user configuration inside the prefix (e.g. registry hives under `users/<name>/`) may carry over stale settings from the old prefix.
## Instance Issues
### Instance Not Found

View File

@@ -1,9 +1,7 @@
#!/usr/bin/env python3
from datetime import datetime
from loguru import logger
from pathlib import Path
from shutil import copytree, move
from step.load_game_info import get_launcher
from util import lang, variables as var, state_file as state
from util.heroic.find_library import get_data as get_heroic_data
@@ -16,8 +14,6 @@ default_tricks = [
yes = ("", "y", "yes")
current_time = datetime.now().strftime("%Y%m%d-%H%M%S")
def load_prefix() -> Path:
"""
@@ -75,56 +71,6 @@ def load_prefix() -> Path:
return prefix
def archive_prefix(prefix: Path):
"""
Archives the existing game prefix by renaming it with a timestamp suffix.\n
Sets the global 'archive' variable to the new archive path.
Parameters
----------
prefix : Path
Path to the existing game prefix to be archived.
"""
if prefix.exists() and prefix.is_dir():
archive_path = prefix.with_suffix(f".{current_time}").expanduser()
move(prefix, archive_path)
logger.trace(f"Backed up existing prefix from {prefix} to {archive_path}")
var.archived_prefix = archive_path
def restore_archived_prefix(prefix: Path):
"""
Restores the `users` directory from the archived prefix to the new prefix,\n
preserving personal data such as saved games.
Parameters
----------
prefix : Path
Path to the new game prefix where personal data will be restored.
"""
match state.current_instance.launcher:
case "steam":
subpath = Path("pfx") / "drive_c" / "users"
src = var.archived_prefix / subpath
case "gog" | "epic" | _:
subpath = Path("drive_c") / "users"
src = var.archived_prefix / subpath
dst = prefix / subpath
logger.trace(
f"Restoring personal data from archived prefix. Source: {src}, Destination: {dst}"
)
copytree(src, dst.with_suffix(".bak"))
logger.trace(
f"Copied personal data to temporary backup location: {dst.with_suffix('.bak')}"
)
copytree(src, dst.with_suffix(".old"))
move(dst.with_suffix(".old"), dst)
logger.trace(
f"Restored personal data to new prefix at {dst}. Original data backed up at {dst.with_suffix('.bak')}"
)
def configure():
"""
Run the necessary winetricks/protontricks for the selected game launcher.
@@ -141,40 +87,18 @@ def configure():
def prompt():
"""
Prompts the user to archive their existing game prefix and create a clean one.
Raises
------
SystemExit
If the user declines to create a clean prefix.
Prompts the user to confirm their prefix is set up, then configures it.
"""
# Prompt user to archive prefix
prefix = load_prefix()
for suffix in ["users", "drive_c", "pfx"]:
if prefix.name == suffix:
prefix = prefix.parent
var.prefix = prefix
logger.trace(f"Resolved prefix path for prompting: {prefix}")
logger.debug(f"Prompting user to archive existing prefix at {prefix}")
if lang.prompt_archive():
logger.trace(
"User agreed to archive existing prefix. Proceeding with archival."
)
archive_prefix(prefix)
else:
logger.warning(
"User declined to archive existing prefix. Proceeding without archival."
)
var.archived_prefix = None
# Prompt user to create a clean prefix
if not lang.prompt_archive_init():
if not lang.prompt_prefix_init():
logger.warning(
"User declined that instructions were followed to create a clean prefix. No support will be provided if errors occur."
"User did not confirm prefix setup. No support will be provided if errors occur."
)
configure()
if var.archived_prefix is not None:
restore_archived_prefix(prefix)
print(lang.prompt_archive_done.format(directory=var.archived_prefix))

View File

@@ -17,11 +17,6 @@ help_pin = """Pin the Mod Organizer 2 installation in the specified directory, p
help_unpin = """Unpin the Mod Organizer 2 installation in the specified directory, allowing updates."""
help_update = """Update the Mod Organizer 2 installation in the specified directory, as well as the launch option for the game."""
prompt_archive_done = """Your prefix has been archived and can be found at: {directory}
Personal data from the archived prefix (e.g. saved games) has been preserved and restored to the new prefix.
Feel free to delete the archive if you no longer need it after confirming your data is intact."""
def list_instances(instance_list: list) -> list:
"""
@@ -44,41 +39,9 @@ def list_instances(instance_list: list) -> list:
return instances
def prompt_archive() -> bool:
def prompt_prefix_init() -> bool:
"""
Prompts the user to archive the existing game prefix and create a clean one.
Returns
-------
bool
True if the user agrees to archive prefix, False otherwise.
"""
logger.debug("Prompting user to archive existing prefix")
if var.unattended:
logger.debug("Unattended mode: skipping archive prompt, defaulting to False")
return False
message = """It is recommended to archive your existing game prefix and create a clean one for Mod Organizer 2 to avoid potential conflicts.
This process will maintain your personal data (save games, settings) while providing a fresh environment for proton-/winetricks to set up the necessary dependencies.
The archived prefix will be renamed with a timestamp suffix for easy identification.
Do you want to proceed with archiving the existing prefix and creating a clean one?"""
msg = {
"type": "confirm",
"message": message,
"name": "archive_clean",
"default": False,
}
result = prompt([msg])
logger.debug(f"User chose to archive prefix: {result['archive_clean']}")
return result["archive_clean"]
def prompt_archive_init() -> bool:
"""
Prompts the user to initialize a clean prefix
Prompts the user to confirm their game prefix has been set up.
Returns
-------
@@ -86,7 +49,7 @@ def prompt_archive_init() -> bool:
True if the user indicates they have followed the instructions, False otherwise.
"""
logger.debug(
f"Prompting user to initialize clean prefix for launcher: {state.current_instance.launcher}"
f"Prompting user to confirm prefix setup for launcher: {state.current_instance.launcher}"
)
if var.unattended:
logger.debug("Unattended mode: skipping prefix init prompt, defaulting to True")
@@ -94,16 +57,16 @@ def prompt_archive_init() -> bool:
match state.current_instance.launcher:
case "steam":
instructions = """To create a clean Steam prefix, please follow these steps:
instructions = """Before continuing, please ensure your Steam prefix is set up correctly:
1. Right-click on the game in your Steam library.
2. Select 'Properties', and navigate to the 'Compatibility' tab.
3. Check the box for 'Force the use of a specific Steam Play compatibility tool', if it's not already checked.
4. From the dropdown menu, select your preferred Proton version. Proton 10.0 is the supported and recommended version.
5. Close the properties window and launch the game once to allow Steam to set up the prefix.
6. Exit the game completely. Do not launch it until the installation process is finished."""
5. If you haven't already, launch the game once to allow Steam to set up the prefix, then exit completely.
6. Do not launch the game again until the installation process is finished."""
case "gog" | "epic":
instructions = """To create a clean prefix for GOG/Epic via Heroic, please follow these steps:
instructions = """Before continuing, please ensure your Heroic prefix is set up correctly:
1. Right-click on the game in your Heroic library.
2. Select 'Settings', and navigate to the 'WINE' tab.
@@ -112,20 +75,18 @@ def prompt_archive_init() -> bool:
* If Proton versions are not available, you will need to enable "Allow using Valve Proton builds to run games"
in Heroic's Settings, under the 'Advanced' tab. Then, ensure that Proton is downloaded and installed in Steam.
4. Optional: Navigate to the 'OTHER' tab and check 'Use Steam Runtime'. This is recommended and may help with compatibility.
5. Launch the game once to allow Heroic to set up the prefix.
6. Exit the game completely. Do not launch it until the installation process is finished."""
5. If you haven't already, launch the game once to allow Heroic to set up the prefix, then exit completely.
6. Do not launch the game again until the installation process is finished."""
msg = {
"type": "confirm",
"message": f"{instructions}\n\n Have you completed these instructions?",
"name": "clean_prefix_done",
"message": f"{instructions}\n\n Have you completed these steps?",
"name": "prefix_init_done",
"default": False,
}
result = prompt([msg])
logger.debug(
f"User completed clean prefix initialization: {result['clean_prefix_done']}"
)
return result["clean_prefix_done"]
logger.debug(f"User confirmed prefix setup: {result['prefix_init_done']}")
return result["prefix_init_done"]
def prompt_install_mo2_checksum_fail(mo2_path: str) -> bool:

View File

@@ -863,16 +863,6 @@ prefix: Path = None
Path to game's Wine/Proton prefix directory.
"""
archived_prefix: Path = None
"""
Path to archived Wine/Proton prefix directory.
"""
archived_prefixes: list[Path] = []
"""
List of previously archived Wine/Proton prefix directories.
"""
heroic_config: tuple[str, str | int, Path, Path, Path] = ()
game_install_path: Path = None