Compare commits
3 Commits
17bd736e59
...
6117173507
| Author | SHA1 | Date | |
|---|---|---|---|
| 6117173507 | |||
| e0616de6c3 | |||
| 1f35f6b078 |
@@ -0,0 +1,94 @@
|
||||
---
|
||||
name: beatsaber-plugin-builder
|
||||
description: Build, test-compile, or package Beat Saber PC BSIPA plugin source on Linux from local checkouts, GitHub branches, or pull requests. Use when asked to build a Beat Saber plugin, compile a plugin PR, produce a DLL/zip artifact, configure BeatSaberDir/local refs for dotnet builds, or diagnose Linux build failures for net472 BSIPA projects.
|
||||
---
|
||||
|
||||
# Build Beat Saber Plugin
|
||||
|
||||
Use this skill to compile PC BSIPA plugin projects on this Linux host, especially from the `plugin-helper` repo. The workflow is adapted from the Setlist repo's Linux/Cursor build notes.
|
||||
|
||||
For detailed Linux/BSMT behavior, read [linux-bsipa-build.md](references/linux-bsipa-build.md) when you need to configure a project, fix missing references, package artifacts, or explain a failure.
|
||||
|
||||
## Core Workflow
|
||||
|
||||
1. Confirm context.
|
||||
|
||||
```bash
|
||||
pwd
|
||||
git status --short
|
||||
```
|
||||
|
||||
In `plugin-helper`, run commands from repo root and keep temporary source checkouts under `.state/build/<name>` unless the user asks for another location. Do not disturb unrelated dirty files.
|
||||
|
||||
2. Resolve source.
|
||||
|
||||
For a GitHub PR, clone or reuse a checkout under `.state/build`, add/fetch the upstream remote if needed, and check out the PR head:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/<owner>/<repo>.git .state/build/<name>
|
||||
git -C .state/build/<name> fetch origin pull/<pr>/head:pr-<pr>
|
||||
git -C .state/build/<name> checkout pr-<pr>
|
||||
```
|
||||
|
||||
If the PR is from a fork and the repo already has a fork remote, preserve it. Never overwrite local source changes without explicit approval.
|
||||
|
||||
3. Inspect build shape.
|
||||
|
||||
```bash
|
||||
find <checkout> -maxdepth 3 -name '*.sln' -o -name '*.csproj' -o -name 'manifest.json' -o -name 'Directory.Build.props'
|
||||
sed -n '1,240p' <project>.csproj
|
||||
```
|
||||
|
||||
Note target framework, `BeatSaberDir`, `LocalRefsDir`, package references, and whether `DisableCopyToPlugins` is already set.
|
||||
|
||||
4. Choose Beat Saber references.
|
||||
|
||||
Prefer a BSManager instance matching the plugin or manifest `gameVersion`. Common local roots:
|
||||
|
||||
```text
|
||||
/home/pleb/.local/share/BSManager/BSInstances/<version>
|
||||
/home/pleb/Windows/Users/pleb/BSManager/BSInstances/<version>
|
||||
```
|
||||
|
||||
Use a local `.csproj.user` or MSBuild properties rather than committing machine paths. For test builds, pass `-p:DisableCopyToPlugins=True` and, for BSMT projects that expose it, `-p:DisableCopyToGame=True` so compilation does not mutate the game install.
|
||||
|
||||
5. Restore and build.
|
||||
|
||||
Prefer the solution when present; otherwise build the plugin `.csproj`:
|
||||
|
||||
```bash
|
||||
dotnet restore <solution-or-project>
|
||||
dotnet build <solution-or-project> -c Release -p:DisableCopyToPlugins=True -p:DisableCopyToGame=True
|
||||
```
|
||||
|
||||
If the project lacks .NET Framework reference assemblies on Linux, add or pass `Microsoft.NETFramework.ReferenceAssemblies.net472` as described in the reference file.
|
||||
|
||||
6. Collect artifacts.
|
||||
|
||||
Find produced DLLs and release zips:
|
||||
|
||||
```bash
|
||||
find <checkout> -path '*/bin/*' \( -name '*.dll' -o -name '*.zip' \) -print
|
||||
```
|
||||
|
||||
Verify the DLL name, version, and manifest. If the result is meant for `plugin-helper`, place a copy under `.state/instances/<instance>/downloads/<plugin-id>/` and use the helper plan/apply workflow rather than hand-copying into a BSManager instance.
|
||||
|
||||
7. Validate.
|
||||
|
||||
For skill edits inside this repo, run:
|
||||
|
||||
```bash
|
||||
python /home/pleb/.codex/skills/.system/skill-creator/scripts/quick_validate.py .agents/skills/beatsaber-plugin-builder
|
||||
PYTHONPATH=src python -m compileall -q src tests
|
||||
PYTHONPATH=src python -m unittest discover -s tests
|
||||
```
|
||||
|
||||
For a plugin build, at minimum report the exact `dotnet build` result and artifact paths. For live game validation, use `docs/SMOKETEST.md` and tear down Beat Saber processes afterward.
|
||||
|
||||
## Failure Triage
|
||||
|
||||
- Missing `Microsoft.NETFramework.ReferenceAssemblies`: add the net472 reference-assemblies package or pass an equivalent MSBuild/package restore fix.
|
||||
- Missing `Main.dll`, `HMUI.dll`, `IPA.Loader.dll`, `BSML.dll`, `SongCore.dll`, or similar: `BeatSaberDir` points at the wrong/unmodded instance, or dependencies are absent from `Plugins/`/`Libs/`.
|
||||
- BSMT copies to `IPA/Pending` or `Plugins` during build: rebuild with `-p:DisableCopyToPlugins=True -p:DisableCopyToGame=True` unless the user explicitly wants deployment.
|
||||
- NuGet package restore fails because a source is missing: inspect `NuGet.config` and installed package sources; use repo-local configuration where possible.
|
||||
- API compile errors from a PR: inspect the target game/dependency versions before changing code. Prefer one target version rather than compatibility branches unless the user asks for multi-version support.
|
||||
@@ -0,0 +1,4 @@
|
||||
interface:
|
||||
display_name: "Build Beat Saber Plugin"
|
||||
short_description: "Build BSIPA plugins on Linux"
|
||||
default_prompt: "Use $beatsaber-plugin-builder to build this Beat Saber plugin PR on Linux."
|
||||
@@ -0,0 +1,131 @@
|
||||
# Linux BSIPA Build Reference
|
||||
|
||||
## Source Notes
|
||||
|
||||
This workflow comes from `/home/pleb/ops/beatsaber/setlist/docs/pc-modding.md` and `/home/pleb/ops/beatsaber/setlist/docs/bootstrap.md`. Prefer those local files and local clones under `~/src` over web copies when deeper reference material is needed.
|
||||
|
||||
## Toolchain
|
||||
|
||||
- PC BSIPA plugins are usually .NET Framework `net472` class libraries.
|
||||
- Linux `dotnet` SDK 6+ can build them because output DLLs are platform-agnostic CIL loaded by Beat Saber under Proton.
|
||||
- `BeatSaberModdingTools.Tasks` supplies the MSBuild targets normally driven by Visual Studio/Rider BSMT extensions.
|
||||
- On this host, `dotnet --list-sdks` should show a usable SDK. NuGet is available for package inspection.
|
||||
|
||||
## Game References
|
||||
|
||||
Point `BeatSaberDir` at a modded BSManager instance containing:
|
||||
|
||||
```text
|
||||
Beat Saber.exe
|
||||
Beat Saber_Data/Managed/
|
||||
IPA/
|
||||
Libs/
|
||||
Plugins/
|
||||
winhttp.dll
|
||||
```
|
||||
|
||||
Preferred local managed instance root:
|
||||
|
||||
```text
|
||||
/home/pleb/.local/share/BSManager/BSInstances/<version>
|
||||
```
|
||||
|
||||
Windows mirror root:
|
||||
|
||||
```text
|
||||
/home/pleb/Windows/Users/pleb/BSManager/BSInstances/<version>
|
||||
```
|
||||
|
||||
Use `BeatSaberVersion.txt` for the exact game version. The manifest `gameVersion` normally uses the `major.minor.patch` prefix, not the build suffix.
|
||||
|
||||
## Project Configuration
|
||||
|
||||
Prefer machine-local configuration in `<Project>.csproj.user`:
|
||||
|
||||
```xml
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<BeatSaberDir>/home/pleb/.local/share/BSManager/BSInstances/1.40.8</BeatSaberDir>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
```
|
||||
|
||||
Some projects use `LocalRefsDir` and set:
|
||||
|
||||
```xml
|
||||
<BeatSaberDir>$(LocalRefsDir)</BeatSaberDir>
|
||||
```
|
||||
|
||||
For those, pass `-p:LocalRefsDir=/path/to/instance` or add a local `.csproj.user` property if the project imports it.
|
||||
|
||||
When changing a project file for Linux portability, prefer the smallest explicit fix:
|
||||
|
||||
- Add `Microsoft.NETFramework.ReferenceAssemblies.net472` when MSBuild reports missing .NET Framework reference assemblies.
|
||||
- Set or pass `DisableCopyToPlugins=True` for artifact-only builds.
|
||||
- Keep hint paths rooted at `$(BeatSaberDir)` where possible.
|
||||
- Do not add broad multi-version compatibility logic unless requested.
|
||||
|
||||
## Build Commands
|
||||
|
||||
From the plugin checkout:
|
||||
|
||||
```bash
|
||||
dotnet restore <solution-or-project>
|
||||
dotnet build <solution-or-project> -c Release -p:DisableCopyToPlugins=True
|
||||
```
|
||||
|
||||
For a Debug smoke build:
|
||||
|
||||
```bash
|
||||
dotnet build <solution-or-project> -c Debug -p:DisableCopyToPlugins=True
|
||||
```
|
||||
|
||||
Expected outputs:
|
||||
|
||||
```text
|
||||
bin/Debug/<Plugin>.dll
|
||||
bin/Release/<Plugin>.dll
|
||||
bin/Release/zip/<Plugin>-<version>.zip
|
||||
bin/<Configuration>/Artifact/Plugins/<Plugin>.dll
|
||||
```
|
||||
|
||||
The exact layout depends on BSMT version and project customization.
|
||||
|
||||
## BSMT Copy Behavior
|
||||
|
||||
`BeatSaberModdingTools.Tasks` may copy built DLLs into the game directory. On Unix hosts, its process check can behave differently and some projects add a custom target to copy into `<BeatSaberDir>/Plugins/`.
|
||||
|
||||
For builds that should not alter a live instance, pass both property names; different BSMT projects/targets surface different copy switches:
|
||||
|
||||
```bash
|
||||
-p:DisableCopyToPlugins=True -p:DisableCopyToGame=True
|
||||
```
|
||||
|
||||
If the purpose is to install the built artifact, copy it into plugin-helper's state downloads and use `plugin-helper plan/apply`, or follow `docs/SMOKETEST.md` for intentional live validation.
|
||||
|
||||
## Common Reference Failures
|
||||
|
||||
- `MSB3644` or missing `.NETFramework,Version=v4.7.2` reference assemblies: add `Microsoft.NETFramework.ReferenceAssemblies.net472`.
|
||||
- Missing game assemblies such as `Main.dll`, `HMUI.dll`, `UnityEngine.CoreModule.dll`: `BeatSaberDir` is wrong or incomplete.
|
||||
- Missing mod dependencies such as `BSML.dll`, `SongCore.dll`, `SiraUtil.dll`, `BeatSaberPlaylistsLib.dll`: install or point at an instance containing those plugins, or fetch the dependency DLL from its verified release only when appropriate.
|
||||
- `IPA.Loader.dll` missing: BSIPA is not bootstrapped in that instance.
|
||||
|
||||
## Artifact Handoff To plugin-helper
|
||||
|
||||
For a built plugin DLL intended for a managed instance:
|
||||
|
||||
```bash
|
||||
mkdir -p .state/instances/<instance>/downloads/<plugin-id>
|
||||
cp <checkout>/<path>/bin/Release/<Plugin>.dll .state/instances/<instance>/downloads/<plugin-id>/<Plugin>.dll
|
||||
sha256sum .state/instances/<instance>/downloads/<plugin-id>/<Plugin>.dll
|
||||
```
|
||||
|
||||
Then update registry/lock data only if the user asked to manage/install the artifact, and use:
|
||||
|
||||
```bash
|
||||
PYTHONPATH=src python -m plugin_helper --state-dir .state check --instance <instance>
|
||||
PYTHONPATH=src python -m plugin_helper --state-dir .state plan --instance <instance> --plugin <plugin-id>
|
||||
PYTHONPATH=src python -m plugin_helper --state-dir .state apply <plan-path>
|
||||
```
|
||||
|
||||
Inspect the generated plan before applying.
|
||||
+52
-16
@@ -1,5 +1,5 @@
|
||||
---
|
||||
name: install-beatsaber-plugin
|
||||
name: beatsaber-plugin-manager
|
||||
description: Install or update a Beat Saber plugin in the plugin-helper repo by using the local helper workflow. Use when the user asks to add, install, update, bump, lock, bootstrap BSIPA, or manage a Beat Saber plugin release for a BSManager instance. Prefer upstream GitHub release artifacts for normal plugins; use BeatMods primarily as compatibility/dependency metadata, with CDN artifacts only for inaccessible upstream assets, BeatMods-only packages, or framework/library dependencies.
|
||||
---
|
||||
|
||||
@@ -12,11 +12,16 @@ Use the repository's own `plugin-helper` commands to manage plugins for BSManage
|
||||
|
||||
## Hard Guardrail
|
||||
|
||||
For ordinary GitHub-hosted plugins, require an explicit GitHub release URL from
|
||||
the user's prompt before selecting any release or repository.
|
||||
For ordinary GitHub-hosted plugins, require an explicit GitHub repository or
|
||||
release URL from the user's prompt or from a user-provided local planning note
|
||||
before selecting any release.
|
||||
|
||||
- If the prompt does not contain a GitHub release URL, stop and ask the user for it.
|
||||
- Do not search the web to discover a repository or "correct" release URL.
|
||||
- If the user has provided a GitHub repository URL but not a release URL, use
|
||||
that exact repository's release API and choose the most appropriate
|
||||
non-draft, non-prerelease release/asset for the target Beat Saber instance.
|
||||
- If the user has provided no GitHub repository or release URL for the plugin,
|
||||
stop and ask the user for one.
|
||||
- Do not search the web to discover a repository or "correct" project URL.
|
||||
- Do not substitute a similar repo, fork, project, or package name.
|
||||
- If the provided URL is a general releases page, use that repo's release API and choose the latest non-draft, non-prerelease release unless the user asks for a specific tag/version.
|
||||
- If the provided URL is a tag URL, use that exact tag.
|
||||
@@ -75,15 +80,38 @@ https://github.com/<owner>/<repo>/releases/download/<tag>/<asset>
|
||||
For BeatMods bootstrap or verified packages, query BeatMods with a browser-like user agent:
|
||||
|
||||
```bash
|
||||
python - <<'PY'
|
||||
PYTHONPATH=src python - <<'PY'
|
||||
import json, urllib.request
|
||||
from plugin_helper.beatmods import by_version_id, normalize_mods
|
||||
|
||||
game_version = "<instance>"
|
||||
url = f"https://beatmods.com/api/mods?status=verified&gameVersion={game_version}&gameName=BeatSaber&platform=steampc"
|
||||
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 plugin-helper"})
|
||||
with urllib.request.urlopen(req, timeout=20) as response:
|
||||
data = json.load(response)
|
||||
mods = data["mods"] if isinstance(data, dict) and "mods" in data else data
|
||||
print(json.dumps(mods, indent=2)[:20000])
|
||||
mods = normalize_mods(data)
|
||||
mods_by_version_id = by_version_id(mods)
|
||||
print(json.dumps(
|
||||
[
|
||||
{
|
||||
"name": mod.name,
|
||||
"modId": mod.mod_id,
|
||||
"gitUrl": mod.git_url,
|
||||
"category": mod.category,
|
||||
"versionId": mod.version_id,
|
||||
"modVersion": mod.mod_version,
|
||||
"zipHash": mod.zip_hash,
|
||||
"dependencies": mod.dependencies,
|
||||
"dependencyNames": [
|
||||
mods_by_version_id[dep].name
|
||||
for dep in mod.dependencies
|
||||
if dep in mods_by_version_id
|
||||
],
|
||||
}
|
||||
for mod in mods
|
||||
],
|
||||
indent=2,
|
||||
)[:20000])
|
||||
PY
|
||||
```
|
||||
|
||||
@@ -103,7 +131,8 @@ https://github.com/<owner>/<repo>/releases/download/<tag>/<asset>
|
||||
the smoketest. This creates/copies the root `winhttp.dll` and root `Libs/`
|
||||
substrate that IPA needs.
|
||||
|
||||
For GitHub URLs, resolve the release from the user-provided URL only.
|
||||
For GitHub URLs, resolve the release from the user-provided repository or
|
||||
release URL only.
|
||||
|
||||
Derive `<owner>/<repo>` and optional `<tag>` from the URL. Query the GitHub API directly for metadata:
|
||||
|
||||
@@ -189,13 +218,20 @@ https://github.com/<owner>/<repo>/releases/download/<tag>/<asset>
|
||||
|
||||
Use `PYTHONPATH=src`; plain `python -m unittest` may fail in this source-layout repo.
|
||||
|
||||
For live Beat Saber validation, follow `docs/SMOKETEST.md`. Do not rely on
|
||||
`timeout` to kill the full game process tree. Prefer the documented
|
||||
foreground Proton launch with a background watchdog that sleeps for the smoke
|
||||
window, then terminates Beat Saber by process name. Confirm `Logs/_latest.log`
|
||||
has the expected IPA/plugin lines. If the game remains open after the
|
||||
watchdog cleanup, say so and ask the user to close it manually rather than
|
||||
leaving the turn with Beat Saber running.
|
||||
After any successful apply that changes a live BSManager instance, always
|
||||
run the documented live smoketest before the final response unless the user
|
||||
explicitly says not to. Do not stop at helper check, unit tests, compile
|
||||
checks, or file-hash verification for live installs. For live Beat Saber
|
||||
validation, follow `docs/SMOKETEST.md`. Before starting the launch, announce
|
||||
in agent chat how long the smoketest window will run for, using the current
|
||||
duration from `docs/SMOKETEST.md` unless the user requested a different
|
||||
duration. Do not rely on `timeout` to kill the full game process tree.
|
||||
Prefer the documented foreground Proton launch with a background watchdog
|
||||
that sleeps for the smoke window, then terminates Beat Saber by process
|
||||
name. Confirm `Logs/_latest.log` has the expected IPA/plugin lines and
|
||||
enough menu/UI initialization evidence for the plugin under test. If the
|
||||
game remains open after the watchdog cleanup, say so and ask the user to
|
||||
close it manually rather than leaving the turn with Beat Saber running.
|
||||
|
||||
For BSIPA/SongCore bootstrap, expected successful log lines include:
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
interface:
|
||||
display_name: "Install Beat Saber Plugin"
|
||||
short_description: "Update Beat Saber plugins via helper"
|
||||
default_prompt: "Use $install-beatsaber-plugin to install or update a Beat Saber plugin from this release URL: <url>."
|
||||
default_prompt: "Use $beatsaber-plugin-manager to install or update a Beat Saber plugin from this release URL: <url>."
|
||||
@@ -1,4 +1,5 @@
|
||||
/.state/
|
||||
/.state-*/
|
||||
/.pytest_cache/
|
||||
/build/
|
||||
/dist/
|
||||
|
||||
@@ -13,24 +13,30 @@ Guidance for coding agents working in this repo.
|
||||
investigating launch behavior, inherited Steam arguments, instance layout, or
|
||||
Proton environment details unless the user explicitly asks for BSManager code
|
||||
changes.
|
||||
- Prefer repo-local state with `--state-dir .state` for planned installs unless
|
||||
the task explicitly targets the user's live default state.
|
||||
- Prefer repo-local state for planned installs unless the task explicitly
|
||||
targets the user's live default state. Use `--state-dir .state` for the local
|
||||
Linux install and `--state-dir .state-windows` for the mounted Windows install
|
||||
when both roots contain the same instance name.
|
||||
|
||||
## Workflow Rules
|
||||
|
||||
- Run commands from the repo root with `PYTHONPATH=src`.
|
||||
- For human-style inspection, prefer the menu with repo-local state:
|
||||
`PYTHONPATH=src python -m plugin_helper --state-dir .state menu`.
|
||||
- When targeting the local Linux BSManager install rather than the Windows
|
||||
mirror, pass `--instances-root /home/pleb/.local/share/BSManager/BSInstances`
|
||||
or choose that path explicitly in the menu.
|
||||
- When targeting the local Linux BSManager install, pass
|
||||
`--instances-root /home/pleb/.local/share/BSManager/BSInstances` and normally
|
||||
`--state-dir .state`.
|
||||
- When targeting the mounted Windows BSManager install, pass
|
||||
`--instances-root /home/pleb/Windows/Users/pleb/BSManager/BSInstances` and
|
||||
normally `--state-dir .state-windows`.
|
||||
- Use the helper commands instead of manually copying plugin files into an
|
||||
instance.
|
||||
- Treat BSIPA as a bootstrap phase:
|
||||
- `bootstrap` installs the locked BSIPA archive and records generated files.
|
||||
- ordinary plugin plans should depend on healthy bootstrap state.
|
||||
- Be careful with duplicate instance names across Windows and local roots. Use
|
||||
the menu or pass `--instances-root` explicitly when targeting one install.
|
||||
the menu or pass `--instances-root` explicitly when targeting one install, and
|
||||
keep install/bootstrap state separate per target root.
|
||||
|
||||
## Validation
|
||||
|
||||
|
||||
@@ -21,6 +21,40 @@ Default BSManager instance roots:
|
||||
Override with `--instances-root` or `PLUGIN_HELPER_INSTANCES_ROOT`. To search
|
||||
multiple explicit roots, separate them with `:`.
|
||||
|
||||
## Managing Multiple Installs
|
||||
|
||||
The helper is intended to manage both the local Linux BSManager install and the
|
||||
mounted Windows install. Lockfiles and registry entries are shared by Beat Saber
|
||||
version, but install state is target-specific. When the same instance name
|
||||
exists under both roots, such as `1.44.1`, use an explicit `--instances-root`
|
||||
and a separate state directory for each target.
|
||||
|
||||
Suggested repo-local convention:
|
||||
|
||||
```text
|
||||
.state/ local Linux BSManager state
|
||||
.state-windows/ mounted Windows BSManager state
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
||||
```sh
|
||||
PYTHONPATH=src python -m plugin_helper \
|
||||
--instances-root /home/pleb/.local/share/BSManager/BSInstances \
|
||||
--state-dir .state \
|
||||
installed --instance 1.44.1
|
||||
|
||||
PYTHONPATH=src python -m plugin_helper \
|
||||
--instances-root /home/pleb/Windows/Users/pleb/BSManager/BSInstances \
|
||||
--state-dir .state-windows \
|
||||
installed --instance 1.44.1
|
||||
```
|
||||
|
||||
Do not reuse the same state directory for both targets when their instance names
|
||||
match. The current state layout is keyed by instance name, so sharing one state
|
||||
directory would mix bootstrap records, generated plans, backups, and installed
|
||||
file records for different game trees.
|
||||
|
||||
## Commands
|
||||
|
||||
For normal use, run the menu from the repo root. Use repo-local state so the
|
||||
@@ -33,7 +67,8 @@ PYTHONPATH=src python -m plugin_helper --state-dir .state menu
|
||||
|
||||
The individual subcommands are mostly for automation and debugging. If you use
|
||||
them, pass `--state-dir .state` unless you intentionally want the default live
|
||||
state outside this repo.
|
||||
state outside this repo or are intentionally targeting the Windows install with
|
||||
`.state-windows`.
|
||||
|
||||
Install assets are currently expected to already exist locally, usually under:
|
||||
|
||||
@@ -41,6 +76,13 @@ Install assets are currently expected to already exist locally, usually under:
|
||||
.state/instances/<instance>/downloads/<plugin-id>/
|
||||
```
|
||||
|
||||
For a second target-specific state directory, copy or re-download the same
|
||||
locked assets under that state root before planning. For example:
|
||||
|
||||
```text
|
||||
.state-windows/instances/<instance>/downloads/<plugin-id>/
|
||||
```
|
||||
|
||||
## Beat Saber Data Backups
|
||||
|
||||
`backup-userdata` copies the mounted Windows `UserData` folder and Beat Saber
|
||||
@@ -68,6 +110,22 @@ the latest backup run. Use
|
||||
for a `UserData`-only sync, or `--backup-root <path>` to choose a different
|
||||
destination.
|
||||
|
||||
`restore-userdata` copies those backup trees back into a target instance. It
|
||||
moves the current `UserData` and AppData trees aside first as
|
||||
`<name>.pre-restore-<timestamp>` snapshots. On Linux BSManager installs,
|
||||
AppData is restored into the BSManager SharedContent Proton prefix unless
|
||||
`--appdata-path` is provided. If `backup-descriptor.json` is present, its
|
||||
`instance` field must match `--instance`.
|
||||
|
||||
Example restore into the local Linux instance:
|
||||
|
||||
```sh
|
||||
PYTHONPATH=src python -m plugin_helper \
|
||||
--instances-root /home/pleb/.local/share/BSManager/BSInstances \
|
||||
restore-userdata \
|
||||
--instance 1.44.1
|
||||
```
|
||||
|
||||
The backup intentionally omits bulky/generated data:
|
||||
|
||||
- `UserData/BeatLeader/Replays`
|
||||
|
||||
+29
-5
@@ -1,16 +1,23 @@
|
||||
# plugin-helper Design
|
||||
|
||||
`plugin-helper` is a Python CLI for managing Beat Saber plugins in a mounted Windows BSManager install. It installs from individual GitHub releases, keeps per-game-version plugin selections pinned, records exact filesystem changes, and leaves compatibility judgment visible enough for an agent to help when upstream packaging is inconsistent.
|
||||
`plugin-helper` is a Python CLI for managing Beat Saber plugins in BSManager
|
||||
installs. It installs from pinned release artifacts, keeps per-game-version
|
||||
plugin selections locked, records exact filesystem changes, and leaves
|
||||
compatibility judgment visible enough for an agent to help when upstream
|
||||
packaging is inconsistent.
|
||||
|
||||
The initial target is the Linux side of `incineroar`, after the Windows partition has been mounted manually. The current Beat Saber instances live under:
|
||||
The current targets are the local Linux BSManager install and the mounted
|
||||
Windows BSManager install:
|
||||
|
||||
```text
|
||||
/home/pleb/Windows/Users/pleb/BSManager/BSInstances
|
||||
/home/pleb/.local/share/BSManager/BSInstances
|
||||
```
|
||||
|
||||
## Goals
|
||||
|
||||
- Manage plugins for one BSManager Beat Saber instance at a time, such as `1.40.8`.
|
||||
- Manage plugins for one BSManager Beat Saber instance at a time, such as
|
||||
`1.44.1`, while supporting the same instance name under multiple roots.
|
||||
- Pull plugin releases directly from configured GitHub repositories.
|
||||
- Determine candidate updates while respecting the pinned Beat Saber version.
|
||||
- Support selective updates and explicit pins.
|
||||
@@ -25,8 +32,10 @@ The initial target is the Linux side of `incineroar`, after the Windows partitio
|
||||
- Replacing BSManager as a GUI or Beat Saber instance manager.
|
||||
- Downloading or downgrading Beat Saber versions.
|
||||
- Running `nixos-rebuild switch`.
|
||||
- Mutating the Windows partition unless the user has mounted it and explicitly runs an apply command.
|
||||
- Treating Nix as the plugin installer. Nix should package `plugin-helper`; the CLI should manage the mutable mounted game tree.
|
||||
- Mutating the Windows partition unless the user has mounted it and explicitly
|
||||
runs an apply command targeting that root.
|
||||
- Treating Nix as the plugin installer. Nix should package `plugin-helper`; the
|
||||
CLI should manage mutable game trees.
|
||||
|
||||
## Core Model
|
||||
|
||||
@@ -82,6 +91,21 @@ Runtime state should not need to live inside the repository. By default, keep mu
|
||||
|
||||
For early development, a `--state-dir` option is useful so plans and manifests can be kept in the repo while the format settles.
|
||||
|
||||
When managing both local Linux and mounted Windows installs, install state must
|
||||
be separated by target root as well as by instance name. The current state
|
||||
layout is keyed by instance name, so two `1.44.1` installs should not share one
|
||||
state directory. A practical repo-local convention is:
|
||||
|
||||
```text
|
||||
.state/ local Linux BSManager state
|
||||
.state-windows/ mounted Windows BSManager state
|
||||
```
|
||||
|
||||
The registry and lockfile remain shared for a Beat Saber version. Downloads may
|
||||
be copied or re-fetched into each target-specific state directory, but generated
|
||||
plans, bootstrap records, backups, and `installed.json` belong to one target
|
||||
game tree.
|
||||
|
||||
## Registry
|
||||
|
||||
The registry describes plugin sources and install behavior. It should be human-editable because many Beat Saber plugins have small packaging differences.
|
||||
|
||||
+7
-1
@@ -15,7 +15,13 @@ The initial tool should stay conservative:
|
||||
- Mutating operations apply an explicit plan and record exact file hashes.
|
||||
- Nix packages `plugin-helper`, but does not directly manage the mutable Beat Saber tree.
|
||||
|
||||
This works well while Beat Saber is still launched from a Windows install or a mounted Windows filesystem.
|
||||
This works well while Beat Saber is launched from either the local Linux
|
||||
BSManager install or a mounted Windows BSManager filesystem.
|
||||
|
||||
For the near term, the Python state model should treat target roots as distinct
|
||||
installations even when they share an instance name. Lockfiles can stay keyed by
|
||||
Beat Saber version, but bootstrap state, generated plans, backups, and
|
||||
`installed.json` need to stay target-specific.
|
||||
|
||||
## Future: Nix-Orchestrated Plugin Sets
|
||||
|
||||
|
||||
+5
-5
@@ -3,10 +3,10 @@
|
||||
Use this workflow after installing or removing a plugin batch. It is adapted
|
||||
from the Setlist repo's working Proton/BSManager smoketest notes.
|
||||
|
||||
The routine smoketest should be short: about 10 seconds wall time for launch and
|
||||
log check, followed by immediate teardown. If expected plugin log lines do not
|
||||
appear inside that window, treat that as a failure to investigate instead of
|
||||
stretching the timeout.
|
||||
The routine smoketest should be short: about 20 seconds wall time for launch,
|
||||
menu/UI initialization, and log check, followed by immediate teardown. If
|
||||
expected plugin log lines do not appear inside that window, treat that as a
|
||||
failure to investigate instead of repeatedly stretching the timeout.
|
||||
|
||||
## Preconditions
|
||||
|
||||
@@ -47,7 +47,7 @@ export SteamEnv=1
|
||||
export OXR_PARALLEL_VIEWS=1
|
||||
|
||||
(
|
||||
sleep 10
|
||||
sleep 20
|
||||
pkill -TERM -f "[B]eat Saber.exe" || pkill -TERM -f "[B]eat Saber" || true
|
||||
sleep 2
|
||||
pkill -KILL -f "[B]eat Saber.exe" || pkill -KILL -f "[B]eat Saber" || true
|
||||
|
||||
@@ -117,175 +117,172 @@ that made them necessary, but record that relationship when it is known.
|
||||
|
||||
Purpose: verify the clean 1.44.1 game and loader before adding gameplay mods.
|
||||
|
||||
| Plugin | Status | Source/version | Verification notes |
|
||||
| --- | --- | --- | --- |
|
||||
| BSIPA | <span style="color:#3fb950; font-weight:600">verified</span> | GitHub `nike4613/BeatSaber-IPA-Reloaded` tag `4.3.7`, asset `BSIPA-net472-x64.zip`; BeatMods version id 2561, zipHash `947774ef1010ff809ae05e345e269a90` | GitHub asset is byte-identical to the BeatMods CDN zip used for initial bootstrap. Smoketest produced `Logs/_latest.log`; IPA reported game version 1.44.1 and BSIPA 4.3.7. |
|
||||
| Plugin | Upstream | Status | Source/version | Verification notes |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| BSIPA | [github](https://github.com/nike4613/BeatSaber-IPA-Reloaded) | <span style="color:#3fb950; font-weight:600">verified</span> | GitHub `nike4613/BeatSaber-IPA-Reloaded` tag `4.3.7`, asset `BSIPA-net472-x64.zip`; BeatMods version id 2561, zipHash `947774ef1010ff809ae05e345e269a90` | GitHub asset is byte-identical to the BeatMods CDN zip used for initial bootstrap. Smoketest produced `Logs/_latest.log`; IPA reported game version 1.44.1 and BSIPA 4.3.7. |
|
||||
|
||||
### Batch 1: Core Song Loading
|
||||
|
||||
Purpose: get custom song loading working before UI, leaderboard, or cosmetic
|
||||
mods are added.
|
||||
|
||||
| Plugin | Status | Source/version | Verification notes |
|
||||
| --- | --- | --- | --- |
|
||||
| SongCore | <span style="color:#3fb950; font-weight:600">verified</span> | BeatMods 3.16.0, version id 2564, zipHash `0af9c0a03074c17ca15c1b667a0e30c8`; BeatMods preferred repo `Kylemc1413/SongCore` currently has no matching 3.16.0 GitHub release asset | IPA loaded SongCore; full song refresh loaded 2 songs from `CustomLevels`. Keep as BeatMods CDN fallback until a matching upstream asset is found. |
|
||||
| BeatSaberMarkupLanguage | <span style="color:#3fb950; font-weight:600">verified</span> | GitHub `monkeymanboy/BeatSaberMarkupLanguage` tag `v1.14.1`, asset `BeatSaberMarkupLanguage-v1.14.1+bs.1.41.1-RELEASE.zip`; BeatMods version id 2567, zipHash `46149d03f8549e07f2c88fefde4337b2` | GitHub asset is byte-identical to the BeatMods CDN zip used for initial bootstrap. IPA loaded BSML; font fallback warnings only. |
|
||||
| SiraUtil | <span style="color:#3fb950; font-weight:600">verified</span> | GitHub `Auros/SiraUtil` tag `v3.3.1`, asset `SiraUtil-v3.3.1+bs.1.42.0.zip`; BeatMods version id 2565, zipHash `ae14f7d3192a919d5d996c802fbde037` | GitHub asset is byte-identical to the BeatMods CDN zip used for initial bootstrap. IPA loaded SiraUtil and installed app/menu installers. |
|
||||
| Plugin | Upstream | Status | Source/version | Verification notes |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| SongCore | [beatmods zip](https://beatmods.com/cdn/mod/0af9c0a03074c17ca15c1b667a0e30c8.zip) | <span style="color:#3fb950; font-weight:600">verified</span> | BeatMods 3.16.0, version id 2564, zipHash `0af9c0a03074c17ca15c1b667a0e30c8`; BeatMods preferred repo `Kylemc1413/SongCore` currently has no matching 3.16.0 GitHub release asset | IPA loaded SongCore; full song refresh loaded 2 songs from `CustomLevels`. Keep as BeatMods CDN fallback until a matching upstream asset is found. |
|
||||
| BeatSaberMarkupLanguage | [github](https://github.com/monkeymanboy/BeatSaberMarkupLanguage) | <span style="color:#3fb950; font-weight:600">verified</span> | GitHub `monkeymanboy/BeatSaberMarkupLanguage` tag `v1.14.1`, asset `BeatSaberMarkupLanguage-v1.14.1+bs.1.41.1-RELEASE.zip`; BeatMods version id 2567, zipHash `46149d03f8549e07f2c88fefde4337b2` | GitHub asset is byte-identical to the BeatMods CDN zip used for initial bootstrap. IPA loaded BSML; font fallback warnings only. |
|
||||
| SiraUtil | [github](https://github.com/Auros/SiraUtil) | <span style="color:#3fb950; font-weight:600">verified</span> | GitHub `Auros/SiraUtil` tag `v3.3.1`, asset `SiraUtil-v3.3.1+bs.1.42.0.zip`; BeatMods version id 2565, zipHash `ae14f7d3192a919d5d996c802fbde037` | GitHub asset is byte-identical to the BeatMods CDN zip used for initial bootstrap. IPA loaded SiraUtil and installed app/menu installers. |
|
||||
|
||||
### Batch 2: Custom Map Capabilities
|
||||
|
||||
Purpose: enable common map extensions after basic song loading is proven.
|
||||
|
||||
| Plugin | Status | Source/version | Verification notes |
|
||||
| --- | --- | --- | --- |
|
||||
| CustomJSONData | <span style="color:#3fb950; font-weight:600">verified</span> | GitHub `Aeroluna/CustomJSONData` tag `v2.6.8`, asset `CustomJSONData-2.6.8+1.40.0-bs1.40.0-7c2c32c.zip`; BeatMods version id 2327, zipHash `fed31638bbb678580ef760ec83cfd486` | GitHub asset is byte-identical to the BeatMods CDN zip. IPA loaded CustomJSONData 2.6.8+1.40.0; game reached main initialization; SongCore still loaded 2 custom songs. |
|
||||
| Heck | <span style="color:#f85149; font-weight:600">omitted</span> | No BeatMods verified 1.44.1 entry found on 2026-06-28 | Required by Chroma/NoodleExtensions/Vivify; skip until a compatible source is identified. |
|
||||
| Chroma | <span style="color:#f85149; font-weight:600">omitted</span> | No BeatMods verified 1.44.1 entry found on 2026-06-28 | Skip until Heck and a compatible Chroma source are identified. |
|
||||
| NoodleExtensions | <span style="color:#f85149; font-weight:600">omitted</span> | No BeatMods verified 1.44.1 entry found on 2026-06-28 | Skip until Heck and a compatible NoodleExtensions source are identified. |
|
||||
| Vivify | <span style="color:#f85149; font-weight:600">omitted</span> | No BeatMods verified 1.44.1 entry found on 2026-06-28 | Skip until Heck and a compatible Vivify source are identified. |
|
||||
| Plugin | Upstream | Status | Source/version | Verification notes |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| CustomJSONData | [github](https://github.com/Aeroluna/CustomJSONData) | <span style="color:#3fb950; font-weight:600">verified</span> | GitHub `Aeroluna/CustomJSONData` tag `v2.6.8`, asset `CustomJSONData-2.6.8+1.40.0-bs1.40.0-7c2c32c.zip`; BeatMods version id 2327, zipHash `fed31638bbb678580ef760ec83cfd486` | GitHub asset is byte-identical to the BeatMods CDN zip. IPA loaded CustomJSONData 2.6.8+1.40.0; game reached main initialization; SongCore still loaded 2 custom songs. |
|
||||
| Heck | [github](https://github.com/Aeroluna/Heck) | <span style="color:#3fb950; font-weight:600">verified</span> | GitHub `Aeroluna/Heck` tag `v2026-05-02`, asset `Heck-1.8.3+1.42.1-bs1.42.1-3ebc6a2.zip`; no BeatMods verified 1.44.1 entry found on 2026-06-28 | Latest upstream Heck asset targets `bs1.42.1`; IPA loaded Heck 1.8.3+1.42.1 and installed Heck app/menu installers during smoketest. |
|
||||
| Chroma | [github](https://github.com/Aeroluna/Heck) | <span style="color:#3fb950; font-weight:600">verified</span> | GitHub `Aeroluna/Heck` tag `v2026-02-23`, asset `Chroma-2.9.22+1.42.1-bs1.42.1-b38d924.zip`; no BeatMods verified 1.44.1 entry found on 2026-06-28 | Required `LookupID` 1.0.1. Latest upstream Chroma asset targets `bs1.42.1`; IPA loaded Chroma 2.9.22+1.42.1 and installed Chroma app/menu installers during smoketest. |
|
||||
| NoodleExtensions | [github](https://github.com/Aeroluna/Heck) | <span style="color:#3fb950; font-weight:600">verified</span> | GitHub `Aeroluna/Heck` tag `v2026-05-09`, asset `NoodleExtensions-1.7.21+1.42.1-bs1.42.1-3bbcaf6.zip`; no BeatMods verified 1.44.1 entry found on 2026-06-28 | Latest upstream Noodle Extensions asset targets `bs1.42.1`; IPA loaded NoodleExtensions 1.7.21+1.42.1 and installed Noodle app installer during smoketest. |
|
||||
| Vivify | [github](https://github.com/Aeroluna/Vivify) | <span style="color:#3fb950; font-weight:600">verified</span> | GitHub `Aeroluna/Vivify` tag `v1.1.0`, asset `Vivify-1.1.0+1.42.1-bs1.42.1-f83aa3c.zip`; no BeatMods verified 1.44.1 entry found on 2026-06-28 | Required `CameraUtils` 1.0.8 and `AssetBundleLoadingTools` 1.1.13. Latest upstream Vivify asset targets `bs1.42.1`; IPA loaded Vivify 1.1.0+1.42.1, installed Vivify app/menu installers, and created camera controllers during smoketest. |
|
||||
|
||||
### Batch 3: Downloaders and Playlists
|
||||
|
||||
Purpose: restore in-game song discovery and playlist management.
|
||||
|
||||
| Plugin | Status | Source/version | Verification notes |
|
||||
| --- | --- | --- | --- |
|
||||
| BeatSaverDownloader | <span style="color:#d29922; font-weight:600">verified with warning</span> | BeatMods 6.0.7, version id 2217, zipHash `a740c6e68a9b5d1dfda3cc8e81f7cf06`; BeatMods preferred repo `Top-Cat/BeatSaverDownloader` exposes no release assets through the GitHub releases API | IPA loaded BeatSaver Downloader 6.0.7 and started its internal webserver. Warning: it probed for missing `BetterSongList.dll` with IPA library-loader `CRITICAL` lines, then continued. |
|
||||
| PlaylistManager | <span style="color:#f85149; font-weight:600">omitted</span> | No BeatMods verified 1.44.1 entry found on 2026-06-28 | Skip until a compatible source is identified. |
|
||||
| BeatSaverUpdater | <span style="color:#3fb950; font-weight:600">verified</span> | GitHub `ibillingsley/BeatSaverUpdater` tag `1.2.11`, asset `BeatSaverUpdater-1.2.11-bs1.39.1-3698f98.zip`; BeatMods version id 2352, zipHash `d9ea8dd0cbaac66cbb02fa59a548e42b` | GitHub asset is byte-identical to the BeatMods CDN zip. IPA loaded BeatSaverUpdater 1.2.11. |
|
||||
| BeatSaverVoting | <span style="color:#f85149; font-weight:600">omitted</span> | No BeatMods verified 1.44.1 entry found on 2026-06-28 | Skip until a compatible source is identified. |
|
||||
| BeatSaberPlaylistsLib | <span style="color:#3fb950; font-weight:600">verified</span> | BeatMods 1.7.2, version id 2175, zipHash `a3418b75ed7294a3856f3eca12bbd672`; BeatMods preferred repo `Meivyn/BeatSaberPlaylistsLib` exposes no release assets through the GitHub releases API | IPA loaded BeatSaberPlaylistsLib 1.7.2. |
|
||||
| BeatSaverSharp | <span style="color:#3fb950; font-weight:600">verified</span> | BeatMods 3.4.5, version id 1831, zipHash `be37e13e93d9ac7da4efbdc3f514fa8f`; BeatMods preferred repo `lolPants/BeatSaverSharp` was inaccessible through the GitHub releases API | IPA loaded BeatSaverSharp 3.4.5. |
|
||||
| ScoreSaberSharp | <span style="color:#d29922; font-weight:600">verified with warning</span> | BeatMods 0.1.0, version id 445, zipHash `8713168c598577ee7c73fa3cf0e26f5c`; BeatMods lists `scoresaber.com` rather than a GitHub release source | IPA loaded ScoreSaberSharp 0.1.0. Warning: bare manifest does not declare files. |
|
||||
| BS Utils | <span style="color:#3fb950; font-weight:600">verified</span> | BeatMods 1.14.3, version id 2563, zipHash `918d13ac2821a3a17b2819f8861453e9`; BeatMods preferred repo `Kylemc1413/Beat-Saber-Utils` exposes no matching 1.14.3 GitHub release asset | IPA loaded BS Utils 1.14.3. |
|
||||
| Ini Parser | <span style="color:#3fb950; font-weight:600">verified</span> | BeatMods 2.5.9, version id 1352, zipHash `5df74ad1c6b120fecdc615dd55f15b88` | IPA loaded INI Parser 2.5.9. |
|
||||
| ImageSharp | <span style="color:#3fb950; font-weight:600">verified</span> | BeatMods 2.0.0, version id 1428, zipHash `b642fec88b0f84a0643ebd401d08da35` | IPA loaded ImageSharp 2.0.0. |
|
||||
| System.IO.Compression | <span style="color:#d29922; font-weight:600">verified with warning</span> | BeatMods 4.6.57, version id 1763, zipHash `a4e9e26f61967e56168e08eecb01ab88` | IPA loaded System.IO.Compression 4.6.57. Warning: duplicate-library notice because the game also ships this assembly. |
|
||||
| System.IO.Compression.FileSystem | <span style="color:#3fb950; font-weight:600">verified</span> | BeatMods 4.7.3056, version id 1762, zipHash `e19f6fd395d54de7bfcbbbe3084dea28` | IPA loaded System.IO.Compression.FileSystem 4.7.3056. |
|
||||
| Plugin | Upstream | Status | Source/version | Verification notes |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| BeatSaverDownloader | [beatmods zip](https://beatmods.com/cdn/mod/a740c6e68a9b5d1dfda3cc8e81f7cf06.zip) | <span style="color:#d29922; font-weight:600">verified with warning</span> | BeatMods 6.0.7, version id 2217, zipHash `a740c6e68a9b5d1dfda3cc8e81f7cf06`; BeatMods preferred repo `Top-Cat/BeatSaverDownloader` exposes no release assets through the GitHub releases API | IPA loaded BeatSaver Downloader 6.0.7 and started its internal webserver. Warning: it probed for missing `BetterSongList.dll` with IPA library-loader `CRITICAL` lines, then continued. |
|
||||
| PlaylistManager | [github](https://github.com/rithik-b/PlaylistManager) | <span style="color:#3fb950; font-weight:600">verified</span> | Local PR82 build from `.state/build/playlistmanager-pr82-skilltest`, artifact `PlaylistManager-1.7.4-bs1.44.0-da1ad17.zip`; replaces failed BeatMods 1.7.3 compatibility trial | IPA loaded PlaylistManager 1.7.4, installed `PlaylistManagerAppInstaller`, and reached `MainSystemInit` during smoketest. The old `IPlatformUserModel` / `PlatformUserModel` failure did not recur. |
|
||||
| BeatSaverUpdater | [github](https://github.com/ibillingsley/BeatSaverUpdater) | <span style="color:#3fb950; font-weight:600">verified</span> | GitHub `ibillingsley/BeatSaverUpdater` tag `1.2.11`, asset `BeatSaverUpdater-1.2.11-bs1.39.1-3698f98.zip`; BeatMods version id 2352, zipHash `d9ea8dd0cbaac66cbb02fa59a548e42b` | GitHub asset is byte-identical to the BeatMods CDN zip. IPA loaded BeatSaverUpdater 1.2.11. |
|
||||
| BeatSaverVoting | [beatmods zip](https://beatmods.com/cdn/mod/bc002ed1a43e2c6d3a10d0750e5d94b4.zip) | <span style="color:#f85149; font-weight:600">failed compatibility trial</span> | BeatMods 2.4.6, version id 2159, zipHash `bc002ed1a43e2c6d3a10d0750e5d94b4`; most recent blessed entry found was for Beat Saber 1.40.8, with no BeatMods verified entry for 1.44.1, 1.44.0, 1.43.0, 1.42.0, or 1.41.1 | IPA discovered and loaded BeatSaverVoting 2.4.6, but BS Utils caught a menu event failure from `BeatSaverVoting` caused by `TypeLoadException` resolving `IPlatformUserModel` from `PlatformUserModel`. Removed from the live instance after the failed smoketest. |
|
||||
| BeatSaberPlaylistsLib | [beatmods zip](https://beatmods.com/cdn/mod/a3418b75ed7294a3856f3eca12bbd672.zip) | <span style="color:#3fb950; font-weight:600">verified</span> | BeatMods 1.7.2, version id 2175, zipHash `a3418b75ed7294a3856f3eca12bbd672`; BeatMods preferred repo `Meivyn/BeatSaberPlaylistsLib` exposes no release assets through the GitHub releases API | IPA loaded BeatSaberPlaylistsLib 1.7.2. |
|
||||
| BeatSaverSharp | [beatmods zip](https://beatmods.com/cdn/mod/be37e13e93d9ac7da4efbdc3f514fa8f.zip) | <span style="color:#3fb950; font-weight:600">verified</span> | BeatMods 3.4.5, version id 1831, zipHash `be37e13e93d9ac7da4efbdc3f514fa8f`; BeatMods preferred repo `lolPants/BeatSaverSharp` was inaccessible through the GitHub releases API | IPA loaded BeatSaverSharp 3.4.5. |
|
||||
| ScoreSaberSharp | [beatmods zip](https://beatmods.com/cdn/mod/8713168c598577ee7c73fa3cf0e26f5c.zip) | <span style="color:#d29922; font-weight:600">verified with warning</span> | BeatMods 0.1.0, version id 445, zipHash `8713168c598577ee7c73fa3cf0e26f5c`; BeatMods lists `scoresaber.com` rather than a GitHub release source | IPA loaded ScoreSaberSharp 0.1.0. Warning: bare manifest does not declare files. |
|
||||
| BS Utils | [beatmods zip](https://beatmods.com/cdn/mod/918d13ac2821a3a17b2819f8861453e9.zip) | <span style="color:#3fb950; font-weight:600">verified</span> | BeatMods 1.14.3, version id 2563, zipHash `918d13ac2821a3a17b2819f8861453e9`; BeatMods preferred repo `Kylemc1413/Beat-Saber-Utils` exposes no matching 1.14.3 GitHub release asset | IPA loaded BS Utils 1.14.3. |
|
||||
| Ini Parser | [beatmods zip](https://beatmods.com/cdn/mod/5df74ad1c6b120fecdc615dd55f15b88.zip) | <span style="color:#3fb950; font-weight:600">verified</span> | BeatMods 2.5.9, version id 1352, zipHash `5df74ad1c6b120fecdc615dd55f15b88` | IPA loaded INI Parser 2.5.9. |
|
||||
| ImageSharp | [beatmods zip](https://beatmods.com/cdn/mod/b642fec88b0f84a0643ebd401d08da35.zip) | <span style="color:#3fb950; font-weight:600">verified</span> | BeatMods 2.0.0, version id 1428, zipHash `b642fec88b0f84a0643ebd401d08da35` | IPA loaded ImageSharp 2.0.0. |
|
||||
| System.IO.Compression | [beatmods zip](https://beatmods.com/cdn/mod/a4e9e26f61967e56168e08eecb01ab88.zip) | <span style="color:#d29922; font-weight:600">verified with warning</span> | BeatMods 4.6.57, version id 1763, zipHash `a4e9e26f61967e56168e08eecb01ab88` | IPA loaded System.IO.Compression 4.6.57. Warning: duplicate-library notice because the game also ships this assembly. |
|
||||
| System.IO.Compression.FileSystem | [beatmods zip](https://beatmods.com/cdn/mod/e19f6fd395d54de7bfcbbbe3084dea28.zip) | <span style="color:#3fb950; font-weight:600">verified</span> | BeatMods 4.7.3056, version id 1762, zipHash `e19f6fd395d54de7bfcbbbe3084dea28` | IPA loaded System.IO.Compression.FileSystem 4.7.3056. |
|
||||
|
||||
### Batch 4: Leaderboards and Ranking
|
||||
|
||||
Purpose: add online leaderboard/ranking integrations after core song behavior is
|
||||
stable.
|
||||
|
||||
| Plugin | Status | Source/version | Verification notes |
|
||||
| --- | --- | --- | --- |
|
||||
| ScoreSaber | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Verify menu panel and song leaderboard. |
|
||||
| BeatLeader | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Verify menu panel and song leaderboard. |
|
||||
| LeaderboardCore | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Dependency for BeatLeader. |
|
||||
| AccSaber | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Manual/plugin-helper registry candidate. |
|
||||
| SongRankedBadge | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Verify song-list badges. |
|
||||
| Plugin | Upstream | Status | Source/version | Verification notes |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| ScoreSaber | [github](https://github.com/ScoreSaber/pc-mod) | <span style="color:#d29922; font-weight:600">verified with warning</span> | GitHub `ScoreSaber/pc-mod` tag `v3.3.27`, asset `ScoreSaber-v3.3.27-bs1.42.0-to-1.44.0-9b4cfcf.zip`; GitHub release digest matched downloaded asset | IPA loaded ScoreSaber 3.3.27 and synchronized its clock. Warning: FPFC/no-VR smoke logged `openxr_loader not found` while trying to get HMD info. |
|
||||
| BeatLeader + LeaderboardCore | [github](https://github.com/BeatLeader/beatleader-mod) | <span style="color:#d29922; font-weight:600">verified with warning</span> | GitHub `BeatLeader/beatleader-mod` tag `v0.10.0`, asset `BeatLeader-0.10.0-bs1.42+.zip`; GitHub release digest matched downloaded asset; archive bundles `Plugins/LeaderboardCore.dll`, superseding the standalone BeatMods CDN trial | IPA loaded BeatLeader 0.10.0 and bundled LeaderboardCore 1.7.0, made BeatLeader API requests, and patched 13 ScoreSaber installers. Warnings: optional interop plugins missing; FPFC/no-VR smoke logged OpenXR session not running; bundled LeaderboardCore logged a Harmony patch error for `PanelView_SetIsLoaded`. |
|
||||
| AccSaber | [github](https://github.com/not-dexter/accsaber-reloaded-plugin) | <span style="color:#f85149; font-weight:600">failed compatibility trial</span> | GitHub `not-dexter/accsaber-reloaded-plugin` tag `v1.1.3`, asset `1.40.8.zip`; GitHub release digest matched downloaded asset; latest release has no 1.44.x asset | IPA loaded AccSaber 1.1.3, but the smoke log hit `TypeLoadException: Invalid type AccSaber.Managers.AccSaberStore` for `AccSaberMissionScreen`. Removed from the live instance after the failed trial. |
|
||||
| SongRankedBadge | [github](https://github.com/qe201020335/SongRankedBadge) | <span style="color:#d29922; font-weight:600">verified with warning</span> | GitHub `qe201020335/SongRankedBadge` tag `v1.0.6`, asset `SongRankedBadge-1.0.6-bs1.40.0-88ee233.zip`; BeatMods version id 2267, zipHash `c6944b8a4b00b0c0bb1d44f273b3bb18` | IPA loaded SongRankedBadge 1.0.6, resolved SongDetailsCache, loaded song details, and initialized. Warning: manifest targets Beat Saber 1.40.0. |
|
||||
|
||||
### Batch 5: Practice and Gameplay Tweaks
|
||||
|
||||
Purpose: add small gameplay helpers two or three at a time.
|
||||
|
||||
| Plugin | Status | Source/version | Verification notes |
|
||||
| --- | --- | --- | --- |
|
||||
| IntroSkip | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Verify song start behavior. |
|
||||
| FailButton | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Verify practice/fail UI behavior. |
|
||||
| EasyOffset | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Verify settings UI opens. |
|
||||
| GottaGoFast | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Verify startup and song load. |
|
||||
| HitsoundTweaks | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Verify settings and audio behavior. |
|
||||
| KeepMyOverridesPls | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Verify startup only unless override test is easy. |
|
||||
| SoundReplacer | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Verify startup and settings. |
|
||||
| KeyRemapper | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Manual install candidate. |
|
||||
| SquatToBegin | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Manual install candidate. |
|
||||
| JDFixer | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Manual install candidate. |
|
||||
| Plugin | Upstream | Status | Source/version | Verification notes |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| IntroSkip | [github](https://github.com/Loloppe/Intro-Skip), [beatmods zip](https://beatmods.com/cdn/mod/7d98ae6049251eb4e3226a5c8ac675b3.zip) | <span style="color:#d29922; font-weight:600">verified with warning</span> | BeatMods 4.0.8, version id 2570, zipHash `7d98ae6049251eb4e3226a5c8ac675b3`; replaces failed GitHub `Loloppe/Intro-Skip` tag `4.0.5`, asset `IntroSkip-v4.0.5+bs.1.37.2.zip` | IPA loaded Intro Skip 4.0.8 and the game reached `MainSystemInit`; the old `IntroSkip.UI.SettingsHost` `TypeLoadException` did not recur. Warning: manifest targets Beat Saber 1.40.11. |
|
||||
| FailButton | [github](https://github.com/qe201020335/FailButton) | <span style="color:#d29922; font-weight:600">verified with warning</span> | GitHub `qe201020335/FailButton` tag `v0.0.4`, asset `FailButton-0.0.4-bs1.39.0-b6415fb.zip` | IPA loaded FailButton 0.0.4 and the game reached `MainSystemInit`. Warning: manifest targets Beat Saber 1.39.0. |
|
||||
| EasyOffset | [github](https://github.com/Reezonate/EasyOffset) | <span style="color:#d29922; font-weight:600">verified with warning</span> | GitHub `Reezonate/EasyOffset` tag `v2.1.16`, asset `EasyOffset.dll`; GitHub release digest matched downloaded asset | IPA loaded EasyOffset 2.1.16, fetched remote config successfully, and the game reached `MainSystemInit`. Warning: manifest targets Beat Saber 1.42.0. |
|
||||
| GottaGoFast | [github](https://github.com/kinsi55/CS_BeatSaber_GottaGoFast) | <span style="color:#3fb950; font-weight:600">verified</span> | GitHub `kinsi55/CS_BeatSaber_GottaGoFast` tag `v0.2.5`, asset `GottaGoFast.dll`; GitHub release digest matched downloaded asset | IPA loaded Gotta Go Fast 0.2.5 and the game reached `MainSystemInit`. |
|
||||
| HitsoundTweaks | [github](https://github.com/GalaxyMaster2/HitsoundTweaks) | <span style="color:#f85149; font-weight:600">failed compatibility trial</span> | GitHub `GalaxyMaster2/HitsoundTweaks` tag `v1.1.9`, asset `HitsoundTweaks-1.1.9-bs1.40.3-4ad8461.zip`; GitHub release digest matched downloaded asset | IPA loaded HitsoundTweaks 1.1.9, but SiraUtil failed to apply the `AudioTimeSyncController_dspTimeOffset_Patch` affinity patch with `InvalidProgramException`. Removed from the live instance after the failed smoke. BeatMods verified search on 2026-06-29 found no `HitsoundTweaks` entry for Beat Saber 1.42.0, 1.42.1, 1.43.0, 1.44.0, or 1.44.1. |
|
||||
| KeepMyOverridesPls | [github](https://github.com/qqrz997/KeepMyOverridesPls) | <span style="color:#d29922; font-weight:600">verified with warning</span> | GitHub `qqrz997/KeepMyOverridesPls` tag `v1.1.3-b`, asset `KeepMyOverridesPls-1.1.3-bs1.40.6-487d417.zip`; GitHub release digest matched downloaded asset | IPA loaded KeepMyOverridesPls 1.1.3, installed its app installer, wrote config, and the game reached `MainSystemInit`. Warnings: manifest targets Beat Saber 1.40.6 and plugin has no start/exit methods. |
|
||||
| SoundReplacer | [github](https://github.com/Meivyn/SoundReplacer), [beatmods zip](https://beatmods.com/cdn/mod/7d7a869996e10249d1f85f95e060319b.zip) | <span style="color:#d29922; font-weight:600">verified with warning</span> | BeatMods 2.0.1, version id 2213, zipHash `7d7a869996e10249d1f85f95e060319b`; GitHub releases API returned no releases on 2026-06-29 | IPA loaded SoundReplacer 2.0.1, installed its app and menu installers, and the game reached `MainSystemInit`. Warning: manifest targets Beat Saber 1.29.4. |
|
||||
| KeyRemapper | [github](https://github.com/lyyQwQ/KeyRemapper) | <span style="color:#d29922; font-weight:600">verified with warning</span> | GitHub `lyyQwQ/KeyRemapper` tag `0.3.0`, asset `KeyRemapper-0.3.0-bs1.39.1-8e4c11a.zip`; GitHub release digest matched downloaded asset | IPA loaded KeyRemapper 0.3.0, initialized config, installed menu bindings, registered the Key Remapper button, and the game reached `MainSystemInit`. Warnings: manifest targets Beat Saber 1.39.1 and FPFC smoke logged dummy input manager because runtime was null. |
|
||||
| SquatToBegin | [github](https://github.com/kinsi55/BeatSaber_SquatToBegin) | <span style="color:#d29922; font-weight:600">verified with warning</span> | GitHub `kinsi55/BeatSaber_SquatToBegin` tag `v0.0.7`, asset `SquatToBegin.dll`; GitHub release digest matched downloaded asset | IPA loaded SquatToBegin 0.0.7 and the game reached `MainSystemInit`. Warnings: manifest reports Beat Saber 1.20.0 despite the GitHub release being labeled for 1.39.1+, and plugin has no start/exit methods. Verify squat gate behavior outside FPFC when practical. |
|
||||
| JDFixer | [github](https://github.com/zeph-yr/JDFixer), [pr](https://github.com/zeph-yr/JDFixer/pull/26) | <span style="color:#d29922; font-weight:600">installed; smoke blocked</span> | Local build from PR 26 commit `3fce6ce465911bdd5e8e00411bc4672c54a317f7`, asset `JDFixer.dll`; SHA-256 `16b7dad9906d838dab40ce48a9b304be4847f18e700ddd31f2293d1065f4529d` | IPA loaded JDFixer 7.4.0 and the prior `OnEnable` Harmony failure did not recur; JDFixer logged config/donate activity. The smoke did not reach the main menu because Steam platform initialization failed (`SteamAPI Init failed`, Steam likely not running). Replaces failed GitHub `zeph-yr/JDFixer` tag `v.7.4.0`, asset `JDFixer.dll`, SHA-256 `a83ae3f68921a9698616ecd89d08b7397a550c2464a7871b6c65506ce0c7d360`; that release loaded JDFixer 7.4.0, but `OnEnable` failed with a Harmony patching exception because `StandardLevelScenesTransitionSetupDataSOPatch::TargetMethod()` returned null. |
|
||||
|
||||
### Batch 6: UI and Song Browser
|
||||
|
||||
Purpose: restore song-list, menu, and visualization conveniences.
|
||||
|
||||
| Plugin | Status | Source/version | Verification notes |
|
||||
| --- | --- | --- | --- |
|
||||
| BetterSongList | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Verify song browser opens and filters work. |
|
||||
| HitScoreVisualizer | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Verify settings UI and in-song display. |
|
||||
| DiTails | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Verify details panel. |
|
||||
| HideTheLogo | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Verify menu loads. |
|
||||
| SongChartVisualizer | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Verify chart UI. |
|
||||
| WhyIsThereNoLeaderboard | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Source TBD from 1.40.8 install. |
|
||||
| Setlist | <span style="color:#8b949e; font-weight:600">todo</span> | local build or release | Requires BeatLeader signed in, PlaylistManager, and BeatSaberPlaylistsLib; verify `Setlist` log lines. |
|
||||
| Custom Campaigns | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Manual install candidate. |
|
||||
| Plugin | Upstream | Status | Source/version | Verification notes |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| BetterSongList | [github](https://github.com/kinsi55/BeatSaber_BetterSongList) | <span style="color:#d29922; font-weight:600">verified with warning</span> | GitHub `kinsi55/BeatSaber_BetterSongList` tag `v0.4.3`, asset `BetterSongList.dll`; GitHub release digest matched downloaded asset | IPA loaded BetterSongList 0.4.3 and the game reached `MainSystemInit`. Warning: manifest targets Beat Saber 1.42.0. |
|
||||
| HitScoreVisualizer | [github](https://github.com/ErisApps/HitScoreVisualizer) | <span style="color:#d29922; font-weight:600">verified with warning</span> | GitHub `ErisApps/HitScoreVisualizer` tag `3.7.3`, asset `HitScoreVisualizer-3.7.3-bs1.42.0-a565cbb.zip`; GitHub release digest matched downloaded asset | IPA loaded HitScoreVisualizer 3.7.3, installed app/menu installers, and the game reached `MainSystemInit`. Warning: manifest targets Beat Saber 1.42.0. |
|
||||
| DiTails | [github](https://github.com/Auros/DiTails) | <span style="color:#f85149; font-weight:600">failed compatibility trial</span> | GitHub `Auros/DiTails` tag `1.1.3`, asset `DiTails-v1.1.3-g1.42.0-271d394.zip`; GitHub release digest matched downloaded asset; BeatMods also verifies DiTails 1.1.3 for 1.44.1 as version id 2609, zipHash `437904f6db78a2ee928738d7d254a93f` | IPA loaded DiTails 1.1.3, but menu initialization failed in `DiTails.Managers.DetailContextManager.Initialize()` with `NullReferenceException`. Removed from the live instance after the failed smoke. |
|
||||
| HideTheLogo | [github](https://github.com/TheBlackParrot/HideTheLogo) | <span style="color:#d29922; font-weight:600">verified with warning</span> | GitHub `TheBlackParrot/HideTheLogo` tag `1.0.3`, asset `HideTheLogo-1.0.3-bs1.40.3-c968d91.zip` | IPA loaded HideTheLogo 1.0.3, logged `yeet`, and the game reached `MainSystemInit`. Warning: manifest targets Beat Saber 1.40.3. |
|
||||
| SongChartVisualizer | [github](https://github.com/NuggoDEV/SongChartVisualizer), [beatmods zip](https://beatmods.com/cdn/mod/5d3fc025fe098277667fc0846e1b8fe3.zip) | <span style="color:#d29922; font-weight:600">verified with warning</span> | BeatMods 1.1.11, version id 2249, zipHash `5d3fc025fe098277667fc0846e1b8fe3`; GitHub releases API returned no releases on 2026-06-29 | IPA loaded SongChartVisualizer 1.1.11, installed app/menu installers, and the game reached `MainSystemInit`. Warning: manifest targets Beat Saber 1.39.1. |
|
||||
| Setlist | | <span style="color:#f85149; font-weight:600">skipped after local-build trial</span> | Local build from `/home/pleb/ops/beatsaber/setlist` commit `14d21ad` with working tree modifications; first SHA-256 `01ecba3cfa697488faddf6eb8bfcc1aedff5d95f4b5a9f673f70b0ff150f5ab9`, rebuilt SHA-256 `57f07f5d99505ee35d45b3914484434fed98113c84cb94e74c6b362abd2216a1` after manifest dependency fix | First smoke ignored Setlist because `BeatLeader@^0.9.0` did not accept installed BeatLeader 0.10.0; after widening to `>=0.9.0`, IPA loaded Setlist 0.1.0 but only logged `No playlists loaded` and did not produce the expected `platformUserId`/playlist ownership lines. Removed from the live instance per skip instruction. |
|
||||
|
||||
### Batch 7: Cosmetic, Camera, and Lighting
|
||||
|
||||
Purpose: add visual and stream-facing mods after functional mods are stable.
|
||||
|
||||
| Plugin | Status | Source/version | Verification notes |
|
||||
| --- | --- | --- | --- |
|
||||
| AdBlocker | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Verify startup. |
|
||||
| HighlightBombs | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Verify in-song visuals. |
|
||||
| PitchBlack | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Verify lighting behavior. |
|
||||
| Dimmer | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Manual install candidate. |
|
||||
| ReeCamera | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Manual install candidate. |
|
||||
| ReeSabers | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Manual install candidate. |
|
||||
| Plugin | Upstream | Status | Source/version | Verification notes |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| AdBlocker | [github](https://github.com/JonnyVR1/AdBlocker), [beatmods zip](https://beatmods.com/cdn/mod/cd397e93b1a03f163534483462edf768.zip) | <span style="color:#d29922; font-weight:600">verified with warning</span> | BeatMods 1.0.5, version id 1872, zipHash `cd397e93b1a03f163534483462edf768`; GitHub releases API returned no releases on 2026-06-29 | IPA loaded AdBlocker 1.0.5, logged `AdBlocker initialized`, and the game reached `MainSystemInit`. Warning: manifest targets Beat Saber 1.34.2. |
|
||||
| HighlightBombs | [github](https://github.com/Meivyn/HighlightBombs), [beatmods zip](https://beatmods.com/cdn/mod/4bedaa80ce5dda8414fea7d914fb94ad.zip) | <span style="color:#d29922; font-weight:600">verified with warning</span> | BeatMods 1.0.3, version id 2066, zipHash `4bedaa80ce5dda8414fea7d914fb94ad`; GitHub latest release v1.0.1 is older than the BeatMods-verified package | IPA loaded HighlightBombs 1.0.3, installed its app installer, loaded QuickOutline material, and the game reached `MainSystemInit`. Warning: manifest targets Beat Saber 1.32.0. In-song bomb visuals not exercised in FPFC smoke. |
|
||||
| PitchBlack | [github](https://github.com/Loloppe/BeatSaber_PitchBlack), [beatmods zip](https://beatmods.com/cdn/mod/65656bf33b2a0b356c381132387ea7ea.zip) | <span style="color:#d29922; font-weight:600">verified with warning</span> | GitHub `Loloppe/BeatSaber_PitchBlack` tag `0.03`, asset `PitchBlack-v0.0.3-bs1.39.1.zip`; BeatMods version id 2233, zipHash `65656bf33b2a0b356c381132387ea7ea`; GitHub asset is byte-identical to the BeatMods CDN zip | IPA loaded PitchBlack 0.0.3, generated config, and the game reached `MainSystemInit`. Warning: manifest targets Beat Saber 1.39.1. In-song lighting behavior not exercised in FPFC smoke. |
|
||||
| Dimmer | | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | Manual install candidate. |
|
||||
| ReeCamera | [github](https://github.com/Reezonate/ReeCamera) | <span style="color:#d29922; font-weight:600">verified with warning</span> | GitHub `Reezonate/ReeCamera` tag `v0.0.5`, asset `ReeCamera.1.42.0.zip`; GitHub release digest matched downloaded asset | IPA loaded ReeCamera 0.0.5, logged Spout load success, installed app/menu installers, and the game reached `MainSystemInit`. Warnings: manifest targets Beat Saber 1.42.0; first launch logged missing `UserData/ReeCamera.json` until the mod creates it on exit per upstream docs. Archive also replaced bundled `Plugins/CameraUtils.dll`. Camera presets not exercised in FPFC smoke. |
|
||||
|
||||
### Batch 8: Paid or Closed-Source Plugins
|
||||
|
||||
Purpose: restore private plugin set only after public/dependency-heavy mods are
|
||||
known good.
|
||||
|
||||
| Plugin | Status | Source/version | Verification notes |
|
||||
| --- | --- | --- | --- |
|
||||
| BeatSaberPlus_Chat | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify startup and module UI. |
|
||||
| BeatSaberPlus_ChatEmoteRain | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify startup. |
|
||||
| BeatSaberPlus_ChatIntegrations | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify startup. |
|
||||
| BeatSaberPlus_ChatRequest | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify startup. |
|
||||
| BeatSaberPlus_GameTweaker | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify startup. |
|
||||
| BeatSaberPlus_MenuMusic | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify startup. |
|
||||
| BeatSaberPlus_Multiplayer | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify startup. |
|
||||
| BeatSaberPlus_NoteTweaker | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify startup. |
|
||||
| BeatSaberPlus_SongChartVisualizer | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify startup. |
|
||||
| BeatSaberPlus_SongOverlay | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify startup. |
|
||||
| NalulunaMenu | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify startup and menu. |
|
||||
| NalulunaCounters | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify in-song counters. |
|
||||
| NalulunaLevelDetail | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify song detail panel. |
|
||||
| NalulunaSliceVisualizer | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify in-song visuals. |
|
||||
| NalulunaSongPreview | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify song preview. |
|
||||
| NalulunaMissIndicator | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify in-song visuals. |
|
||||
| NalulunaEnergy | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify in-song HUD. |
|
||||
| NalulunaFps | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify overlay. |
|
||||
| NalulunaPPCoin | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify startup. |
|
||||
| NalulunaRewinder | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify startup. |
|
||||
| NalulunaAvatars | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify avatar load. |
|
||||
| NalulunaShaders | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify dependency for Naluluna visuals. |
|
||||
| NalulunaSkybox | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify skybox sample manifest and skybox load. |
|
||||
| NalulunaUtils | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify dependency load. |
|
||||
| Plugin | Upstream | Status | Source/version | Verification notes |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| ReeSabers | | <span style="color:#8b949e; font-weight:600">todo</span> | paid/private | Verify saber visuals in VR when practical. |
|
||||
| BeatSaberPlus_Chat | | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify startup and module UI. |
|
||||
| BeatSaberPlus_ChatEmoteRain | | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify startup. |
|
||||
| BeatSaberPlus_ChatIntegrations | | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify startup. |
|
||||
| BeatSaberPlus_ChatRequest | | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify startup. |
|
||||
| BeatSaberPlus_GameTweaker | | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify startup. |
|
||||
| BeatSaberPlus_MenuMusic | | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify startup. |
|
||||
| BeatSaberPlus_Multiplayer | | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify startup. |
|
||||
| BeatSaberPlus_NoteTweaker | | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify startup. |
|
||||
| BeatSaberPlus_SongChartVisualizer | | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify startup. |
|
||||
| BeatSaberPlus_SongOverlay | | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify startup. |
|
||||
| NalulunaMenu | | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify startup and menu. |
|
||||
| NalulunaCounters | | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify in-song counters. |
|
||||
| NalulunaLevelDetail | | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify song detail panel. |
|
||||
| NalulunaSliceVisualizer | | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify in-song visuals. |
|
||||
| NalulunaSongPreview | | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify song preview. |
|
||||
| NalulunaMissIndicator | | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify in-song visuals. |
|
||||
| NalulunaEnergy | | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify in-song HUD. |
|
||||
| NalulunaFps | | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify overlay. |
|
||||
| NalulunaPPCoin | | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify startup. |
|
||||
| NalulunaRewinder | | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify startup. |
|
||||
| NalulunaAvatars | | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify avatar load. |
|
||||
| NalulunaShaders | | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify dependency for Naluluna visuals. |
|
||||
| NalulunaSkybox | | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify skybox sample manifest and skybox load. |
|
||||
| NalulunaUtils | | <span style="color:#8b949e; font-weight:600">todo</span> | local/private | Verify dependency load. |
|
||||
|
||||
## Shared Libraries and Dependency Packages
|
||||
|
||||
These should be installed because selected plugins require them, not because they
|
||||
are user-facing features.
|
||||
|
||||
| Package | Status | Required by | Source/version | Verification notes |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| AssetBundleLoadingTools | <span style="color:#8b949e; font-weight:600">todo</span> | Vivify | TBD | Usually `Libs/`. |
|
||||
| BS Utils | <span style="color:#3fb950; font-weight:600">verified</span> | BeatSaverDownloader, BeatLeader | BeatMods 1.14.3, version id 2563, zipHash `918d13ac2821a3a17b2819f8861453e9` | IPA loaded BS Utils 1.14.3. |
|
||||
| CameraUtils | <span style="color:#8b949e; font-weight:600">todo</span> | Vivify | TBD | Verify no missing assembly errors. |
|
||||
| ImageSharp | <span style="color:#3fb950; font-weight:600">verified</span> | BeatSaberPlaylistsLib | BeatMods 2.0.0, version id 1428, zipHash `b642fec88b0f84a0643ebd401d08da35` | IPA loaded ImageSharp 2.0.0. |
|
||||
| Ini Parser | <span style="color:#3fb950; font-weight:600">verified</span> | BS Utils | BeatMods 2.5.9, version id 1352, zipHash `5df74ad1c6b120fecdc615dd55f15b88` | IPA loaded INI Parser 2.5.9. |
|
||||
| LookupID | <span style="color:#8b949e; font-weight:600">todo</span> | Chroma | TBD | Verify no missing assembly errors. |
|
||||
| OpenVR API | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | TBD | Include only if a 1.44.1 dependency needs it. |
|
||||
| protobuf-net | <span style="color:#8b949e; font-weight:600">todo</span> | SongDetailsCache | TBD | Usually `Libs/`. |
|
||||
| SongDetailsCache | <span style="color:#8b949e; font-weight:600">todo</span> | BetterSongList, SongRankedBadge | TBD | Verify cache startup. |
|
||||
| System.IO.Compression | <span style="color:#d29922; font-weight:600">verified with warning</span> | BeatSaberPlaylistsLib, System.IO.Compression.FileSystem | BeatMods 4.6.57, version id 1763, zipHash `a4e9e26f61967e56168e08eecb01ab88` | IPA loaded System.IO.Compression 4.6.57; logged a duplicate-library notice because the game also ships this assembly. |
|
||||
| System.IO.Compression.FileSystem | <span style="color:#3fb950; font-weight:600">verified</span> | BeatSaverDownloader | BeatMods 4.7.3056, version id 1762, zipHash `e19f6fd395d54de7bfcbbbe3084dea28` | IPA loaded System.IO.Compression.FileSystem 4.7.3056. |
|
||||
| Dynamic Bone | <span style="color:#dbab79; font-weight:600">defer</span> | TBD | TBD | Include only if a selected 1.44.1 mod requires it. |
|
||||
| Final IK | <span style="color:#dbab79; font-weight:600">defer</span> | TBD | TBD | Include only if a selected 1.44.1 mod requires it. |
|
||||
| Package | Upstream | Status | Required by | Source/version | Verification notes |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| AssetBundleLoadingTools | [github](https://github.com/nicoco007/AssetBundleLoadingTools) | <span style="color:#8b949e; font-weight:600">todo</span> | Vivify | TBD | Usually `Libs/`. |
|
||||
| BS Utils | [beatmods zip](https://beatmods.com/cdn/mod/918d13ac2821a3a17b2819f8861453e9.zip) | <span style="color:#3fb950; font-weight:600">verified</span> | BeatSaverDownloader, BeatLeader | BeatMods 1.14.3, version id 2563, zipHash `918d13ac2821a3a17b2819f8861453e9` | IPA loaded BS Utils 1.14.3. |
|
||||
| CameraUtils | [github](https://github.com/Reezonate/CameraUtils) | <span style="color:#8b949e; font-weight:600">todo</span> | Vivify | TBD | Verify no missing assembly errors. |
|
||||
| ImageSharp | [beatmods zip](https://beatmods.com/cdn/mod/b642fec88b0f84a0643ebd401d08da35.zip) | <span style="color:#3fb950; font-weight:600">verified</span> | BeatSaberPlaylistsLib | BeatMods 2.0.0, version id 1428, zipHash `b642fec88b0f84a0643ebd401d08da35` | IPA loaded ImageSharp 2.0.0. |
|
||||
| Ini Parser | [beatmods zip](https://beatmods.com/cdn/mod/5df74ad1c6b120fecdc615dd55f15b88.zip) | <span style="color:#3fb950; font-weight:600">verified</span> | BS Utils | BeatMods 2.5.9, version id 1352, zipHash `5df74ad1c6b120fecdc615dd55f15b88` | IPA loaded INI Parser 2.5.9. |
|
||||
| LookupID | [github](https://github.com/Aeroluna/Heck) | <span style="color:#8b949e; font-weight:600">todo</span> | Chroma | TBD | Verify no missing assembly errors. |
|
||||
| OpenVR API | [github](https://github.com/nicoco007/BeatSaber-OpenVR-API) | <span style="color:#8b949e; font-weight:600">todo</span> | TBD | TBD | Include only if a 1.44.1 dependency needs it. |
|
||||
| protobuf-net | [beatmods zip](https://beatmods.com/cdn/mod/1f55ae4b80b747b5f03fa18337ead864.zip) | <span style="color:#d29922; font-weight:600">verified with warning</span> | SongDetailsCache | BeatMods 3.0.102, version id 958, zipHash `1f55ae4b80b747b5f03fa18337ead864` | IPA loaded protobuf-net 3.0.102. Warning: manifest targets Beat Saber 1.13.2. |
|
||||
| SongDetailsCache | [github](https://github.com/kinsi55/BeatSaber_SongDetails) | <span style="color:#d29922; font-weight:600">verified with warning</span> | BetterSongList, SongRankedBadge | GitHub `kinsi55/BeatSaber_SongDetails` tag `v1.4.0`, asset `SongDetailsCache.BS.Lib.zip`; BeatMods version id 2226, zipHash `e1167b64cd3eff7e3651ec2dbbe50d81` | IPA loaded SongDetailsCache 1.4.0 and SongRankedBadge used it to load song details. Warning: manifest targets Beat Saber 1.13.2. |
|
||||
| System.IO.Compression | [beatmods zip](https://beatmods.com/cdn/mod/a4e9e26f61967e56168e08eecb01ab88.zip) | <span style="color:#d29922; font-weight:600">verified with warning</span> | BeatSaberPlaylistsLib, System.IO.Compression.FileSystem | BeatMods 4.6.57, version id 1763, zipHash `a4e9e26f61967e56168e08eecb01ab88` | IPA loaded System.IO.Compression 4.6.57; logged a duplicate-library notice because the game also ships this assembly. |
|
||||
| System.IO.Compression.FileSystem | [beatmods zip](https://beatmods.com/cdn/mod/e19f6fd395d54de7bfcbbbe3084dea28.zip) | <span style="color:#3fb950; font-weight:600">verified</span> | BeatSaverDownloader | BeatMods 4.7.3056, version id 1762, zipHash `e19f6fd395d54de7bfcbbbe3084dea28` | IPA loaded System.IO.Compression.FileSystem 4.7.3056. |
|
||||
| Dynamic Bone | | <span style="color:#dbab79; font-weight:600">defer</span> | TBD | TBD | Include only if a selected 1.44.1 mod requires it. |
|
||||
| Final IK | | <span style="color:#dbab79; font-weight:600">defer</span> | TBD | TBD | Include only if a selected 1.44.1 mod requires it. |
|
||||
|
||||
## Omitted Plugins
|
||||
|
||||
Record plugins skipped for this 1.44.1 pass. This is not a fix list for today.
|
||||
|
||||
| Plugin | Reason omitted | Evidence/log note | Follow-up |
|
||||
| --- | --- | --- | --- |
|
||||
| Heck | No BeatMods verified 1.44.1 entry found on 2026-06-28. | Not installed. | Revisit only with a compatible source. |
|
||||
| Chroma | No BeatMods verified 1.44.1 entry found on 2026-06-28. | Not installed. | Revisit after Heck is available. |
|
||||
| NoodleExtensions | No BeatMods verified 1.44.1 entry found on 2026-06-28. | Not installed. | Revisit after Heck is available. |
|
||||
| Vivify | No BeatMods verified 1.44.1 entry found on 2026-06-28. | Not installed. | Revisit after Heck is available. |
|
||||
| PlaylistManager | No BeatMods verified 1.44.1 entry found on 2026-06-28. | Not installed. | Revisit only with a compatible source. |
|
||||
| BeatSaverVoting | No BeatMods verified 1.44.1 entry found on 2026-06-28. | Not installed. | Revisit only with a compatible source. |
|
||||
| Plugin | Upstream | Reason omitted | Evidence/log note | Follow-up |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Heck | [github](https://github.com/Aeroluna/Heck) | No BeatMods verified 1.44.1 entry found on 2026-06-28. | Not installed. | Revisit only with a compatible source. |
|
||||
| Chroma | [github](https://github.com/Aeroluna/Heck) | No BeatMods verified 1.44.1 entry found on 2026-06-28. | Not installed. | Revisit after Heck is available. |
|
||||
| NoodleExtensions | [github](https://github.com/Aeroluna/Heck) | No BeatMods verified 1.44.1 entry found on 2026-06-28. | Not installed. | Revisit after Heck is available. |
|
||||
| Vivify | [github](https://github.com/Aeroluna/Vivify) | No BeatMods verified 1.44.1 entry found on 2026-06-28. | Not installed. | Revisit after Heck is available. |
|
||||
| PlaylistManager | [github](https://github.com/rithik-b/PlaylistManager) | No BeatMods verified 1.44.1 entry found on 2026-06-28; latest blessed BeatMods entry found was 1.7.3 for Beat Saber 1.40.8. | Installed for a compatibility trial, then removed after smoketest failure. | Fails on 1.44.1 menu setup with `IPlatformUserModel` / `PlatformUserModel` type resolution; needs a newer compatible build. |
|
||||
| BeatSaverVoting | [beatmods zip](https://beatmods.com/cdn/mod/bc002ed1a43e2c6d3a10d0750e5d94b4.zip) | No BeatMods verified 1.44.1 entry found on 2026-06-28; latest blessed BeatMods entry found was 2.4.6 for Beat Saber 1.40.8. | Installed for a compatibility trial, then removed after smoketest failure. | Fails on 1.44.1 menu event handling with `IPlatformUserModel` / `PlatformUserModel` type resolution; needs a newer compatible build. |
|
||||
|
||||
## Batch Results
|
||||
|
||||
@@ -295,8 +292,8 @@ Record plugins skipped for this 1.44.1 pass. This is not a fix list for today.
|
||||
| 1 | 2026-06-28 | <span style="color:#3fb950; font-weight:600">verified</span> | BSML, SiraUtil, and SongCore loaded; SongCore loaded 2 custom songs. Warnings: older target game-version metadata, missing Windows fonts, missing `CustomWIPLevels/Cache/Info.dat`, and one built-in `Magic.wav` duration approximation. | Treat as acceptable bootstrap baseline. |
|
||||
| 2 | 2026-06-28 | <span style="color:#3fb950; font-weight:600">verified</span> | CustomJSONData loaded; startup reached main initialization; SongCore still loaded 2 custom songs. Warnings: older target game-version metadata, missing Windows fonts, and missing `CustomWIPLevels/Cache/Info.dat`. | Continue to downloader/playlist batch. |
|
||||
| 3 | 2026-06-28 | <span style="color:#d29922; font-weight:600">verified with warning</span> | BeatSaverDownloader, BeatSaverUpdater, BeatSaberPlaylistsLib, BeatSaverSharp, ScoreSaberSharp, BS Utils, Ini Parser, ImageSharp, and System.IO.Compression packages loaded; startup reached main initialization; SongCore still loaded 2 custom songs. Warning: BeatSaverDownloader probed for missing `BetterSongList.dll` with IPA library-loader `CRITICAL` lines, then continued and started its internal webserver. | Stop here per request; consider installing BetterSongList before deeper downloader UI testing. |
|
||||
| 4 | | <span style="color:#8b949e; font-weight:600">todo</span> | | |
|
||||
| 4 | 2026-06-29 | <span style="color:#d29922; font-weight:600">verified with warning</span> | Installed and smoke-tested ScoreSaber, BeatLeader, BeatLeader-bundled LeaderboardCore, SongRankedBadge, SongDetailsCache, and protobuf-net. AccSaber Reloaded was trialed from the user-provided GitHub release page and removed after a compatibility failure. | Warnings: ScoreSaber and BeatLeader assets target 1.42.x/1.44.0 ranges rather than explicit 1.44.1; BeatLeader-bundled LeaderboardCore logged a Harmony patch error; AccSaber 1.40.8 asset failed with a `TypeLoadException`; existing BeatSaverDownloader BetterSongList probe still logs `CRITICAL` library-loader lines. |
|
||||
| 5 | | <span style="color:#8b949e; font-weight:600">todo</span> | | |
|
||||
| 6 | | <span style="color:#8b949e; font-weight:600">todo</span> | | |
|
||||
| 7 | | <span style="color:#8b949e; font-weight:600">todo</span> | | |
|
||||
| 7 | 2026-06-29 | <span style="color:#d29922; font-weight:600">verified with warning</span> | Installed and smoke-tested AdBlocker 1.0.5, HighlightBombs 1.0.3, PitchBlack 0.0.3, and ReeCamera 0.0.5. All four loaded and the game reached `MainSystemInit`. HighlightBombs installed its app installer and loaded QuickOutline material. ReeCamera logged Spout load success and installed app/menu installers. | Warnings: all four manifests target older Beat Saber versions (1.34.2, 1.32.0, 1.39.1, 1.42.0). AdBlocker and PitchBlack used BeatMods CDN or byte-identical GitHub assets because JonnyVR1/AdBlocker exposes no GitHub releases and HighlightBombs BeatMods 1.0.3 is newer than GitHub v1.0.1. ReeCamera first launch logged missing `UserData/ReeCamera.json` until mod creates it on exit; archive replaced bundled `CameraUtils.dll`. In-song bomb/lighting/camera visuals were not exercised in FPFC smoke. Pre-existing LeaderboardCore and PlaylistManager errors still appear in the log. |
|
||||
| 8 | | <span style="color:#8b949e; font-weight:600">todo</span> | | |
|
||||
|
||||
@@ -46,6 +46,69 @@ sha256 = "30555c77485d2837bd294608fe4e23c34415b89413997be67a6150318090b216"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "BeatMods verified CustomJSONData 2.6.8+1.40.0 for Beat Saber 1.44.1 as version id 2327, zipHash fed31638bbb678580ef760ec83cfd486. GitHub asset is byte-identical to the BeatMods CDN zip."
|
||||
|
||||
[[plugins]]
|
||||
id = "heck"
|
||||
repo = "Aeroluna/Heck"
|
||||
tag = "v2026-05-02"
|
||||
asset = "Heck-1.8.3+1.42.1-bs1.42.1-3ebc6a2.zip"
|
||||
sha256 = "d6c6a2b7e54285c8e7ee0515546f8cec274033afaa11324164803d003d9afab4"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "No BeatMods verified 1.44.1 entry was returned on 2026-06-28. Use latest upstream GitHub Heck asset targeting the highest available Beat Saber version, bs1.42.1; GitHub release digest matched the downloaded asset."
|
||||
|
||||
[[plugins]]
|
||||
id = "chroma"
|
||||
repo = "Aeroluna/Heck"
|
||||
tag = "v2026-02-23"
|
||||
asset = "Chroma-2.9.22+1.42.1-bs1.42.1-b38d924.zip"
|
||||
sha256 = "5438d039336ec91573818b7dbd8d5fce30454d16a66f3e70d6eedf5bff6c32bf"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "No BeatMods verified 1.44.1 entry was returned on 2026-06-28. Use latest upstream GitHub Chroma asset targeting the highest available Beat Saber version, bs1.42.1; GitHub release digest matched the downloaded asset."
|
||||
|
||||
[[plugins]]
|
||||
id = "lookupid"
|
||||
repo = "Aeroluna/Heck"
|
||||
tag = "chroma-v2.5.10"
|
||||
asset = "LookupID-1.0.1.zip"
|
||||
sha256 = "5fc838c9bf3693a8e3728056d95c07c5b0e13f944c1f9f49607f701b58e3d05d"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "Required by Chroma. BeatMods verified LookupID 1.0.1 for Beat Saber 1.44.1 as version id 1527, zipHash 5f5655b91193602c0311b8b6d1b4c71c. GitHub exposes the same version as an upstream release asset."
|
||||
|
||||
[[plugins]]
|
||||
id = "noodleextensions"
|
||||
repo = "Aeroluna/Heck"
|
||||
tag = "v2026-05-09"
|
||||
asset = "NoodleExtensions-1.7.21+1.42.1-bs1.42.1-3bbcaf6.zip"
|
||||
sha256 = "116d5a56c52faf652cecee2dd0b9a02c1798d893fb5adef4145a1fa555ef727f"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "No BeatMods verified 1.44.1 entry was returned on 2026-06-28. Use latest upstream GitHub Noodle Extensions asset targeting the highest available Beat Saber version, bs1.42.1; GitHub release digest matched the downloaded asset."
|
||||
|
||||
[[plugins]]
|
||||
id = "camerautils"
|
||||
repo = "Reezonate/CameraUtils"
|
||||
tag = "beatmods-1.0.8"
|
||||
asset = "CameraUtils-1.0.8.zip"
|
||||
sha256 = "7066ec311dc599e1d654d70557abc6c0809b72b5f4d695c8cfb85bb0bf2e2366"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "Required by Vivify. BeatMods verified CameraUtils 1.0.8 for Beat Saber 1.44.1 as version id 2576, zipHash 38507e3a9ff8486c75b002cc47226f43. Upstream GitHub latest release is 1.0.7, so use the newer BeatMods CDN artifact."
|
||||
|
||||
[[plugins]]
|
||||
id = "assetbundleloadingtools"
|
||||
repo = "nicoco007/AssetBundleLoadingTools"
|
||||
tag = "v1.1.13"
|
||||
asset = "AssetBundleLoadingTools-v1.1.13+bs.1.41.1.zip"
|
||||
sha256 = "356b1aa4722ecca6f13199ad378429ece9873c365d9cf013e095fd5c0757a127"
|
||||
install_strategy = "root-zip"
|
||||
reason = "Required by Vivify. BeatMods verified AssetBundleLoadingTools 1.1.13 for Beat Saber 1.44.1 as version id 2590, zipHash a381a964cb69d26be8348edf04fabbc7. GitHub asset is the same version and its release digest matched the downloaded asset."
|
||||
|
||||
[[plugins]]
|
||||
id = "vivify"
|
||||
repo = "Aeroluna/Vivify"
|
||||
tag = "v1.1.0"
|
||||
asset = "Vivify-1.1.0+1.42.1-bs1.42.1-f83aa3c.zip"
|
||||
sha256 = "0e8799a7243496925aa527bc4ca7d3bcab770f4e828bbceeed066539f90a0902"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "No BeatMods verified 1.44.1 entry was returned on 2026-06-28. Use latest upstream GitHub Vivify asset targeting the highest available Beat Saber version, bs1.42.1; GitHub release digest matched the downloaded asset."
|
||||
|
||||
[[plugins]]
|
||||
id = "iniparser"
|
||||
repo = "rickyah/ini-parser"
|
||||
@@ -115,6 +178,51 @@ sha256 = "7f30be996f8f0e997f2d848e34de1d03ef6dc744ffc32d3fe505881ca22c6cd3"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "BeatMods verified ScoreSaberSharp 0.1.0 for Beat Saber 1.44.1 as version id 445, zipHash 8713168c598577ee7c73fa3cf0e26f5c. BeatMods lists scoresaber.com rather than a GitHub release source, so this remains a BeatMods CDN fallback."
|
||||
|
||||
[[plugins]]
|
||||
id = "scoresaber"
|
||||
repo = "ScoreSaber/pc-mod"
|
||||
tag = "v3.3.27"
|
||||
asset = "ScoreSaber-v3.3.27-bs1.42.0-to-1.44.0-9b4cfcf.zip"
|
||||
sha256 = "8ad509ab38353dfb4d92d127ba4e40917552f700580a7384509cafa70e62c096"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "User-provided GitHub releases URL https://github.com/ScoreSaber/pc-mod/releases. Latest non-draft, non-prerelease release v3.3.27 includes this highest compatible asset, labeled for Beat Saber 1.42.0 through 1.44.0; GitHub release digest matched the downloaded asset."
|
||||
|
||||
[[plugins]]
|
||||
id = "protobuf-net"
|
||||
repo = "protobuf-net/protobuf-net"
|
||||
tag = "beatmods-3.0.102"
|
||||
asset = "protobuf-net-3.0.102.zip"
|
||||
sha256 = "8bf4a361038172eab2c0c2e6737773d6c9a47fc6504a97304a35f697d5166414"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "Required by SongDetailsCache. BeatMods verified protobuf-net 3.0.102 for Beat Saber 1.44.1 as version id 958, zipHash 1f55ae4b80b747b5f03fa18337ead864. Use BeatMods CDN as a framework/library dependency payload."
|
||||
|
||||
[[plugins]]
|
||||
id = "songdetailscache"
|
||||
repo = "kinsi55/BeatSaber_SongDetails"
|
||||
tag = "v1.4.0"
|
||||
asset = "SongDetailsCache.BS.Lib.zip"
|
||||
sha256 = "6ccc816dd40752b46d5e7e1cfb41e5af9d915d1a032be51ce1e52fb154daa28c"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "Required by SongRankedBadge. BeatMods verified SongDetailsCache 1.4.0 for Beat Saber 1.44.1 as version id 2226, zipHash e1167b64cd3eff7e3651ec2dbbe50d81. GitHub asset is the same version selected from kinsi55/BeatSaber_SongDetails v1.4.0."
|
||||
|
||||
[[plugins]]
|
||||
id = "songrankedbadge"
|
||||
repo = "qe201020335/SongRankedBadge"
|
||||
tag = "v1.0.6"
|
||||
asset = "SongRankedBadge-1.0.6-bs1.40.0-88ee233.zip"
|
||||
sha256 = "6028f2392dd5e228f477837a7e32d95f0ca8af4f1b697b17c842ea62c050edff"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "BeatMods verified SongRankedBadge 1.0.6 for Beat Saber 1.44.1 as version id 2267, zipHash c6944b8a4b00b0c0bb1d44f273b3bb18. GitHub release asset v1.0.6 was used."
|
||||
|
||||
[[plugins]]
|
||||
id = "beatleader"
|
||||
repo = "BeatLeader/beatleader-mod"
|
||||
tag = "v0.10.0"
|
||||
asset = "BeatLeader-0.10.0-bs1.42+.zip"
|
||||
sha256 = "0b7c2adcf4db9806cdc765164788a9394bca2ee12179fad1b8ed255da1b7104e"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "User-provided GitHub releases URL https://github.com/BeatLeader/beatleader-mod/releases. Latest non-draft, non-prerelease release v0.10.0 includes this highest compatible asset, labeled for Beat Saber 1.42+; GitHub release digest matched the downloaded asset. The archive bundles Plugins/LeaderboardCore.dll, so the standalone LeaderboardCore lock entry is intentionally omitted."
|
||||
|
||||
[[plugins]]
|
||||
id = "beatsaverdownloader"
|
||||
repo = "Top-Cat/BeatSaverDownloader"
|
||||
@@ -124,6 +232,15 @@ sha256 = "4108b11eae11d09f8c4e838dde1dc36108f1f3fd4fff31b951e7c551fa59f5f1"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "BeatMods verified BeatSaverDownloader 6.0.7 for Beat Saber 1.44.1 as version id 2217, zipHash a740c6e68a9b5d1dfda3cc8e81f7cf06. Upstream GitHub exposes no release assets through the releases API, so this remains a BeatMods CDN fallback."
|
||||
|
||||
[[plugins]]
|
||||
id = "playlistmanager"
|
||||
repo = "rithik-b/PlaylistManager"
|
||||
tag = "pr-82-localbuild-da1ad17"
|
||||
asset = "PlaylistManager-1.7.4-bs1.44.0-da1ad17.zip"
|
||||
sha256 = "57b449e614db1d5214cd3a88000d52f5a989c8521390a9f4819b1c62f20f16fa"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "Local build from .state/build/playlistmanager-pr82-skilltest, artifact PlaylistManager/bin/Release/net48/zip/PlaylistManager-1.7.4-bs1.44.0-da1ad17.zip. Use this PR82 build instead of the failed BeatMods 1.7.3 compatibility trial for Beat Saber 1.44.1."
|
||||
|
||||
[[plugins]]
|
||||
id = "beatsaverupdater"
|
||||
repo = "ibillingsley/BeatSaverUpdater"
|
||||
@@ -132,3 +249,156 @@ asset = "BeatSaverUpdater-1.2.11-bs1.39.1-3698f98.zip"
|
||||
sha256 = "6237da05cbc044e99211cb0e569c917fe61bb0da25e42c61a6d438371548ebba"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "BeatMods verified BeatSaverUpdater 1.2.11 for Beat Saber 1.44.1 as version id 2352, zipHash d9ea8dd0cbaac66cbb02fa59a548e42b. GitHub asset is byte-identical to the BeatMods CDN zip."
|
||||
|
||||
[[plugins]]
|
||||
id = "failbutton"
|
||||
repo = "qe201020335/FailButton"
|
||||
tag = "v0.0.4"
|
||||
asset = "FailButton-0.0.4-bs1.39.0-b6415fb.zip"
|
||||
sha256 = "bb163170f400191592af1689e7b7557ec82e45e36de36e6eaaae16b4273e595b"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "User-provided GitHub repository URL https://github.com/qe201020335/FailButton. Latest non-draft, non-prerelease release v0.0.4 exposes this asset; no newer 1.44.x-specific asset was available."
|
||||
|
||||
[[plugins]]
|
||||
id = "easyoffset"
|
||||
repo = "Reezonate/EasyOffset"
|
||||
tag = "v2.1.16"
|
||||
asset = "EasyOffset.dll"
|
||||
sha256 = "6b9c63e5a4ae0ebc103fc11f442917b8fae12d62369faed1aca7470899a2bbbf"
|
||||
install_strategy = "dll-to-plugins"
|
||||
reason = "User-provided GitHub repository URL https://github.com/Reezonate/EasyOffset. Latest non-draft, non-prerelease release v2.1.16 exposes this direct DLL asset; GitHub release digest matched the downloaded asset."
|
||||
|
||||
[[plugins]]
|
||||
id = "gottagofast"
|
||||
repo = "kinsi55/CS_BeatSaber_GottaGoFast"
|
||||
tag = "v0.2.5"
|
||||
asset = "GottaGoFast.dll"
|
||||
sha256 = "c9cd0e29dd7540027cdd3b9138335d81f93d69daeb8460d2fdfef31199e0e44e"
|
||||
install_strategy = "dll-to-plugins"
|
||||
reason = "User-provided GitHub repository URL https://github.com/kinsi55/CS_BeatSaber_GottaGoFast. Latest non-draft, non-prerelease release v0.2.5 is labeled for Beat Saber 1.42.0+ and exposes this direct DLL asset; GitHub release digest matched the downloaded asset."
|
||||
|
||||
[[plugins]]
|
||||
id = "keepmyoverridespls"
|
||||
repo = "qqrz997/KeepMyOverridesPls"
|
||||
tag = "v1.1.3-b"
|
||||
asset = "KeepMyOverridesPls-1.1.3-bs1.40.6-487d417.zip"
|
||||
sha256 = "4c2da7349778eba98eb02ba518a1a056a784b719b974055c877aa6ce3e735fbc"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "User-provided GitHub repository URL https://github.com/qqrz997/KeepMyOverridesPls. Latest non-draft, non-prerelease release v1.1.3-b exposes this asset; GitHub release digest matched the downloaded asset."
|
||||
|
||||
[[plugins]]
|
||||
id = "keyremapper"
|
||||
repo = "lyyQwQ/KeyRemapper"
|
||||
tag = "0.3.0"
|
||||
asset = "KeyRemapper-0.3.0-bs1.39.1-8e4c11a.zip"
|
||||
sha256 = "962e3f6e18bebdf101e6575fa7b8b7e0d92179bab4f0825d122078f1842f7380"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "User-provided GitHub releases URL https://github.com/lyyQwQ/KeyRemapper/releases. Latest non-draft, non-prerelease release 0.3.0 exposes this asset; GitHub release digest matched the downloaded asset. Release notes label compatibility as Beat Saber 1.39.1 with BSIPA/BSML/SiraUtil dependencies."
|
||||
|
||||
[[plugins]]
|
||||
id = "squattobegin"
|
||||
repo = "kinsi55/BeatSaber_SquatToBegin"
|
||||
tag = "v0.0.7"
|
||||
asset = "SquatToBegin.dll"
|
||||
sha256 = "8426a64f6a3224b8cd79b9ee86347727a5a43c40a0fda116bf9c613f145fc18e"
|
||||
install_strategy = "dll-to-plugins"
|
||||
reason = "User-provided GitHub releases URL https://github.com/kinsi55/BeatSaber_SquatToBegin/releases. Latest non-draft, non-prerelease release v0.0.7 is labeled for Beat Saber 1.39.1+ and exposes this direct DLL asset; GitHub release digest matched the downloaded asset."
|
||||
|
||||
[[plugins]]
|
||||
id = "introskip"
|
||||
repo = "Loloppe/Intro-Skip"
|
||||
tag = "beatmods-4.0.8"
|
||||
asset = "IntroSkip-4.0.8.zip"
|
||||
sha256 = "319bce38f69661a3b0997071b85d57e5b4295b9699ac5432877b0fed72aa48b9"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "BeatMods verified IntroSkip 4.0.8 for Beat Saber 1.44.1 as version id 2570, zipHash 7d98ae6049251eb4e3226a5c8ac675b3. Use BeatMods CDN after the older GitHub 4.0.5 asset failed compatibility on this instance."
|
||||
|
||||
[[plugins]]
|
||||
id = "soundreplacer"
|
||||
repo = "Meivyn/SoundReplacer"
|
||||
tag = "beatmods-2.0.1"
|
||||
asset = "SoundReplacer-2.0.1.zip"
|
||||
sha256 = "c14cc99cb0bd4ea4c3ab47345692489cbdfed8b2b577925200b1a8f6f4f93512"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "BeatMods verified SoundReplacer 2.0.1 for Beat Saber 1.44.1 as version id 2213, zipHash 7d7a869996e10249d1f85f95e060319b. GitHub releases API returned no releases on 2026-06-29, so use the BeatMods CDN artifact."
|
||||
|
||||
[[plugins]]
|
||||
id = "bettersonglist"
|
||||
repo = "kinsi55/BeatSaber_BetterSongList"
|
||||
tag = "v0.4.3"
|
||||
asset = "BetterSongList.dll"
|
||||
sha256 = "50993315f41e5ca8e83ce7ded6be7780cfbe62ce248023849bd898068a4e25c1"
|
||||
install_strategy = "dll-to-plugins"
|
||||
reason = "User-provided GitHub repository URL https://github.com/kinsi55/BeatSaber_BetterSongList. Latest non-draft, non-prerelease release v0.4.3 exposes this direct DLL asset; GitHub release digest matched the downloaded asset."
|
||||
|
||||
[[plugins]]
|
||||
id = "hitscorevisualizer"
|
||||
repo = "ErisApps/HitScoreVisualizer"
|
||||
tag = "3.7.3"
|
||||
asset = "HitScoreVisualizer-3.7.3-bs1.42.0-a565cbb.zip"
|
||||
sha256 = "b889355cd47cc3b09a352f081110b8130ac6e1d285fa58776466117d585814db"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "User-provided GitHub repository URL https://github.com/ErisApps/HitScoreVisualizer. Latest non-draft, non-prerelease release 3.7.3 exposes this asset for Beat Saber 1.42.0; GitHub release digest matched the downloaded asset."
|
||||
|
||||
[[plugins]]
|
||||
id = "hidethelogo"
|
||||
repo = "TheBlackParrot/HideTheLogo"
|
||||
tag = "1.0.3"
|
||||
asset = "HideTheLogo-1.0.3-bs1.40.3-c968d91.zip"
|
||||
sha256 = "f7177cf28add03ddd73fdbd720628755c7787f515523f2b92e0041c63e83d6ca"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "User-provided GitHub repository URL https://github.com/TheBlackParrot/HideTheLogo. Latest non-draft, non-prerelease release 1.0.3 exposes this asset for Beat Saber 1.40.3; GitHub release did not expose a digest."
|
||||
|
||||
[[plugins]]
|
||||
id = "songchartvisualizer"
|
||||
repo = "NuggoDEV/SongChartVisualizer"
|
||||
tag = "beatmods-1.1.11"
|
||||
asset = "SongChartVisualizer-1.1.11.zip"
|
||||
sha256 = "290a55ff87d8769f3ae7c5d0b44a67e9e6e89e8ef83a2b1bb988ebea28f89da4"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "BeatMods verified SongChartVisualizer 1.1.11 for Beat Saber 1.44.1 as version id 2249, zipHash 5d3fc025fe098277667fc0846e1b8fe3. User-provided GitHub repository URL https://github.com/NuggoDEV/SongChartVisualizer returned no releases through the GitHub releases API, so use the BeatMods CDN artifact."
|
||||
|
||||
[[plugins]]
|
||||
id = "adblocker"
|
||||
repo = "JonnyVR1/AdBlocker"
|
||||
tag = "beatmods-1.0.5"
|
||||
asset = "AdBlocker-1.0.5.zip"
|
||||
sha256 = "0ee298563760d8c7f6ba7c8134982f9d1c2e554fe142648a3465051ac6468487"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "BeatMods verified AdBlocker 1.0.5 for Beat Saber 1.44.1 as version id 1872, zipHash cd397e93b1a03f163534483462edf768. User-provided GitHub repository URL https://github.com/JonnyVR1/AdBlocker returned no releases through the GitHub releases API, so use the BeatMods CDN artifact."
|
||||
|
||||
[[plugins]]
|
||||
id = "highlightbombs"
|
||||
repo = "Meivyn/HighlightBombs"
|
||||
tag = "beatmods-1.0.3"
|
||||
asset = "HighlightBombs-1.0.3.zip"
|
||||
sha256 = "fba7750632079dbce846ba57c2e3284fc72cb95671bff08394246e6664fe8113"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "BeatMods verified HighlightBombs 1.0.3 for Beat Saber 1.44.1 as version id 2066, zipHash 4bedaa80ce5dda8414fea7d914fb94ad. GitHub latest release v1.0.1 is older than the BeatMods-verified 1.0.3 package, so use the BeatMods CDN artifact."
|
||||
|
||||
[[plugins]]
|
||||
id = "pitchblack"
|
||||
repo = "Loloppe/BeatSaber_PitchBlack"
|
||||
tag = "0.03"
|
||||
asset = "PitchBlack-v0.0.3-bs1.39.1.zip"
|
||||
sha256 = "ae7269b4f40fea8bee3d2b213a67c3e779ff8bfbcb4f4deabcb9e9c21bfadafb"
|
||||
install_strategy = "bsipa-zip"
|
||||
reason = "BeatMods verified PitchBlack 0.0.3 for Beat Saber 1.44.1 as version id 2233, zipHash 65656bf33b2a0b356c381132387ea7ea. User-provided GitHub repository URL https://github.com/Loloppe/BeatSaber_PitchBlack tag 0.03 asset is byte-identical to the BeatMods CDN zip."
|
||||
|
||||
[[plugins]]
|
||||
id = "reecamera"
|
||||
repo = "Reezonate/ReeCamera"
|
||||
tag = "v0.0.5"
|
||||
asset = "ReeCamera.1.42.0.zip"
|
||||
sha256 = "76fb8efa1103ed18d7913c63818cb2699785fb3718d394176d5e2a6938842c24"
|
||||
install_strategy = "root-zip"
|
||||
reason = "User-provided GitHub repository URL https://github.com/Reezonate/ReeCamera. Latest non-draft, non-prerelease release v0.0.5 offers ReeCamera.1.42.0.zip as the highest Beat Saber version asset for instance 1.44.1; GitHub release digest matched the downloaded asset. Archive bundles Plugins/ReeCamera.dll, bundled CameraUtils.dll, and UserData/ReeCamera presets."
|
||||
|
||||
[[plugins]]
|
||||
id = "jdfixer"
|
||||
repo = "zeph-yr/JDFixer"
|
||||
tag = "pr-26-3fce6ce"
|
||||
asset = "JDFixer.dll"
|
||||
sha256 = "16b7dad9906d838dab40ce48a9b304be4847f18e700ddd31f2293d1065f4529d"
|
||||
install_strategy = "dll-to-plugins"
|
||||
reason = "Local build from GitHub PR https://github.com/zeph-yr/JDFixer/pull/26 at commit 3fce6ce465911bdd5e8e00411bc4672c54a317f7. Use this PR build instead of the failed upstream v.7.4.0 release asset, which loaded but failed OnEnable on Beat Saber 1.44.1."
|
||||
|
||||
@@ -43,6 +43,96 @@ id = "bsipa"
|
||||
constraint = ">=4.3.7"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "bettersonglist"
|
||||
name = "BetterSongList"
|
||||
repo = "kinsi55/BeatSaber_BetterSongList"
|
||||
asset_patterns = ["BetterSongList.dll"]
|
||||
install_strategy = "dll-to-plugins"
|
||||
category = "ui"
|
||||
|
||||
[[plugins]]
|
||||
id = "hitscorevisualizer"
|
||||
name = "HitScoreVisualizer"
|
||||
repo = "ErisApps/HitScoreVisualizer"
|
||||
asset_patterns = ["HitScoreVisualizer-*.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "ui"
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "beatsabermarkuplanguage"
|
||||
constraint = ">=1.14.1"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "bsipa"
|
||||
constraint = ">=4.3.7"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "ditails"
|
||||
name = "DiTails"
|
||||
repo = "Auros/DiTails"
|
||||
asset_patterns = ["DiTails-*.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "ui"
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "songcore"
|
||||
constraint = ">=3.16.0"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "beatsaversharp"
|
||||
constraint = ">=3.4.5"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "beatsabermarkuplanguage"
|
||||
constraint = ">=1.14.1"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "sirautil"
|
||||
constraint = ">=3.3.1"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "bsipa"
|
||||
constraint = ">=4.3.7"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "hidethelogo"
|
||||
name = "HideTheLogo"
|
||||
repo = "TheBlackParrot/HideTheLogo"
|
||||
asset_patterns = ["HideTheLogo-*.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "ui"
|
||||
|
||||
[[plugins]]
|
||||
id = "songchartvisualizer"
|
||||
name = "SongChartVisualizer"
|
||||
repo = "NuggoDEV/SongChartVisualizer"
|
||||
asset_patterns = ["SongChartVisualizer-*.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "ui"
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "beatsabermarkuplanguage"
|
||||
constraint = ">=1.14.1"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "sirautil"
|
||||
constraint = ">=3.3.1"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "bsipa"
|
||||
constraint = ">=4.3.7"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "sirautil"
|
||||
name = "SiraUtil"
|
||||
@@ -92,6 +182,147 @@ id = "bsipa"
|
||||
constraint = ">=4.3.7"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "heck"
|
||||
name = "Heck"
|
||||
repo = "Aeroluna/Heck"
|
||||
asset_patterns = ["Heck-*.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "library"
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "bsipa"
|
||||
constraint = ">=4.3.7"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "customjsondata"
|
||||
constraint = ">=2.6.8"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "lookupid"
|
||||
name = "LookupID"
|
||||
repo = "Aeroluna/Heck"
|
||||
asset_patterns = ["LookupID-*.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "library"
|
||||
|
||||
[[plugins]]
|
||||
id = "chroma"
|
||||
name = "Chroma"
|
||||
repo = "Aeroluna/Heck"
|
||||
asset_patterns = ["Chroma-*.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "mapping"
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "heck"
|
||||
constraint = ">=1.8.3"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "customjsondata"
|
||||
constraint = ">=2.6.8"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "lookupid"
|
||||
constraint = ">=1.0.1"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "noodleextensions"
|
||||
name = "Noodle Extensions"
|
||||
repo = "Aeroluna/Heck"
|
||||
asset_patterns = ["NoodleExtensions-*.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "mapping"
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "heck"
|
||||
constraint = ">=1.8.3"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "customjsondata"
|
||||
constraint = ">=2.6.8"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "camerautils"
|
||||
name = "CameraUtils"
|
||||
repo = "Reezonate/CameraUtils"
|
||||
asset_patterns = ["CameraUtils-*.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "library"
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "bsipa"
|
||||
constraint = ">=4.3.7"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "assetbundleloadingtools"
|
||||
name = "AssetBundleLoadingTools"
|
||||
repo = "nicoco007/AssetBundleLoadingTools"
|
||||
asset_patterns = ["AssetBundleLoadingTools-*.zip"]
|
||||
install_strategy = "root-zip"
|
||||
category = "library"
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "bsipa"
|
||||
constraint = ">=4.3.7"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "beatsabermarkuplanguage"
|
||||
constraint = ">=1.14.1"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "sirautil"
|
||||
constraint = ">=3.3.1"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "system-io-compression"
|
||||
constraint = ">=4.6.57"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "system-io-compression-filesystem"
|
||||
constraint = ">=4.7.3056"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "vivify"
|
||||
name = "Vivify"
|
||||
repo = "Aeroluna/Vivify"
|
||||
asset_patterns = ["Vivify-*.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "mapping"
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "heck"
|
||||
constraint = ">=1.8.3"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "customjsondata"
|
||||
constraint = ">=2.6.8"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "camerautils"
|
||||
constraint = ">=1.0.8"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "assetbundleloadingtools"
|
||||
constraint = ">=1.1.13"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "iniparser"
|
||||
name = "Ini Parser"
|
||||
@@ -188,6 +419,139 @@ asset_patterns = ["ScoreSaberSharp-*.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "library"
|
||||
|
||||
[[plugins]]
|
||||
id = "scoresaber"
|
||||
name = "ScoreSaber"
|
||||
repo = "ScoreSaber/pc-mod"
|
||||
asset_patterns = ["ScoreSaber-*.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "leaderboard"
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "beatsabermarkuplanguage"
|
||||
constraint = ">=1.14.1"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "sirautil"
|
||||
constraint = ">=3.3.1"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "songcore"
|
||||
constraint = ">=3.16.0"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "bsipa"
|
||||
constraint = ">=4.3.7"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "leaderboardcore"
|
||||
name = "LeaderboardCore"
|
||||
repo = "NSGolova/LeaderboardCore"
|
||||
asset_patterns = ["LeaderboardCore-*.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "library"
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "beatsabermarkuplanguage"
|
||||
constraint = ">=1.14.1"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "sirautil"
|
||||
constraint = ">=3.3.1"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "bsipa"
|
||||
constraint = ">=4.3.7"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "protobuf-net"
|
||||
name = "protobuf-net"
|
||||
repo = "protobuf-net/protobuf-net"
|
||||
asset_patterns = ["protobuf-net-*.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "library"
|
||||
|
||||
[[plugins]]
|
||||
id = "songdetailscache"
|
||||
name = "SongDetailsCache"
|
||||
repo = "kinsi55/BeatSaber_SongDetails"
|
||||
asset_patterns = ["SongDetailsCache.BS.Lib.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "library"
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "protobuf-net"
|
||||
constraint = ">=3.0.102"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "songrankedbadge"
|
||||
name = "SongRankedBadge"
|
||||
repo = "qe201020335/SongRankedBadge"
|
||||
asset_patterns = ["SongRankedBadge-*.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "ui"
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "songcore"
|
||||
constraint = ">=3.16.0"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "beatsabermarkuplanguage"
|
||||
constraint = ">=1.14.1"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "songdetailscache"
|
||||
constraint = ">=1.4.0"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "bsipa"
|
||||
constraint = ">=4.3.7"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "beatleader"
|
||||
name = "BeatLeader"
|
||||
repo = "BeatLeader/beatleader-mod"
|
||||
asset_patterns = ["BeatLeader-*.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "leaderboard"
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "bs-utils"
|
||||
constraint = ">=1.14.3"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "beatsabermarkuplanguage"
|
||||
constraint = ">=1.14.1"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "songcore"
|
||||
constraint = ">=3.16.0"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "sirautil"
|
||||
constraint = ">=3.3.1"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "bsipa"
|
||||
constraint = ">=4.3.7"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "beatsaverdownloader"
|
||||
name = "BeatSaverDownloader"
|
||||
@@ -264,6 +628,82 @@ id = "beatsaversharp"
|
||||
constraint = ">=3.4.5"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "playlistmanager"
|
||||
name = "PlaylistManager"
|
||||
repo = "rithik-b/PlaylistManager"
|
||||
asset_patterns = ["PlaylistManager-*.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "playlist"
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "songcore"
|
||||
constraint = ">=3.16.0"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "sirautil"
|
||||
constraint = ">=3.3.1"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "beatsaberplaylistslib"
|
||||
constraint = ">=1.7.2"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "system-io-compression-filesystem"
|
||||
constraint = ">=4.7.3056"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "bsipa"
|
||||
constraint = ">=4.3.7"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "beatsaversharp"
|
||||
constraint = ">=3.4.5"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "system-io-compression"
|
||||
constraint = ">=4.6.57"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "beatsabermarkuplanguage"
|
||||
constraint = ">=1.14.1"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "beatsavervoting"
|
||||
name = "BeatSaverVoting"
|
||||
repo = "Top-Cat/BeatSaverVoting"
|
||||
asset_patterns = ["BeatSaverVoting-*.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "downloader"
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "bs-utils"
|
||||
constraint = ">=1.14.3"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "songcore"
|
||||
constraint = ">=3.16.0"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "beatsabermarkuplanguage"
|
||||
constraint = ">=1.14.1"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "bsipa"
|
||||
constraint = ">=4.3.7"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "setlist"
|
||||
name = "Setlist"
|
||||
@@ -285,3 +725,215 @@ required = true
|
||||
id = "beatsaberplaylistslib"
|
||||
constraint = ">=1.7.0"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "introskip"
|
||||
name = "IntroSkip"
|
||||
repo = "Loloppe/Intro-Skip"
|
||||
asset_patterns = ["IntroSkip-*.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "gameplay"
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "beatsabermarkuplanguage"
|
||||
constraint = ">=1.14.1"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "sirautil"
|
||||
constraint = ">=3.3.1"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "bsipa"
|
||||
constraint = ">=4.3.7"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "failbutton"
|
||||
name = "FailButton"
|
||||
repo = "qe201020335/FailButton"
|
||||
asset_patterns = ["FailButton-*.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "gameplay"
|
||||
|
||||
[[plugins]]
|
||||
id = "easyoffset"
|
||||
name = "EasyOffset"
|
||||
repo = "Reezonate/EasyOffset"
|
||||
asset_patterns = ["EasyOffset.dll"]
|
||||
install_strategy = "dll-to-plugins"
|
||||
category = "gameplay"
|
||||
|
||||
[[plugins]]
|
||||
id = "gottagofast"
|
||||
name = "GottaGoFast"
|
||||
repo = "kinsi55/CS_BeatSaber_GottaGoFast"
|
||||
asset_patterns = ["GottaGoFast.dll"]
|
||||
install_strategy = "dll-to-plugins"
|
||||
category = "gameplay"
|
||||
|
||||
[[plugins]]
|
||||
id = "hitsoundtweaks"
|
||||
name = "HitsoundTweaks"
|
||||
repo = "GalaxyMaster2/HitsoundTweaks"
|
||||
asset_patterns = ["HitsoundTweaks-*.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "gameplay"
|
||||
|
||||
[[plugins]]
|
||||
id = "keepmyoverridespls"
|
||||
name = "KeepMyOverridesPls"
|
||||
repo = "qqrz997/KeepMyOverridesPls"
|
||||
asset_patterns = ["KeepMyOverridesPls-*.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "gameplay"
|
||||
|
||||
[[plugins]]
|
||||
id = "soundreplacer"
|
||||
name = "SoundReplacer"
|
||||
repo = "Meivyn/SoundReplacer"
|
||||
asset_patterns = ["SoundReplacer-*.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "gameplay"
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "sirautil"
|
||||
constraint = ">=3.3.1"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "beatsabermarkuplanguage"
|
||||
constraint = ">=1.14.1"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "bsipa"
|
||||
constraint = ">=4.3.7"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "keyremapper"
|
||||
name = "KeyRemapper"
|
||||
repo = "lyyQwQ/KeyRemapper"
|
||||
asset_patterns = ["KeyRemapper-*.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "gameplay"
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "beatsabermarkuplanguage"
|
||||
constraint = ">=1.14.1"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "sirautil"
|
||||
constraint = ">=3.3.1"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "bsipa"
|
||||
constraint = ">=4.3.7"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "squattobegin"
|
||||
name = "SquatToBegin"
|
||||
repo = "kinsi55/BeatSaber_SquatToBegin"
|
||||
asset_patterns = ["SquatToBegin.dll"]
|
||||
install_strategy = "dll-to-plugins"
|
||||
category = "gameplay"
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "bsipa"
|
||||
constraint = ">=4.3.7"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "adblocker"
|
||||
name = "AdBlocker"
|
||||
repo = "JonnyVR1/AdBlocker"
|
||||
asset_patterns = ["AdBlocker-*.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "cosmetic"
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "bsipa"
|
||||
constraint = ">=4.3.7"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "highlightbombs"
|
||||
name = "HighlightBombs"
|
||||
repo = "Meivyn/HighlightBombs"
|
||||
asset_patterns = ["HighlightBombs-*.zip", "HighlightBombs.dll"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "cosmetic"
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "bsipa"
|
||||
constraint = ">=4.3.7"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "sirautil"
|
||||
constraint = ">=3.3.1"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "pitchblack"
|
||||
name = "PitchBlack"
|
||||
repo = "Loloppe/BeatSaber_PitchBlack"
|
||||
asset_patterns = ["PitchBlack-*.zip"]
|
||||
install_strategy = "bsipa-zip"
|
||||
category = "lighting"
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "beatsabermarkuplanguage"
|
||||
constraint = ">=1.14.1"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "bsipa"
|
||||
constraint = ">=4.3.7"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "reecamera"
|
||||
name = "ReeCamera"
|
||||
repo = "Reezonate/ReeCamera"
|
||||
asset_patterns = ["ReeCamera.*.zip"]
|
||||
install_strategy = "root-zip"
|
||||
category = "camera"
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "camerautils"
|
||||
constraint = ">=1.0.8"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "bsipa"
|
||||
constraint = ">=4.3.7"
|
||||
required = true
|
||||
|
||||
[[plugins]]
|
||||
id = "jdfixer"
|
||||
name = "JDFixer"
|
||||
repo = "zeph-yr/JDFixer"
|
||||
asset_patterns = ["JDFixer.dll"]
|
||||
install_strategy = "dll-to-plugins"
|
||||
category = "gameplay"
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "beatsabermarkuplanguage"
|
||||
constraint = ">=1.14.1"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "sirautil"
|
||||
constraint = ">=3.3.1"
|
||||
required = true
|
||||
|
||||
[[plugins.dependencies]]
|
||||
id = "bsipa"
|
||||
constraint = ">=4.3.7"
|
||||
required = true
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class BeatModsEntry:
|
||||
name: str
|
||||
mod_id: int | None
|
||||
git_url: str | None
|
||||
category: str | None
|
||||
version_id: int | None
|
||||
mod_version: str | None
|
||||
zip_hash: str | None
|
||||
dependencies: tuple[int, ...]
|
||||
raw: dict[str, Any]
|
||||
|
||||
|
||||
def extract_mods(payload: Any) -> list[dict[str, Any]]:
|
||||
"""Return the list of BeatMods records from either current or legacy shapes."""
|
||||
if isinstance(payload, dict) and isinstance(payload.get("mods"), list):
|
||||
payload = payload["mods"]
|
||||
if not isinstance(payload, list):
|
||||
raise ValueError("BeatMods payload must be a list or an object with a mods list")
|
||||
return [item for item in payload if isinstance(item, dict)]
|
||||
|
||||
|
||||
def normalize_entry(entry: dict[str, Any]) -> BeatModsEntry:
|
||||
"""Normalize BeatMods' nested {mod, latest} response and older flat records."""
|
||||
mod = entry.get("mod") if isinstance(entry.get("mod"), dict) else entry
|
||||
latest = entry.get("latest") if isinstance(entry.get("latest"), dict) else entry
|
||||
dependencies = latest.get("dependencies", ())
|
||||
|
||||
return BeatModsEntry(
|
||||
name=str(mod.get("name") or ""),
|
||||
mod_id=_int_or_none(mod.get("id")),
|
||||
git_url=_str_or_none(mod.get("gitUrl")),
|
||||
category=_str_or_none(mod.get("category")),
|
||||
version_id=_int_or_none(latest.get("id")),
|
||||
mod_version=_str_or_none(latest.get("modVersion")),
|
||||
zip_hash=_str_or_none(latest.get("zipHash")),
|
||||
dependencies=tuple(_dependency_id(dep) for dep in dependencies if _dependency_id(dep) is not None),
|
||||
raw=entry,
|
||||
)
|
||||
|
||||
|
||||
def normalize_mods(payload: Any) -> list[BeatModsEntry]:
|
||||
return [normalize_entry(entry) for entry in extract_mods(payload)]
|
||||
|
||||
|
||||
def by_version_id(entries: list[BeatModsEntry]) -> dict[int, BeatModsEntry]:
|
||||
return {entry.version_id: entry for entry in entries if entry.version_id is not None}
|
||||
|
||||
|
||||
def _dependency_id(dependency: Any) -> int | None:
|
||||
if isinstance(dependency, dict):
|
||||
return _int_or_none(dependency.get("id"))
|
||||
return _int_or_none(dependency)
|
||||
|
||||
|
||||
def _int_or_none(value: Any) -> int | None:
|
||||
if isinstance(value, bool) or value is None:
|
||||
return None
|
||||
try:
|
||||
return int(value)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
|
||||
def _str_or_none(value: Any) -> str | None:
|
||||
if value is None:
|
||||
return None
|
||||
text = str(value)
|
||||
return text if text else None
|
||||
@@ -19,7 +19,7 @@ from .planner import create_plan
|
||||
from .scanner import scan_instance
|
||||
from .state import load_installed_state
|
||||
from .updates import check_updates
|
||||
from .userdata import sync_windows_data_repo
|
||||
from .userdata import restore_windows_data_repo, sync_windows_data_repo
|
||||
|
||||
|
||||
def _json(data: Any) -> None:
|
||||
@@ -246,6 +246,16 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
backup.add_argument("--appdata-path", help="Override Beat Saber Windows AppData path")
|
||||
backup.add_argument("--no-appdata", action="store_true", help="Only copy UserData")
|
||||
|
||||
restore = subcommands.add_parser(
|
||||
"restore-userdata",
|
||||
help="Restore UserData and AppData from the backups repo into an instance",
|
||||
parents=[_common_parent()],
|
||||
)
|
||||
restore.add_argument("--instance", required=True)
|
||||
restore.add_argument("--backup-root", default="../backups/beat-saber", help="Backup directory")
|
||||
restore.add_argument("--appdata-path", help="Override Beat Saber AppData destination path")
|
||||
restore.add_argument("--no-appdata", action="store_true", help="Only restore UserData")
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
@@ -300,7 +310,8 @@ def _run_menu(inst_roots: list[Path], st_root: Path, input_func: Callable[[str],
|
||||
("bootstrap-check", "Check BSIPA bootstrap", "Verifies recorded bootstrap state and the latest BSIPA log evidence."),
|
||||
("plan", "Create install plan", "Writes a dry-run JSON plan for locked plugin files before anything is applied."),
|
||||
("apply", "Apply a plan by path", "Installs exactly the file changes from a previously generated plan JSON."),
|
||||
("backup-userdata", "Back up UserData", "Creates a timestamped archive of UserData before risky changes."),
|
||||
("backup-userdata", "Back up UserData", "Copies UserData and AppData into the adjacent backups repo."),
|
||||
("restore-userdata", "Restore UserData", "Restores UserData and AppData from the backups repo into an instance."),
|
||||
("change", "Choose another version", "Returns to the Beat Saber version picker."),
|
||||
]
|
||||
|
||||
@@ -574,6 +585,27 @@ def run(argv: list[str] | None = None) -> int:
|
||||
print(f" {item['source']} -> {item['destination']}")
|
||||
return 0
|
||||
|
||||
if args.command == "restore-userdata":
|
||||
instance = get_instance(inst_roots, args.instance)
|
||||
root = repo_root()
|
||||
backup_root = Path(args.backup_root).expanduser()
|
||||
if not backup_root.is_absolute():
|
||||
backup_root = (root / backup_root).resolve()
|
||||
result = restore_windows_data_repo(
|
||||
instance=args.instance,
|
||||
instance_path=instance.path,
|
||||
backup_root=backup_root,
|
||||
appdata_path=Path(args.appdata_path).expanduser() if args.appdata_path else None,
|
||||
include_appdata=not args.no_appdata,
|
||||
)
|
||||
print(f"Backup root: {result['backupRoot']}")
|
||||
for item in result["restored"]:
|
||||
print(f"{item['label']}: {item['fileCount']} files")
|
||||
print(f" {item['source']} -> {item['destination']}")
|
||||
if item["snapshot"]:
|
||||
print(f" previous: {item['snapshot']}")
|
||||
return 0
|
||||
|
||||
except Exception as exc:
|
||||
print(f"error: {exc}", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
@@ -84,6 +84,19 @@ def infer_windows_appdata_path(instance_path: Path) -> Path:
|
||||
return profile / "AppData" / "LocalLow" / "Hyperbolic Magnetism" / "Beat Saber"
|
||||
|
||||
|
||||
def infer_proton_appdata_path() -> Path:
|
||||
return (
|
||||
Path.home()
|
||||
/ ".local/share/BSManager/SharedContent/compatdata/pfx/drive_c/users/steamuser/AppData/LocalLow/Hyperbolic Magnetism/Beat Saber"
|
||||
)
|
||||
|
||||
|
||||
def infer_appdata_path(instance_path: Path) -> Path:
|
||||
if "Users" in instance_path.resolve().parts:
|
||||
return infer_windows_appdata_path(instance_path)
|
||||
return infer_proton_appdata_path()
|
||||
|
||||
|
||||
def sync_windows_data_repo(
|
||||
*,
|
||||
instance: str,
|
||||
@@ -96,7 +109,7 @@ def sync_windows_data_repo(
|
||||
("UserData", instance_path / "UserData", backup_root / "UserData"),
|
||||
]
|
||||
if include_appdata:
|
||||
appdata = appdata_path or infer_windows_appdata_path(instance_path)
|
||||
appdata = appdata_path or infer_appdata_path(instance_path)
|
||||
sources.append(("AppData", appdata, backup_root / "AppData"))
|
||||
|
||||
for label, source, _ in sources:
|
||||
@@ -145,6 +158,63 @@ def sync_windows_data_repo(
|
||||
}
|
||||
|
||||
|
||||
def restore_windows_data_repo(
|
||||
*,
|
||||
instance: str,
|
||||
instance_path: Path,
|
||||
backup_root: Path,
|
||||
appdata_path: Path | None = None,
|
||||
include_appdata: bool = True,
|
||||
) -> dict[str, Any]:
|
||||
descriptor_path = backup_root / "backup-descriptor.json"
|
||||
if descriptor_path.is_file():
|
||||
descriptor = json.loads(descriptor_path.read_text(encoding="utf-8"))
|
||||
descriptor_instance = descriptor.get("instance")
|
||||
if descriptor_instance and descriptor_instance != instance:
|
||||
raise ValueError(
|
||||
f"backup descriptor instance {descriptor_instance!r} does not match requested instance {instance!r}"
|
||||
)
|
||||
|
||||
created_at = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
|
||||
restores: list[tuple[str, Path, Path]] = [
|
||||
("UserData", backup_root / "UserData", instance_path / "UserData"),
|
||||
]
|
||||
if include_appdata:
|
||||
appdata = appdata_path or infer_appdata_path(instance_path)
|
||||
restores.append(("AppData", backup_root / "AppData", appdata))
|
||||
|
||||
for label, source, _ in restores:
|
||||
if not source.is_dir():
|
||||
raise FileNotFoundError(f"{label} backup not found: {source}")
|
||||
|
||||
restored: list[dict[str, Any]] = []
|
||||
snapshots: list[dict[str, Any]] = []
|
||||
for label, source, destination in restores:
|
||||
snapshot: Path | None = None
|
||||
if destination.exists():
|
||||
snapshot = destination.parent / f"{destination.name}.pre-restore-{created_at}"
|
||||
destination.rename(snapshot)
|
||||
snapshots.append({"label": label, "path": str(snapshot)})
|
||||
|
||||
destination.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copytree(source, destination, symlinks=True)
|
||||
restored.append(
|
||||
{
|
||||
"label": label,
|
||||
"source": str(source),
|
||||
"destination": str(destination),
|
||||
"fileCount": sum(1 for item in destination.rglob("*") if item.is_file()),
|
||||
"snapshot": str(snapshot) if snapshot else None,
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
"backupRoot": str(backup_root),
|
||||
"restored": restored,
|
||||
"snapshots": snapshots,
|
||||
}
|
||||
|
||||
|
||||
def _ignore_backup_paths(source_root: Path, skipped: list[str]) -> Callable[[str, list[str]], set[str]]:
|
||||
def ignore(current_dir: str, names: list[str]) -> set[str]:
|
||||
ignored: set[str] = set()
|
||||
|
||||
+155
-1
@@ -10,6 +10,7 @@ from unittest.mock import patch
|
||||
from zipfile import ZipFile
|
||||
|
||||
from plugin_helper.bootstrap import _run_ipa
|
||||
from plugin_helper.beatmods import by_version_id, normalize_mods
|
||||
from plugin_helper.checker import check_lock
|
||||
from plugin_helper.cli import installed_plugins_report, run
|
||||
from plugin_helper.fsutil import sha256_file
|
||||
@@ -20,10 +21,67 @@ from plugin_helper.planner import create_plan
|
||||
from plugin_helper.scanner import scan_bootstrap_files, scan_instance
|
||||
from plugin_helper.state import downloads_dir, load_installed_state, plugin_downloads_dir, save_bootstrap_state
|
||||
from plugin_helper.updates import check_updates
|
||||
from plugin_helper.userdata import backup_userdata, infer_windows_appdata_path, sync_windows_data_repo
|
||||
from plugin_helper.userdata import (
|
||||
backup_userdata,
|
||||
infer_appdata_path,
|
||||
infer_proton_appdata_path,
|
||||
infer_windows_appdata_path,
|
||||
restore_windows_data_repo,
|
||||
sync_windows_data_repo,
|
||||
)
|
||||
|
||||
|
||||
class PluginHelperTests(unittest.TestCase):
|
||||
def test_normalize_beatmods_current_nested_response(self) -> None:
|
||||
payload = {
|
||||
"mods": [
|
||||
{
|
||||
"mod": {
|
||||
"id": 10,
|
||||
"name": "Example",
|
||||
"gitUrl": "https://github.com/example/mod",
|
||||
"category": "library",
|
||||
},
|
||||
"latest": {
|
||||
"id": 1234,
|
||||
"modVersion": "1.2.3",
|
||||
"zipHash": "abc123",
|
||||
"dependencies": [2561, {"id": "2567"}],
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
entries = normalize_mods(payload)
|
||||
|
||||
self.assertEqual(len(entries), 1)
|
||||
self.assertEqual(entries[0].name, "Example")
|
||||
self.assertEqual(entries[0].mod_id, 10)
|
||||
self.assertEqual(entries[0].version_id, 1234)
|
||||
self.assertEqual(entries[0].dependencies, (2561, 2567))
|
||||
self.assertEqual(by_version_id(entries)[1234].zip_hash, "abc123")
|
||||
|
||||
def test_normalize_beatmods_legacy_flat_response(self) -> None:
|
||||
entries = normalize_mods(
|
||||
[
|
||||
{
|
||||
"id": "12",
|
||||
"name": "FlatExample",
|
||||
"gitUrl": "",
|
||||
"category": "mods",
|
||||
"modVersion": "2.0.0",
|
||||
"zipHash": "def456",
|
||||
"dependencies": [{"id": 44}, "45", None],
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
self.assertEqual(entries[0].name, "FlatExample")
|
||||
self.assertEqual(entries[0].mod_id, 12)
|
||||
self.assertEqual(entries[0].version_id, 12)
|
||||
self.assertIsNone(entries[0].git_url)
|
||||
self.assertEqual(entries[0].dependencies, (44, 45))
|
||||
|
||||
def test_instances_and_scan(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root = Path(tmp)
|
||||
@@ -480,6 +538,102 @@ class PluginHelperTests(unittest.TestCase):
|
||||
self.assertIn("BeatLeader/Replays", descriptor["skipped"])
|
||||
self.assertIn("*.log", descriptor["excludePatterns"])
|
||||
|
||||
def test_infer_proton_appdata_path(self) -> None:
|
||||
self.assertEqual(
|
||||
infer_proton_appdata_path(),
|
||||
Path.home()
|
||||
/ ".local/share/BSManager/SharedContent/compatdata/pfx/drive_c/users/steamuser/AppData/LocalLow/Hyperbolic Magnetism/Beat Saber",
|
||||
)
|
||||
|
||||
def test_infer_appdata_path_uses_windows_or_proton(self) -> None:
|
||||
windows_instance = Path("/home/pleb/Windows/Users/pleb/BSManager/BSInstances/1.44.1")
|
||||
linux_instance = Path("/home/pleb/.local/share/BSManager/BSInstances/1.44.1")
|
||||
|
||||
self.assertEqual(infer_appdata_path(windows_instance), infer_windows_appdata_path(windows_instance))
|
||||
self.assertEqual(infer_appdata_path(linux_instance), infer_proton_appdata_path())
|
||||
|
||||
def test_restore_windows_data_repo_roundtrip(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root = Path(tmp)
|
||||
instance = root / "Users" / "pleb" / "BSManager" / "BSInstances" / "1.44.1"
|
||||
appdata = root / "Users" / "pleb" / "AppData" / "LocalLow" / "Hyperbolic Magnetism" / "Beat Saber"
|
||||
backup_repo = root / "backup"
|
||||
(instance / "UserData").mkdir(parents=True)
|
||||
(instance / "UserData" / "settings.json").write_text('{"saved": true}', encoding="utf-8")
|
||||
appdata.mkdir(parents=True)
|
||||
(appdata / "settings.cfg").write_text("settings", encoding="utf-8")
|
||||
|
||||
sync_windows_data_repo(
|
||||
instance="1.44.1",
|
||||
instance_path=instance,
|
||||
backup_root=backup_repo,
|
||||
)
|
||||
|
||||
(instance / "UserData" / "settings.json").write_text('{"saved": false}', encoding="utf-8")
|
||||
(appdata / "settings.cfg").write_text("changed", encoding="utf-8")
|
||||
|
||||
result = restore_windows_data_repo(
|
||||
instance="1.44.1",
|
||||
instance_path=instance,
|
||||
backup_root=backup_repo,
|
||||
)
|
||||
|
||||
self.assertEqual((instance / "UserData" / "settings.json").read_text(), '{"saved": true}')
|
||||
self.assertEqual((appdata / "settings.cfg").read_text(), "settings")
|
||||
self.assertEqual(len(result["restored"]), 2)
|
||||
self.assertTrue(all(item["snapshot"] for item in result["restored"]))
|
||||
|
||||
def test_restore_windows_data_repo_rejects_instance_mismatch(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root = Path(tmp)
|
||||
instance = root / "1.44.1"
|
||||
backup_repo = root / "backup"
|
||||
(instance / "UserData").mkdir(parents=True)
|
||||
(instance / "UserData" / "settings.json").write_text("{}", encoding="utf-8")
|
||||
(backup_repo / "UserData").mkdir(parents=True)
|
||||
(backup_repo / "UserData" / "settings.json").write_text("{}", encoding="utf-8")
|
||||
(backup_repo / "backup-descriptor.json").write_text(
|
||||
json.dumps({"instance": "1.40.8"}) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "does not match"):
|
||||
restore_windows_data_repo(
|
||||
instance="1.44.1",
|
||||
instance_path=instance,
|
||||
backup_root=backup_repo,
|
||||
include_appdata=False,
|
||||
)
|
||||
|
||||
def test_restore_userdata_cli(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root = Path(tmp)
|
||||
instances_root = root / "instances"
|
||||
instance = instances_root / "1.44.1"
|
||||
backup_repo = root / "backup"
|
||||
(instance / "UserData").mkdir(parents=True)
|
||||
(instance / "UserData" / "settings.json").write_text('{"restored": true}', encoding="utf-8")
|
||||
(backup_repo / "UserData").mkdir(parents=True)
|
||||
(backup_repo / "UserData" / "settings.json").write_text('{"restored": true}', encoding="utf-8")
|
||||
|
||||
status = run(
|
||||
[
|
||||
"--instances-root",
|
||||
str(instances_root),
|
||||
"--state-dir",
|
||||
str(root / "state"),
|
||||
"restore-userdata",
|
||||
"--instance",
|
||||
"1.44.1",
|
||||
"--backup-root",
|
||||
str(backup_repo),
|
||||
"--no-appdata",
|
||||
]
|
||||
)
|
||||
|
||||
self.assertEqual(status, 0)
|
||||
self.assertEqual((instance / "UserData" / "settings.json").read_text(), '{"restored": true}')
|
||||
|
||||
def test_check_reports_missing_asset(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
work = Path(tmp)
|
||||
|
||||
Reference in New Issue
Block a user