Files
plugin-helper/.agents/skills/beatsaber-plugin-builder/references/linux-bsipa-build.md
T
2026-06-29 10:57:11 -07:00

4.6 KiB

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:

Beat Saber.exe
Beat Saber_Data/Managed/
IPA/
Libs/
Plugins/
winhttp.dll

Preferred local managed instance root:

/home/pleb/.local/share/BSManager/BSInstances/<version>

Windows mirror root:

/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:

<Project>
  <PropertyGroup>
    <BeatSaberDir>/home/pleb/.local/share/BSManager/BSInstances/1.40.8</BeatSaberDir>
  </PropertyGroup>
</Project>

Some projects use LocalRefsDir and set:

<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:

dotnet restore <solution-or-project>
dotnet build <solution-or-project> -c Release -p:DisableCopyToPlugins=True

For a Debug smoke build:

dotnet build <solution-or-project> -c Debug -p:DisableCopyToPlugins=True

Expected outputs:

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:

-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:

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:

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.