-
Notifications
You must be signed in to change notification settings - Fork 4
Detect and locate VRChat and Proton out of the box #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| # VRCSDK Patching Notes | ||
|
|
||
| ## How VRCSDK is meant to work | ||
|
|
||
| The Unity EditorPref key `VRC_installedClientPath` is meant to hold a string of a absolute path that points to VRChat.exe. | ||
|
|
||
| This is always set after VRCSDK initializes; if it was not already set, it pulls the value from some key in the registry, which was probably set beforehand by VRChat.exe or launch.exe. | ||
|
|
||
| In the settings page, the user has an option to change `VRC_installedClientPath`. I have no idea what the practical use-case is for doing this. Clicking "Revert to Default" resets it by reading from the registry again. | ||
|
|
||
| The VRCSDK.World "Build and test" button will execute VRChat.exe directly, giving it many arguments that launch the game in offline mode with a `file:///` URI to the built world AssetBundle ending in `.vrcw`. It only falls back to "executing" the equivalent `vrchat://` link directly if VRChat.exe hasn't been found -- that is, `VRC_installedClientPath` is unset because the registry key was missing, or VRChat was recently moved to a different Steam library. | ||
|
|
||
| The VRCSDK fetches avatars in the Content Manager window, by searching directly inside `Environment.SpecialFolder.LocalApplicationData`. This is `AppData/LocalLow` on Windows and `~/.local/share/VRChat` on Linux, but there's no practical reason for the latter to exist. This dir has even been known to interfere with VRCFaceTracking. The AssetBundle can technically work no matter where you save it, but it's best if it's saved to the LocalLow inside VRChat's Proton prefix. | ||
|
|
||
| ## Alternatives | ||
|
|
||
| Prior to this doc, we've been manually launching Proton, and the user must select the Proton version by way of searching for the `proton` Python script file to use as an entrypoint. It also did not locate the VRChat game directory, so moving it to a non-default Steam library would break it. | ||
|
|
||
| Now, we use `SteamLocator`, `VrcLocator`, and `ProtonLocator` to find everything, reconciling it with Unity preferences. | ||
|
|
||
| There is still the option to ask Steam to launch it, with `steam -applaunch 438100` or something, but I don't know if it would work. We want it to use offline mode, allow multiple instances, and not interfere with any instance running in online mode. The URI handler below has the same effect as launching it from Steam. The `vrchat://` URIs can work on Linux, but they need a .desktop file. | ||
|
|
||
| ## How we patch VRCSDK | ||
|
|
||
| This is an exhaustive list. | ||
|
|
||
| - Fixes the VRCSDK initialization so it can correctly find VRChat.exe. | ||
| - We replace the `LoadRegistryVRCInstallPath` method to call our `VrcLocator` instead. | ||
| - Prevents the pointless creation (lol) and use of `~/.local/share/VRChat`, instead saving test worlds and avatars to the Proton prefix. | ||
| - We replace the method that looks for `LocalLow` to do Not that. | ||
| - Fixes the Content Manager tab so it can show your test avatars. | ||
| - Fixes the Build and Test button. | ||
| - We replace the entire `VRCWorldAssetExporter.RunWorldTestDesktop` method to: | ||
| - Translate the path of the saved AssetBundle to a winepath relative to `Z:/`. | ||
| - Call Proton instead, with all `STEAM_COMPAT_` env vars set. | ||
| - We do not launch it in Steam Linux Runtime, and I hope it doesn't come to that. But it is possible, we'd just read `toolmanifest.vdf` and wrap the command further. | ||
| - Adds some UI in VRCSDK Settings tab to select a different Proton to use instead of what's set in Steam. | ||
|
|
||
| ## Ideas/Roadmap | ||
|
|
||
| - [ ] Warn if `~/.local/share/VRChat` exists, because the latest VRCFaceTracking.Avalonia release (currently v1.1.1.0) will break if it sees this. | ||
| - [ ] Run `xdg-mime query` and offer to set up [the URI handler](#vrchat-uri-handler). | ||
| - [ ] Patch UdonSharp exception watcher. | ||
| - [ ] MIT license button in Tools menu. | ||
|
|
||
| ## Snippets | ||
|
|
||
| ### VRChat Offline Mode | ||
|
|
||
| Example command I use to run VRChat in offline mode, supporting multiple clients all loaded into an instance of a world AssetBundle stored locally: | ||
|
|
||
| ```bash | ||
| STEAM_COMPAT_DATA_PATH=/mnt/steam/steamapps/compatdata/438100/ STEAM_COMPAT_CLIENT_INSTALL_PATH=$HOME/.local/share/Steam/ STEAM_COMPAT_INSTALL_PATH=/mnt/steam/steamapps/common/VRChat/ ~/.local/share/Steam/compatibilitytools.d/GE-Proton10-33-rtsp24-1/proton run /mnt/steam/steamapps/common/VRChat/VRChat.exe '--url=create?roomId=8094722763&hidden=true&name=BuildAndRun&url=file:///Z:/mnt/steam/steamapps/common/VRChat/VRChat_Data/StreamingAssets/Worlds/errorworld.vrcw' --enable-debug-gui --enable-sdk-log-levels --enable-udon-debug-logging --no-vr | ||
| ``` | ||
|
|
||
| ### VRChat URI Handler | ||
|
|
||
| Save to `~/.local/share/applications/vrchat-uri-handler.desktop`, and Firefox will let you click "Launch World" buttons on the website. It'll let you `xdg-open vrchat://` too. | ||
|
|
||
| ```desktop file=vrchat-uri-handler.desktop | ||
| [Desktop Entry] | ||
| Name=URI-vrchat | ||
| Comment=URI handler for vrchat:// | ||
| Exec=/usr/bin/env steam -applaunch 438100 %U | ||
| Terminal=false | ||
| Type=Application | ||
| Categories=Game; | ||
| MimeType=x-scheme-handler/vrchat; | ||
| NoDisplay=true | ||
| ``` |
This file was deleted.
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,118 @@ | ||||||||||
| #nullable enable | ||||||||||
|
|
||||||||||
| using System.IO; | ||||||||||
| using BefuddledLabs.LinuxVRChatSdkPatch.Base.Editor.Locators; | ||||||||||
| using UnityEngine; | ||||||||||
|
|
||||||||||
| namespace BefuddledLabs.LinuxVRChatSdkPatch.Base.Editor | ||||||||||
| { | ||||||||||
| /// <summary> | ||||||||||
| /// Represents all Linux-specific settings required to start VRChat using the World SDK Build and Test functionality, | ||||||||||
| /// determined by resolving all defaults with the user's preferences in Unity and Steam. | ||||||||||
| /// </summary> | ||||||||||
| public class LaunchConfiguration | ||||||||||
| { | ||||||||||
| private LaunchConfiguration(string steamRoot, string protonExecutable, string compatDataPath, | ||||||||||
| string vrcInstallRoot) | ||||||||||
| { | ||||||||||
| SteamRoot = steamRoot; | ||||||||||
| ProtonExecutable = protonExecutable; | ||||||||||
| CompatDataPath = compatDataPath; | ||||||||||
| VrcInstallRoot = vrcInstallRoot; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| /// <summary> | ||||||||||
| /// An absolute path to the Steam root directory. | ||||||||||
| /// </summary> | ||||||||||
|
|
||||||||||
| public string SteamRoot { get; } | ||||||||||
|
|
||||||||||
| /// <summary> | ||||||||||
| /// An absolute path to a Proton version's <c>proton</c> Python script. | ||||||||||
| /// </summary> | ||||||||||
| public string ProtonExecutable { get; } | ||||||||||
|
|
||||||||||
| /// <summary> | ||||||||||
| /// An absolute path to the <c>compatdata/</c> directory in a Proton prefix. | ||||||||||
| /// </summary> | ||||||||||
|
|
||||||||||
| public string CompatDataPath { get; } | ||||||||||
|
|
||||||||||
| /// <summary> | ||||||||||
| /// An absolute path to <c>common/VRChat/</c> | ||||||||||
| /// </summary> | ||||||||||
| public string VrcInstallRoot { get; } | ||||||||||
|
|
||||||||||
| /// <summary> | ||||||||||
| /// Determine the environment taking preferences and locations from Steam and Unity into account by which we should | ||||||||||
| /// launch VRChat in offline Build and Test mode. | ||||||||||
| /// </summary> | ||||||||||
| /// <param name="protonPath"> | ||||||||||
| /// The value of the Unity EditorPref for custom Proton version, or <see langword="null" /> if it is unset. | ||||||||||
| /// </param> | ||||||||||
| /// <returns></returns> | ||||||||||
| public static LaunchConfiguration? Resolve(string? protonPath) | ||||||||||
| { | ||||||||||
| // SteamRoot | ||||||||||
| var steamRoot = SteamLocator.FindSteamRoot(); | ||||||||||
| if (steamRoot == null || !SteamLocator.IsValidSteamRoot(steamRoot)) | ||||||||||
|
Comment on lines
+57
to
+58
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Why check twice here? |
||||||||||
| { | ||||||||||
| Debug.LogError($"Couldn't find Steam root: \"{steamRoot}\""); | ||||||||||
| return null; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // ProtonExecutable | ||||||||||
| if (!ProtonLocator.IsValidCompatToolPath(protonPath)) | ||||||||||
| { | ||||||||||
| Debug.Log("Custom Proton install path is unset or invalid, will auto-detect."); | ||||||||||
|
|
||||||||||
| protonPath = ProtonLocator.GetSteamVdfCompatTool(steamRoot, VrcLocator.VrcAppId); | ||||||||||
| if (!ProtonLocator.IsValidCompatToolPath(protonPath)) | ||||||||||
| { | ||||||||||
| Debug.LogError($"Couldn't find compat tool used for VRChat: {protonPath}"); | ||||||||||
| return null; | ||||||||||
| } | ||||||||||
| } | ||||||||||
|
|
||||||||||
| if (protonPath == null) | ||||||||||
| return null; // satisfies compiler | ||||||||||
|
|
||||||||||
| var protonExecutable = Path.Combine(protonPath, "proton"); | ||||||||||
|
|
||||||||||
| // CompatDataPath | ||||||||||
| var compatDataPath = VrcLocator.GetCompatDataPath(); | ||||||||||
| if (compatDataPath == null || !VrcLocator.IsValidCompatDataPath(compatDataPath)) | ||||||||||
| { | ||||||||||
| Debug.LogError($"Could not find VRChat's compatdata: \"{compatDataPath}\""); | ||||||||||
| return null; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // VrcGameRoot | ||||||||||
| var vrcExePath = VrcLocator.GetVrcInstallPath(); | ||||||||||
| if (vrcExePath == null || !VrcLocator.IsValidVrcInstallPath(vrcExePath)) | ||||||||||
| { | ||||||||||
| Debug.LogError($"Could not locate VRChat.exe: \"{vrcExePath}\""); | ||||||||||
| return null; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| var vrcInstallRoot = Path.GetDirectoryName(vrcExePath); | ||||||||||
| // ReSharper disable once InvertIf | ||||||||||
| if (vrcInstallRoot == null || !Directory.Exists(vrcInstallRoot)) | ||||||||||
| { | ||||||||||
| Debug.LogError($"Could not locate VRChat's install directory: \"{vrcInstallRoot}\""); | ||||||||||
| return null; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // TODON'T: read toolmanifest.vdf "commandline"? nah | ||||||||||
| return new LaunchConfiguration(steamRoot, protonExecutable, compatDataPath, vrcInstallRoot); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| public void DebugPrint() | ||||||||||
| { | ||||||||||
| Debug.Log($"Steam root: \"{SteamRoot}\""); | ||||||||||
| Debug.Log($"Proton executable: \"{ProtonExecutable}\""); | ||||||||||
| Debug.Log($"Compat data path: \"{CompatDataPath}\""); | ||||||||||
| Debug.Log($"VRChat install directory: \"{VrcInstallRoot}\""); | ||||||||||
| } | ||||||||||
| } | ||||||||||
| } | ||||||||||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is Bumping the VRC SDK version required here?
If not, I'd prefer it not be bumped
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I bumped VRCSDK in response to this, but I misremembered the details:
https://ask.vrchat.com/t/developer-update-23-october-2025/46983#p-85382-a-reminder-to-upgrade-to-the-latest-sdk-3
VRC now requires SDK 3.9.0+ on newly-uploaded avatars, but I forgot the conditional words on that: newly, uploaded, and avatars. But beyond that, I have not tested old SDKs so the patches may or may not apply. If you'd like, we can un-bump, and cross that bridge when we come to it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer not to bump if the patches work for older versions of the SDK, but bumping to 3.9.0 would be okay