Skip to content

Commit

Permalink
feat!: BTRFS snapshot management
Browse files Browse the repository at this point in the history
BREAKING CHANGE: manual setup necessary to work with BTRFS snapshots of
the game installation folder.
  • Loading branch information
alterNERDtive committed Feb 22, 2024
1 parent e67f2be commit f3e502f
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 49 deletions.
115 changes: 66 additions & 49 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,27 @@ manage separate sets of mods in conjunction with a mod manager like
[BG3 Mod Manager](https://github.com/LaughingLeader/BG3ModManager) or
[Lamp](https://github.com/CHollingworth/Lamp).

Well OK, at this point it’s _kind of_ a mod-manager-like thing for loose files
Well OK, at this point it’s _kind of_ a mod-manager-like thing for loose files
mods. Read on :)

## Features

* Store an arbitrary amount of profiles. Each of them contains distinct
* "Mods" folders
* "Script Extender" folders
* Mod settings files
* Loose files mods
* "Mods" folders
* "Script Extender" folders
* Mod settings files
* Loose files mods
* Create and switch between your profiles via shell script.
* Mount loose files mods into your game folder at runtime via overlayfs
* Keep loose files mods separate! No more “which files did that add⁈” when you
* Keep loose files mods separate! No more “which files did that add⁈” when you
want to uninstall one of them.
* **(Experimental)** Manage BTRFS snapshots of the game’s installation folder;
keep backups of old patches around!

## Installation

The overlay script depends on `fusermount`/`fuse-overlayfs`, so you will
probably want to install whatever package(s) provide(s) those for your
The overlay script depends on `fusermount`/`fuse-overlayfs`, so you will
probably want to install whatever package(s) provide(s) those for your
distribution.

Now install the scripts via Basher:
Expand All @@ -38,8 +40,8 @@ basher install git.alterNERD.tv/alterNERDtive/bg3-mod-profiles

or manually:

1. Clone this repository or grab an archive and extract it somewhere.
2. Symlink or copy `bg3overlay` and `bg3switch` into your `PATH`.
1. Clone this repository or grab an archive and extract it somewhere.
2. Symlink or copy `bg3overlay` and `bg3switch` into your `PATH`.

Next you need to change Baldur’s Gate 3’s launch options in Steam:

Expand All @@ -51,21 +53,35 @@ Next you need to change Baldur’s Gate 3’s launch options in Steam:

You might also have to disable Steam Cloud Sync for the game.

To get started, you need to create at least one mod profile using `bg3switch
To get started, you need to create at least one mod profile using `bg3switch
--create PROFILE`.

For the **experimental** snapshot support you will need to make sure beforehand
that your game installation folder is a valid BTRFS snapshot, e.g.:

```bash
> mv Baldurs\ Gate\ 3 tmpdir
> sudo bfrs sub snap create Baldurs\ Gate\ 3
> mv tmpdir/* !$
> rmdir tmpdir
```

Set your game to only update on launch, and make sure that you have activated
the snapshot of the current live version before you update your game. Otherwise
you **will** run into issues and have to verify your game files.

## Configuration

The configuration lives in `$XDG_CONFIG_HOME/bg3-mod-profiles/config` or
The configuration lives in `$XDG_CONFIG_HOME/bg3-mod-profiles/config` or
`~/.config/bg3-mod-profiles/config`.

| option | default | description |
|--------|---------|-------------|
| BG3_DATA_DIR | auto-detected for Steam | The game’s “%APPDATA%” folder. |
| BG3_INSTALL_DIR | auto-detected for Steam | The game’s installation folder. |

You can also create `bg3overlay.pre` and `bg3overlay.post` files in the
configuration folder. They will be executed before mounting and after unmounting
You can also create `bg3overlay.pre` and `bg3overlay.post` files in the
configuration folder. They will be executed before mounting and after unmounting
the overlay, respectively.

## Installing / Updating Mods
Expand All @@ -81,7 +97,7 @@ the `override` subfolder of your profile. This not only means no accidental
mixups between profiles, but also that uninstalling / reinstalling / verifying
/ updating the game files does not affect your loose files mods in any way.

Alternatively, you can put loose files mods in their own subfolder of
Alternatively, you can put loose files mods in their own subfolder of
`profiles/<name>/modular-override`:

```bash
Expand All @@ -92,106 +108,107 @@ Alternatively, you can put loose files mods in their own subfolder of
'Contextual Dialogue Buttons' 'Highlight Prepared Spells' 'Native Camera Tweaks' 'Script Extender'
```

Priority will be the `override` folder, then the modular overrides in
alphabetical order. I use this e.g. to keep my “Native Camera Mod” configuration
Priority will be the `override` folder, then the modular overrides in
alphabetical order. I use this e.g. to keep my “Native Camera Mod” configuration
file in `override` and not have it overwritten by mod updates.

Profiles are stored under `%AppData%/Local/Larian Studios` within the game’s
proton prefix. You can easily access it by running `bg3switch --cd`.

## Usage

See [Configuration](#configuration), `bg3switch --help` and `bg3overlay --help`.
See [Configuration](#configuration), `bg3switch --help`, `bg3overlay --help`
and `bg3version --help`.

## How It Works

### Mod Profiles

`bg3switch` creates mod profiles in the `profiles` folder. Each profile consists
`bg3switch` creates mod profiles in the `profiles` folder. Each profile consists
of a `Mods` folder, an `override` folder, and a `modsettings.lsx` file.

The `Mods` folder is straight up just what you would usually expect in the
The `Mods` folder is straight up just what you would usually expect in the
`Baldur's Gate 3` subfolder and contains all the Mods’ `.pak` files.

The `override` folder contains all lose file mods that you would normally
extract directly into the game’s installation folder. They are overlaid onto it
The `override` folder contains all lose file mods that you would normally
extract directly into the game’s installation folder. They are overlaid onto it
at runtime.

The `modular-override` folder contains loose file mods, each in their own
folder. They are _each_ overlaid onto the game’s installation folder at runtime.
The big advantage of using this approach is being able to keep each mod’s files
The `modular-override` folder contains loose file mods, each in their own
folder. They are _each_ overlaid onto the game’s installation folder at runtime.
The big advantage of using this approach is being able to keep each mod’s files
separate.

Override priority is

1. The `override` folder. Put e.g. your settings file for the native camera mod
here; it will have higher priority than the default file shipped with the mod
1. The `override` folder. Put e.g. your settings file for the native camera mod
here; it will have higher priority than the default file shipped with the mod
then.
2. Modular overrides in alphabetical order.
3. Anything you manually change in the game’s installation folder. (Don’t do
3. Anything you manually change in the game’s installation folder. (Don’t do
that, please.)

The `modsettings.lsx` file resides inside the same subfolder structure it would
normally (`PlayerProfiles/Public`). This makes zipping up your Mods – including
your modsettings file / load order – easier. Personally I use it for co-op
setups to make updating the mod list easier for the other players. _I_ update
The `modsettings.lsx` file resides inside the same subfolder structure it would
normally (`PlayerProfiles/Public`). This makes zipping up your Mods – including
your modsettings file / load order – easier. Personally I use it for co-op
setups to make updating the mod list easier for the other players. _I_ update
everything, I zip it up, they just extract it.

The `Script Extender` folder contains anything you would usually find in
`Baldur's Gate 3/Script Extender`, e.g. the configuration file for [Zerd's Rules
The `Script Extender` folder contains anything you would usually find in
`Baldur's Gate 3/Script Extender`, e.g. the configuration file for [Zerd's Rules
As Written (RAW)](https://www.nexusmods.com/baldursgate3/mods/1329)

### Switching Mod Profiles

Switching mod profiles is the most trivial thing here. The `Mods` folder and
`modsettings.lsx` files are symbolically linked from `current` to the `Baldur's
Switching mod profiles is the most trivial thing here. The `Mods` folder and
`modsettings.lsx` files are symbolically linked from `current` to the `Baldur's
Gate 3` folder.

`current` is just another symbolic link to the currently active profile. E.g.
`current` is just another symbolic link to the currently active profile. E.g.
the link structure for the `Mods` folder looks like this:

```bash
> stat -c "%N" -- Baldur\'s\ Gate\ 3/Mods
"Baldur's Gate 3/Mods" -> '../current/Mods'
> stat -c "%N" -- current
> stat -c "%N" -- current
'current' -> './profiles/solo'
```

`Baldur's Gate 3/Mods``current/Mods``profile/<name>/Mods`

When switching mod profiles, the `current` symbolic link is updated to point to
When switching mod profiles, the `current` symbolic link is updated to point to
the freshly activated / created profile. That’s it!

When you create your first profile, your current configuration is moved to the
When you create your first profile, your current configuration is moved to the
profile and the symbolic links are set up automatically.

### Loading Loose File Mods at Runtime

`bg3overlay --enable` will mount an overlayfs to your game’s installation
folder. The folder itself will be temporarily renamed to `bg3base` in order to
be able to mount the overlay into `Baldurs Gate 3`, where Steam expects the
`bg3overlay --enable` will mount an overlayfs to your game’s installation
folder. The folder itself will be temporarily renamed to `bg3base` in order to
be able to mount the overlay into `Baldurs Gate 3`, where Steam expects the
game.

The working directory and newly created files (the “upper” directory in
The working directory and newly created files (the “upper” directory in
overlayfs terms) reside in `/tmp/bg3/`, which should usually be a RAM disk.

The benefit is that log files and crash dumps will not clutter your `override`
folder. The downside (or additional benefit) is that the files will disappear on
The benefit is that log files and crash dumps will not clutter your `override`
folder. The downside (or additional benefit) is that the files will disappear on
a reboot.

`bg3overlay --disable` will unmount the overlay and rename the game’s
`bg3overlay --disable` will unmount the overlay and rename the game’s
installation folder back to `Baldurs Gate 3`.

## Troubleshooting

### The game doesn’t start after changing the launch options.
### The game doesn’t start after changing the launch options

Double check the launch options match the example. Run `bg3overlay --enable`
manually and see if it works.

If yes, run `bg3overlay --disable` and start looking elsewhere, because it’s not
my fault :)

If not, feel free to [file an
If not, feel free to [file an
issue](https://github.com/alterNERDtive/bg3-mod-profiles/issues) and be
sure to attach the output.
117 changes: 117 additions & 0 deletions bg3version
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#!/usr/bin/env bash
die() {
echo "${1}"
exit 1
}

activate() {
local version="${1:-current}"
check "${_folder}" "bg3.${version}"

echo "Removing current game folder …"
sudo btrfs sub del "${_folder}"
echo "Creating snapshot of \"bg3.${version}\""
sudo btrfs sub snap "bg3.${version}" "${_folder}"
}

check() {
local dir
for dir in "$@"; do
if ! [ -d "${dir}" ] || [ $(stat --format=%i "${dir}") -ne 256 ] || [ $(stat -f --format=%T "${dir}") != "btrfs" ]; then
die "Folder \"${dir}\" is not a BTRFS snapshot, aborting …"
fi
done
}

create() {
check "${_folder}"
local version="${1}"

if [ -z "${version}" ]; then
die "No VERSION given, aborting …"
fi

echo "Creating snapshot \"bg3.${version}\""
sudo btrfs sub snap "${_folder}" "bg3.${version}"
}

delete() {
local version=$1

if [ -z "${version}" ]; then
die "No VERSION given, aborting …"
fi

check "bg3.${version}"

echo "Removing snapshot \"bg3.${version}\""
sudo btrfs sub del "bg3.${version}"
}

list() {
ls | tr " " "\n" | grep "bg3."
}

usage() {
cat <<EOF
Usage: $(basename $0) [OPTION] VERSION
Options:
--activate Use this snapshot as the game folder.
--create, snap Create a new VERSION snapshot.
--delete Delete the snapshot for VERSION.
--help Print this text.
--list Instead of changing to VERSION, list all available versions.
--version Switch to VERSION.
Empty argument list is equivalent to "--list". A single argument is equivalent to
"--activate <argument>".
EOF
}

source "$(dirname $(realpath $0))/util/folders.sh"

CONFIG_FILE="${CONFIG_FILE:-${XDG_CONFIG_HOME:-$HOME/.config}/bg3-mod-profiles/config}"
if [ -f "${CONFIG_FILE}"]; then
while read line; do declare "$line"; done <"${CONFIG_FILE}"
fi

if [ -z "${BG3_DATA_DIR}" ] || [ -z "${BG3_INSTALL_DIR}" ]; then
_library="$(getBg3Library)"
[ $? -eq 1 ] && echo "Steam library folder for Baldur’s Gate 3 not found! Exiting …" && exit 1
BG3_DATA_DIR="${_library}/steamapps/compatdata/1086940/pfx/drive_c/users/steamuser/AppData/Local/Larian Studios/Baldur's Gate 3"
BG3_INSTALL_DIR="${_library}/steamapps/common/Baldurs Gate 3"
fi

cd "$(dirname "${BG3_INSTALL_DIR}")"

_library="$(getBg3Library)"
[ $? -eq 1 ] && echo "Steam library folder for Baldur’s Gate 3 not found! Exiting …" && exit 1
_common="${_library}/steamapps/common"
_folder="$(getBg3Folder ${_library})"
[ $? -eq 1 ] && echo "Steam installation folder for Baldur’s Gate 3 not found! Exiting …" && exit 1

cd "${_common}"

case $1 in
--create | --snap)
create "$2"
;;
--delete)
delete "$2"
;;
--help)
usage
;;
--list)
list
;;
--version)
activate "$2"
;;
"")
list
;;
*)
activate "$1"
;;
esac

0 comments on commit f3e502f

Please sign in to comment.