Move many mods to Deprecated folder, fix spelling

This commit is contained in:
NotAKidoS 2025-04-03 02:57:35 -05:00
parent 5e822cec8d
commit 0042590aa6
539 changed files with 7475 additions and 3120 deletions

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<Reference Include="BTKUILib">
<HintPath>$(MsBuildThisFileDirectory)\..\.ManagedLibs\BTKUILib.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
</Project>

View file

@ -0,0 +1,26 @@
using ABI_RC.Core;
using ABI_RC.Systems.InputManagement;
using HarmonyLib;
using NativeVRModeSwitchManager = ABI_RC.Systems.VRModeSwitch.VRModeSwitchManager;
namespace NAK.DesktopVRSwitch.HarmonyPatches;
internal class CVRInputManagerPatches
{
[HarmonyPostfix]
[HarmonyPatch(typeof(CVRInputManager), "OnPostVRModeSwitch")]
private static void Postfix_CVRInputManager_OnPostVRModeSwitch(bool inVr, UnityEngine.Camera playerCamera)
{
RootLogic.Instance.ToggleMouse(inVr);
}
}
internal class VRModeSwitchManagerPatches
{
[HarmonyPrefix]
[HarmonyPatch(typeof(NativeVRModeSwitchManager), "StartSwitchInternal")]
private static void Postfix_CVRInputManager_OnPostVRModeSwitch()
{
CVRInputManager.Instance.inputEnabled = false;
}
}

View file

@ -0,0 +1,63 @@
using BTKUILib;
using BTKUILib.UIObjects;
using System.Runtime.CompilerServices;
namespace NAK.DesktopVRSwitch.Integrations;
public static class BTKUIAddon
{
#region Initialization
[MethodImpl(MethodImplOptions.NoInlining)]
public static void Initialize()
{
// Add mod to the Misc Menu
Page miscPage = QuickMenuAPI.MiscTabPage;
Category vrSwitchMiscCategory = miscPage.AddCategory(ModSettings.SettingsCategory);
vrSwitchMiscCategory.AddButton("Switch VR Mode", "", "Switch to Desktop/VR.").OnPress +=
() =>
{
QuickMenuAPI.ShowConfirm(
title: "Switch VR Mode",
content: "Are you sure you want to switch to Desktop/VR?",
onYes: () => VRModeSwitchManager.Instance.AttemptSwitch()
);
};
SetupSwitchConfigurationPage(ref vrSwitchMiscCategory);
}
#endregion
#region Pages Setup
private static void SetupSwitchConfigurationPage(ref Category parentCategory)
{
Page vrSwitchPage = parentCategory.AddPage("DesktopVRSwitch Settings", "", "Configure the settings for DesktopVRSwitch.", ModSettings.SettingsCategory);
vrSwitchPage.MenuTitle = "DesktopVRSwitch Settings";
Category vrSwitchCategory = vrSwitchPage.AddCategory(vrSwitchPage.MenuTitle);
AddMelonToggle(ref vrSwitchCategory, ModSettings.EntryEnterCalibrationOnSwitch);
AddMelonToggle(ref vrSwitchCategory, ModSettings.EntryUseTransitionOnSwitch);
AddMelonToggle(ref vrSwitchCategory, ModSettings.EntrySwitchToDesktopOnExit);
}
#endregion
#region Melon Pref Helpers
private static void AddMelonToggle(ref Category category, MelonLoader.MelonPreferences_Entry<bool> entry)
{
category.AddToggle(entry.DisplayName, entry.Description, entry.Value).OnValueUpdated += b => entry.Value = b;
}
// private static void AddMelonSlider(ref Page page, MelonLoader.MelonPreferences_Entry<float> entry, float min, float max, int decimalPlaces = 2)
// {
// page.AddSlider(entry.DisplayName, entry.Description, entry.Value, min, max, decimalPlaces).OnValueUpdated += f => entry.Value = f;
// }
#endregion
}

View file

@ -0,0 +1,26 @@
using System;
using MelonLoader;
namespace NAK.DesktopVRSwitch;
public class DesktopVRSwitch : MelonMod
{
public override void OnInitializeMelon()
{
ApplyPatches(typeof(HarmonyPatches.CVRInputManagerPatches));
ApplyPatches(typeof(HarmonyPatches.VRModeSwitchManagerPatches));
}
private void ApplyPatches(Type type)
{
try
{
HarmonyInstance.PatchAll(type);
}
catch (Exception e)
{
LoggerInstance.Msg($"Failed while patching {type.Name}!");
LoggerInstance.Error(e);
}
}
}

View file

@ -0,0 +1,20 @@
using MelonLoader;
namespace NAK.DesktopVRSwitch;
public static class ModSettings
{
internal const string SettingsCategory = nameof(DesktopVRSwitch);
private static readonly MelonPreferences_Category Category =
MelonPreferences.CreateCategory(SettingsCategory);
public static readonly MelonPreferences_Entry<bool> EntryEnterCalibrationOnSwitch =
Category.CreateEntry("Enter Calibration on Switch", true, description: "Should you automatically be placed into calibration after switch if FBT is available? Overridden by Save Calibration IK setting.");
public static readonly MelonPreferences_Entry<bool> EntryUseTransitionOnSwitch =
Category.CreateEntry("Use Transition on Switch", true, description: "Should the world transition play on VRMode switch?");
public static readonly MelonPreferences_Entry<bool> EntrySwitchToDesktopOnExit =
Category.CreateEntry("Switch to Desktop on SteamVR Exit", false, description: "Should the game switch to Desktop when SteamVR quits?");
}

View file

@ -0,0 +1,29 @@
using ABI_RC.Systems.IK;
using ABI_RC.Systems.IK.TrackingModules;
using ABI_RC.Systems.InputManagement;
using ABI_RC.Systems.InputManagement.XR.Modules;
namespace NAK.DesktopVRSwitch.Patches;
internal static class SteamVRNullReferencePatch
{
public static void DestroySteamVRInstancesImmediate()
{
// set to null so input manager doesnt attempt to access it
if (CVRInputManager._moduleXR != null)
{
if (CVRInputManager._moduleXR._leftModule != null)
if (CVRInputManager._moduleXR._leftModule is CVRXRModule_SteamVR leftModule) leftModule._steamVr = null;
if (CVRInputManager._moduleXR._rightModule != null)
if (CVRInputManager._moduleXR._rightModule is CVRXRModule_SteamVR rightModule) rightModule._steamVr = null;
}
if (IKSystem.Instance == null)
return;
// set to null so tracking module doesnt attempt to access it
foreach (TrackingModule module in IKSystem.Instance._trackingModules)
if (module is SteamVRTrackingModule steamVRModule)
steamVRModule._steamVr = null;
}
}

View file

@ -0,0 +1,103 @@
using ABI_RC.Core.Base;
using ABI_RC.Core.Player;
using ABI_RC.Core.Savior;
using Aura2API;
using BeautifyEffect;
using UnityEngine;
using UnityEngine.AzureSky;
using UnityEngine.Rendering.PostProcessing;
namespace NAK.DesktopVRSwitch.Patches;
internal static class ReferenceCameraPatch
{
public static void OnWorldLoad()
{
Camera activeCamera = (MetaPort.Instance.isUsingVr ? PlayerSetup.Instance.vrCamera : PlayerSetup.Instance.desktopCamera).GetComponent<Camera>();
Camera inactiveCamera = (MetaPort.Instance.isUsingVr ? PlayerSetup.Instance.desktopCamera : PlayerSetup.Instance.vrCamera).GetComponent<Camera>();
CopyToInactiveCam(activeCamera, inactiveCamera);
}
private static void CopyToInactiveCam(Camera activeCam, Camera inactiveCam)
{
if (inactiveCam == null || activeCam == null)
return;
DesktopVRSwitch.Logger.Msg("Copying active camera settings & components to inactive camera.");
// Copy basic settings
inactiveCam.farClipPlane = activeCam.farClipPlane;
inactiveCam.nearClipPlane = activeCam.nearClipPlane;
inactiveCam.depthTextureMode = activeCam.depthTextureMode;
// We cant copy this because we set it to 0 with ThirdPerson
var cullingMask = inactiveCam.cullingMask;
cullingMask &= -32769;
cullingMask |= 256;
cullingMask |= 512;
cullingMask |= 32;
cullingMask &= -4097;
cullingMask |= 1024;
cullingMask |= 8192;
inactiveCam.cullingMask = cullingMask;
// Copy post processing if added
PostProcessLayer ppLayerActiveCam = activeCam.GetComponent<PostProcessLayer>();
PostProcessLayer ppLayerInactiveCam = inactiveCam.AddComponentIfMissing<PostProcessLayer>();
if (ppLayerActiveCam != null && ppLayerInactiveCam != null)
{
ppLayerInactiveCam.enabled = ppLayerActiveCam.enabled;
ppLayerInactiveCam.volumeLayer = ppLayerActiveCam.volumeLayer;
}
// Copy Aura camera settings
AuraCamera auraActiveCam = activeCam.GetComponent<AuraCamera>();
AuraCamera auraInactiveCam = inactiveCam.AddComponentIfMissing<AuraCamera>();
if (auraActiveCam != null && auraInactiveCam != null)
{
auraInactiveCam.enabled = auraActiveCam.enabled;
auraInactiveCam.frustumSettings = auraActiveCam.frustumSettings;
}
else
{
auraInactiveCam.enabled = false;
}
// Copy Flare layer settings
FlareLayer flareActiveCam = activeCam.GetComponent<FlareLayer>();
FlareLayer flareInactiveCam = inactiveCam.AddComponentIfMissing<FlareLayer>();
if (flareActiveCam != null && flareInactiveCam != null)
{
flareInactiveCam.enabled = flareActiveCam.enabled;
}
else
{
flareInactiveCam.enabled = false;
}
// Copy Azure Fog Scattering settings
AzureFogScattering azureFogActiveCam = activeCam.GetComponent<AzureFogScattering>();
AzureFogScattering azureFogInactiveCam = inactiveCam.AddComponentIfMissing<AzureFogScattering>();
if (azureFogActiveCam != null && azureFogInactiveCam != null)
{
azureFogInactiveCam.fogScatteringMaterial = azureFogActiveCam.fogScatteringMaterial;
}
else
{
UnityEngine.Object.Destroy(inactiveCam.GetComponent<AzureFogScattering>());
}
// Copy Beautify settings
Beautify beautifyActiveCam = activeCam.GetComponent<Beautify>();
Beautify beautifyInactiveCam = inactiveCam.AddComponentIfMissing<Beautify>();
if (beautifyActiveCam != null && beautifyInactiveCam != null)
{
beautifyInactiveCam.quality = beautifyActiveCam.quality;
beautifyInactiveCam.profile = beautifyActiveCam.profile;
}
else
{
UnityEngine.Object.Destroy(inactiveCam.gameObject.GetComponent<Beautify>());
}
}
}

View file

@ -0,0 +1,32 @@
using MelonLoader;
using NAK.DesktopVRSwitch.Properties;
using System.Reflection;
[assembly: AssemblyVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyTitle(nameof(NAK.DesktopVRSwitch))]
[assembly: AssemblyCompany(AssemblyInfoParams.Author)]
[assembly: AssemblyProduct(nameof(NAK.DesktopVRSwitch))]
[assembly: MelonInfo(
typeof(NAK.DesktopVRSwitch.DesktopVRSwitch),
nameof(NAK.DesktopVRSwitch),
AssemblyInfoParams.Version,
AssemblyInfoParams.Author,
downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/DesktopVRSwitch"
)]
[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")]
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
[assembly: MelonColor(255, 52, 152, 219)]
[assembly: MelonAuthorColor(255, 114, 17, 25)]
[assembly: HarmonyDontPatchAll]
namespace NAK.DesktopVRSwitch.Properties;
internal static class AssemblyInfoParams
{
public const string Version = "4.4.2";
public const string Author = "NotAKidoS";
}

View file

@ -0,0 +1,28 @@
# DesktopVRSwitch
Allows you to switch between Desktop and VR with a keybind.
Press Control + F6 to switch. SteamVR will automatically start if it isn't already running.
https://github.com/NotAKidoS/NAK_CVR_Mods/assets/37721153/3f7d6747-720a-45a0-aa51-af7ae677d15e
---
## Note:
* This mod will likely cause issues with other mods that are not built for or expect VRMode changes during runtime.
* This mod may not account for every feature or system present in ChilloutVR, and may easily break on any update.
## Notable Features:
* Option to automatically switch to Desktop when exiting ChilloutVR from the SteamVR overlay.
* Allows for switching HMD, controllers, restarting SteamVR, etc by switching to Desktop and back again.
* Option to switch directly into calibrated Full Body Tracking if Save Calibration in IK Settings is enabled.
---
Here is the block of text where I tell you this mod is not affiliated with, or endorsed by ABI.
https://documentation.abinteractive.net/official/legal/tos/#7-modding-our-games
> This mod is an independent creation not affiliated with, supported by, or approved by Alpha Blend Interactive.
> Use of this mod is done so at the user's own risk and the creator cannot be held responsible for any issues arising from its use.
> To the best of my knowledge, I have adhered to the Modding Guidelines established by Alpha Blend Interactive.

View file

@ -0,0 +1,26 @@
using ABI_RC.Core.EventSystem;
using ABI_RC.Core.Player;
using ABI_RC.Core.Savior;
using UnityEngine;
namespace NAK.DesktopVRSwitch;
internal static class Utils
{
internal static GameObject GetPlayerCameraObject(bool intoVR)
{
return intoVR ? PlayerSetup.Instance.vrCamera : PlayerSetup.Instance.desktopCamera;
}
internal static void ClearLocalAvatar()
{
DesktopVRSwitch.Logger.Msg("Clearing local avatar.");
PlayerSetup.Instance.ClearAvatar();
}
internal static void ReloadLocalAvatar()
{
DesktopVRSwitch.Logger.Msg("Attempting to reload current local avatar from GUID.");
AssetManagement.Instance.LoadLocalAvatar(MetaPort.Instance.currentAvatarGuid);
}
}

View file

@ -0,0 +1,42 @@
using System.Collections;
using UnityEngine;
namespace NAK.DesktopVRSwitch;
internal class VRModeSwitchDebugger : MonoBehaviour
{
private Coroutine _switchCoroutine;
private WaitForSeconds _sleep;
private void OnEnable()
{
if (_switchCoroutine != null)
return;
_switchCoroutine = StartCoroutine(SwitchLoop());
_sleep = new WaitForSeconds(2f);
}
private void OnDisable()
{
if (_switchCoroutine == null)
return;
StopCoroutine(_switchCoroutine);
_switchCoroutine = null;
_sleep = null;
}
private IEnumerator SwitchLoop()
{
while (true)
{
if (!VRModeSwitchManager.Instance.SwitchInProgress)
{
VRModeSwitchManager.Instance.AttemptSwitch();
yield return _sleep;
}
yield return null;
}
}
}

View file

@ -0,0 +1,222 @@
using ABI_RC.Systems.UI;
using System.Collections;
using ABI_RC.Core.EventSystem;
using ABI_RC.Core.IO;
using ABI_RC.Core.Player;
using ABI_RC.Core.Savior;
using ABI_RC.Core.UI;
using ABI_RC.Systems.GameEventSystem;
using ABI_RC.Systems.InputManagement;
using ABI_RC.Systems.VRModeSwitch;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.XR.Management;
namespace NAK.DesktopVRSwitch;
public class VRModeSwitchManager : MonoBehaviour
{
public static VRModeSwitchManager Instance { get; private set; }
#region Variables
// Settings
public bool DesktopVRSwitchEnabled;
public bool UseWorldTransition = true;
public bool ReloadLocalAvatar = true;
public bool SwitchInProgress { get; private set; }
#endregion
#region Unity Methods
private void Awake()
{
if (Instance != null)
{
DestroyImmediate(this);
return;
}
Instance = this;
DesktopVRSwitchEnabled = MetaPort.Instance.settings.GetSettingsBool("ExperimentalDesktopVRSwitch");
MetaPort.Instance.settings.settingBoolChanged.AddListener(OnSettingsBoolChanged);
}
private void Update()
{
if (!DesktopVRSwitchEnabled)
return;
if (SwitchInProgress)
return;
if (CVRInputManager.Instance.switchMode)
AttemptSwitch();
}
#endregion
#region Public Methods
public void AttemptSwitch()
{
if (SwitchInProgress)
return;
// dont allow switching during world transfer, itll explode violently
if (CVRObjectLoader.Instance.IsLoadingWorldToJoin())
return;
StartCoroutine(StartSwitchInternal());
}
#endregion
#region Private Methods
private void OnSettingsBoolChanged(string settingName, bool val)
{
if (settingName == "ExperimentalDesktopVRSwitch")
DesktopVRSwitchEnabled = val;
}
private IEnumerator StartSwitchInternal()
{
if (SwitchInProgress)
yield break;
NotifyOnPreSwitch();
bool useWorldTransition = UseWorldTransition;
SwitchInProgress = true;
yield return null;
if (useWorldTransition)
yield return StartCoroutine(StartTransition());
var wasInXr = IsInXR();
InvokeOnPreSwitch(!wasInXr);
// Note: this assumes that wasInXr has been correctly set earlier in your method.
Task xrTask = wasInXr ? XRHandler.StopXR() : XRHandler.StartXR();
// Wait for the task to complete. This makes the coroutine wait here until the above thread is done.
yield return new WaitUntil(() => xrTask.IsCompleted || xrTask.IsFaulted);
// Check task status, handle any fault that occurred during the execution of the task.
if (xrTask.IsFaulted)
{
// Log and/or handle exceptions that occurred within the task.
Exception innerException = xrTask.Exception.InnerException; // The Exception that caused the Task to enter the faulted state
MelonLoader.MelonLogger.Error("Encountered an error while executing the XR task: " + innerException.Message);
// Handle the exception appropriately.
}
if (wasInXr != IsInXR())
{
ReloadAvatar();
InvokeOnPostSwitch(!wasInXr);
}
else
{
NotifyOnFailedSwitch();
InvokeOnFailedSwitch(!wasInXr);
}
if (useWorldTransition)
yield return StartCoroutine(ContinueTransition());
SwitchInProgress = false;
}
private void ReloadAvatar()
{
if (!ReloadLocalAvatar)
return;
// TODO: Is there a better way to reload only locally?
PlayerSetup.Instance.ClearAvatar();
AssetManagement.Instance.LoadLocalAvatar(MetaPort.Instance.currentAvatarGuid);
}
private bool IsInXR()
{
return XRGeneralSettings.Instance.Manager.activeLoader != null;
}
private UnityEngine.Camera GetPlayerCamera(bool isVr)
{
return (isVr ? PlayerSetup.Instance.vrCamera : PlayerSetup.Instance.desktopCamera)
.GetComponent<UnityEngine.Camera>();
}
#endregion
#region Transition Coroutines
private IEnumerator StartTransition()
{
if (WorldTransitionSystem.Instance == null) yield break;
WorldTransitionSystem.Instance.StartTransition();
yield return new WaitForSeconds(WorldTransitionSystem.Instance.CurrentInLength + 0.25f);
}
private IEnumerator ContinueTransition()
{
if (WorldTransitionSystem.Instance == null) yield break;
WorldTransitionSystem.Instance.ContinueTransition();
yield return new WaitForSeconds(WorldTransitionSystem.Instance.CurrentInLength + 0.25f);
}
#endregion
#region Event Handling
private void InvokeOnPreSwitch(bool isUsingVr)
{
UnityEngine.Camera playerCamera = GetPlayerCamera(isUsingVr);
ABI_RC.Systems.VRModeSwitch.VRModeSwitchManager.OnPreSwitchInternal?.Invoke(isUsingVr, playerCamera);
CVRGameEventSystem.VRModeSwitch.OnPreSwitch?.Invoke(isUsingVr, playerCamera);
}
private void InvokeOnPostSwitch(bool isUsingVr)
{
UnityEngine.Camera playerCamera = GetPlayerCamera(isUsingVr);
ABI_RC.Systems.VRModeSwitch.VRModeSwitchManager.OnPostSwitchInternal?.Invoke(isUsingVr, playerCamera);
CVRGameEventSystem.VRModeSwitch.OnPostSwitch?.Invoke(isUsingVr, playerCamera);
}
private void InvokeOnFailedSwitch(bool isUsingVr)
{
UnityEngine.Camera playerCamera = GetPlayerCamera(isUsingVr);
ABI_RC.Systems.VRModeSwitch.VRModeSwitchManager.OnFailedSwitchInternal?.Invoke(isUsingVr, playerCamera);
CVRGameEventSystem.VRModeSwitch.OnFailedSwitch?.Invoke(isUsingVr, playerCamera);
}
#endregion
#region Notifications
private void NotifyOnPreSwitch()
{
CohtmlHud.Instance.ViewDropTextImmediate("(Local) Client",
"VR Mode Switch", "Switching to " + (IsInXR() ? "Desktop" : "VR") + " Mode");
}
private void NotifyOnFailedSwitch()
{
// TODO: Can we get reason it failed?
CohtmlHud.Instance.ViewDropTextImmediate("(Local) Client",
"VR Mode Switch", "Switch failed");
}
#endregion
}

View file

@ -0,0 +1,23 @@
using ABI_RC.Core.Savior;
namespace NAK.DesktopVRSwitch.VRModeTrackers;
public class CVRGestureRecognizerTracker : VRModeTracker
{
public override void TrackerInit()
{
VRModeSwitchManager.OnPostVRModeSwitch += OnPostSwitch;
}
public override void TrackerDestroy()
{
VRModeSwitchManager.OnPostVRModeSwitch -= OnPostSwitch;
}
private void OnPostSwitch(object sender, VRModeSwitchManager.VRModeEventArgs args)
{
DesktopVRSwitch.Logger.Msg("Updating CVRGestureRecognizer _camera to active camera.");
CVRGestureRecognizer.Instance._camera = args.PlayerCamera;
}
}

View file

@ -0,0 +1,62 @@
using ABI_RC.Core;
using ABI_RC.Systems.InputManagement;
using ABI_RC.Systems.InputManagement.InputModules;
using ABI_RC.Systems.InputManagement.XR.Modules;
namespace NAK.DesktopVRSwitch.VRModeTrackers;
public class CVRInputManagerTracker : VRModeTracker
{
public override void TrackerInit()
{
VRModeSwitchManager.OnPostVRModeSwitch += OnPostSwitch;
}
public override void TrackerDestroy()
{
VRModeSwitchManager.OnPostVRModeSwitch -= OnPostSwitch;
}
private void OnPostSwitch(object sender, VRModeSwitchManager.VRModeEventArgs args)
{
DesktopVRSwitch.Logger.Msg("Resetting CVRInputManager inputs.");
CVRInputManager.Instance.inputEnabled = true;
// IM CRYING
//CVRInputManager.Instance.reload = true;
// TODO: MOVE THIS TO DIFFERENT TRACKER
RootLogic.Instance.ToggleMouse(args.IsUsingVr);
//just in case
CVRInputManager.Instance.textInputFocused = false;
//sometimes head can get stuck, so just in case
CVRInputManager.Instance.independentHeadToggle = false;
//just nice to load into desktop with idle gesture
CVRInputManager.Instance.gestureLeft = 0f;
CVRInputManager.Instance.gestureLeftRaw = 0f;
CVRInputManager.Instance.gestureRight = 0f;
CVRInputManager.Instance.gestureRightRaw = 0f;
//turn off finger tracking input
CVRInputManager.Instance.individualFingerTracking = false;
//add input module if you started in desktop
if (CVRInputManager._moduleXR == null)
{
CVRInputManager.Instance.AddInputModule(CVRInputManager._moduleXR = new CVRInputModule_XR());
}
else
{
// set to null so input manager doesnt attempt to access it
if (CVRInputManager._moduleXR._leftModule != null)
if (CVRInputManager._moduleXR._leftModule is CVRXRModule_SteamVR leftModule) leftModule._steamVr = null;
if (CVRInputManager._moduleXR._rightModule != null)
if (CVRInputManager._moduleXR._rightModule is CVRXRModule_SteamVR rightModule) rightModule._steamVr = null;
}
//enable xr input or whatnot
CVRInputManager._moduleXR.InputEnabled = args.IsUsingVr;
}
}

View file

@ -0,0 +1,33 @@
using ABI.CCK.Components;
using UnityEngine;
namespace NAK.DesktopVRSwitch.VRModeTrackers;
public class CVRPickupObjectTracker : MonoBehaviour
{
internal CVRPickupObject _pickupObject;
internal Transform _storedGripOrigin;
private void Start()
{
VRModeSwitchManager.OnPostVRModeSwitch += OnPostSwitch;
}
private void OnDestroy()
{
VRModeSwitchManager.OnPostVRModeSwitch -= OnPostSwitch;
}
public void OnPostSwitch(object sender, VRModeSwitchManager.VRModeEventArgs args)
{
if (_pickupObject == null)
return;
// Drop the object if it is being held locally
if (_pickupObject._controllerRay != null)
_pickupObject._controllerRay.DropObject(true);
// Swap the grip origins
(_storedGripOrigin, _pickupObject.gripOrigin) = (_pickupObject.gripOrigin, _storedGripOrigin);
}
}

View file

@ -0,0 +1,48 @@
using ABI.CCK.Components;
using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.Player;
using UnityEngine;
namespace NAK.DesktopVRSwitch.VRModeTrackers;
public class CVRWorldTracker : VRModeTracker
{
public override void TrackerInit()
{
VRModeSwitchManager.OnPostVRModeSwitch += OnPostSwitch;
}
public override void TrackerDestroy()
{
VRModeSwitchManager.OnPostVRModeSwitch -= OnPostSwitch;
}
private void OnPostSwitch(object sender, VRModeSwitchManager.VRModeEventArgs args)
{
DesktopVRSwitch.Logger.Msg("Configuring CVRWorld. Updating PostProcessing & DesktopCameraController FOV settings.");
// some post processing settings aren't used in VR
CVRWorld.Instance.UpdatePostProcessing();
UpdateCVRDesktopCameraController();
}
private void UpdateCVRDesktopCameraController()
{
// Just making sure- Starting in VR will not call Start() as rig is disabled
if (CVR_DesktopCameraController._cam == null)
CVR_DesktopCameraController._cam = PlayerSetup.Instance.desktopCamera.GetComponent<Camera>();
CVR_DesktopCameraController.defaultFov = Mathf.Clamp(CVRWorld.Instance.fov, 60f, 120f);
CVR_DesktopCameraController.zoomFov = CVR_DesktopCameraController.defaultFov * 0.5f;
CVR_DesktopCameraController.enableZoom = CVRWorld.Instance.enableZoom;
// must happen after PlayerSetupTracker
CVR_DesktopCameraController.UpdateFov();
CVR_MenuManager.Instance.coreData.instance.current_game_rule_no_zoom = !CVRWorld.Instance.enableZoom;
// UICamera has a script that copies the FOV from the desktop cam.
// Toggling the cameras on/off resets the aspect ratio,
// so when rigs switch, that is already handled.
}
}

View file

@ -0,0 +1,24 @@
using ABI_RC.Core.InteractionSystem;
namespace NAK.DesktopVRSwitch.VRModeTrackers;
public class CVR_InteractableManagerTracker : VRModeTracker
{
public override void TrackerInit()
{
VRModeSwitchManager.OnPostVRModeSwitch += OnPostSwitch;
}
public override void TrackerDestroy()
{
VRModeSwitchManager.OnPostVRModeSwitch -= OnPostSwitch;
}
private void OnPostSwitch(object sender, VRModeSwitchManager.VRModeEventArgs args)
{
DesktopVRSwitch.Logger.Msg($"Enabling CVR_InteractableManager enableInteractions.");
// ?
CVR_InteractableManager.enableInteractions = true;
}
}

View file

@ -0,0 +1,34 @@
using ABI_RC.Core.InteractionSystem;
using UnityEngine;
namespace NAK.DesktopVRSwitch.VRModeTrackers;
public class CVR_MenuManagerTracker : VRModeTracker
{
public override void TrackerInit()
{
VRModeSwitchManager.OnPreVRModeSwitch += OnPreSwitch;
VRModeSwitchManager.OnPostVRModeSwitch += OnPostSwitch;
}
public override void TrackerDestroy()
{
VRModeSwitchManager.OnPreVRModeSwitch -= OnPreSwitch;
VRModeSwitchManager.OnPostVRModeSwitch -= OnPostSwitch;
}
private void OnPreSwitch(object sender, VRModeSwitchManager.VRModeEventArgs args)
{
DesktopVRSwitch.Logger.Msg("Closing CVR_MenuManager - Quick Menu.");
CVR_MenuManager.Instance.ToggleQuickMenu(false);
}
private void OnPostSwitch(object sender, VRModeSwitchManager.VRModeEventArgs args)
{
DesktopVRSwitch.Logger.Msg("Updating CVR_Menu_Data core data.");
CVR_MenuManager.Instance.coreData.core.inVr = args.IsUsingVr;
CVR_MenuManager.Instance.quickMenu.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
}
}

View file

@ -0,0 +1,25 @@
using ABI_RC.Core.Util.Object_Behaviour;
using UnityEngine;
namespace NAK.DesktopVRSwitch.VRModeTrackers;
public class CameraFacingObjectTracker : MonoBehaviour
{
private CameraFacingObject _cameraFacingObject;
private void Start()
{
_cameraFacingObject = GetComponent<CameraFacingObject>();
VRModeSwitchManager.OnPostVRModeSwitch += OnPostSwitch;
}
private void OnDestroy()
{
VRModeSwitchManager.OnPostVRModeSwitch -= OnPostSwitch;
}
public void OnPostSwitch(object sender, VRModeSwitchManager.VRModeEventArgs args)
{
_cameraFacingObject.m_Camera = args.PlayerCamera;
}
}

View file

@ -0,0 +1,21 @@
using ABI_RC.Core.Savior;
namespace NAK.DesktopVRSwitch.VRModeTrackers;
public class CheckVRTracker : VRModeTracker
{
public override void TrackerInit()
{
VRModeSwitchManager.OnPostVRModeSwitch += OnPostSwitch;
}
public override void TrackerDestroy()
{
VRModeSwitchManager.OnPostVRModeSwitch -= OnPostSwitch;
}
private void OnPostSwitch(object sender, VRModeSwitchManager.VRModeEventArgs args)
{
CheckVR.Instance.hasVrDeviceLoaded = args.IsUsingVr;
}
}

View file

@ -0,0 +1,34 @@
using ABI_RC.Core;
using ABI_RC.Core.UI;
using UnityEngine;
namespace NAK.DesktopVRSwitch.VRModeTrackers;
public class CohtmlHudTracker : VRModeTracker
{
public override void TrackerInit()
{
VRModeSwitchManager.OnPostVRModeSwitch += OnPostSwitch;
}
public override void TrackerDestroy()
{
VRModeSwitchManager.OnPostVRModeSwitch -= OnPostSwitch;
}
private void OnPostSwitch(object sender, VRModeSwitchManager.VRModeEventArgs args)
{
DesktopVRSwitch.Logger.Msg("Configuring new hud affinity for CohtmlHud.");
CohtmlHud.Instance.gameObject.transform.parent = Utils.GetPlayerCameraObject(args.IsUsingVr).transform;
// This handles rotation and position
CVRTools.ConfigureHudAffinity();
CohtmlHud.Instance.gameObject.transform.localScale = new Vector3(1.2f, 1f, 1.2f);
// required to set menu vr mode (why is it offset in js?)
CohtmlHud.uiCoreGameData.isVr = args.IsUsingVr;
if (CohtmlHud.Instance._isReady)
CohtmlHud.Instance.hudView.View.TriggerEvent("updateCoreGameVars", CohtmlHud.uiCoreGameData);
}
}

View file

@ -0,0 +1,24 @@
using ABI_RC.Core.Player;
namespace NAK.DesktopVRSwitch.VRModeTrackers;
public class HudOperationsTracker : VRModeTracker
{
public override void TrackerInit()
{
VRModeSwitchManager.OnPostVRModeSwitch += OnPostSwitch;
}
public override void TrackerDestroy()
{
VRModeSwitchManager.OnPostVRModeSwitch -= OnPostSwitch;
}
private void OnPostSwitch(object sender, VRModeSwitchManager.VRModeEventArgs args)
{
DesktopVRSwitch.Logger.Msg("Switching HudOperations worldLoadingItem & worldLoadStatus.");
HudOperations.Instance.worldLoadingItem = args.IsUsingVr ? HudOperations.Instance.worldLoadingItemVr : HudOperations.Instance.worldLoadingItemDesktop;
HudOperations.Instance.worldLoadStatus = args.IsUsingVr ? HudOperations.Instance.worldLoadStatusVr : HudOperations.Instance.worldLoadStatusDesktop;
}
}

View file

@ -0,0 +1,81 @@
using ABI_RC.Systems.IK;
using ABI_RC.Systems.IK.SubSystems;
using ABI_RC.Systems.IK.TrackingModules;
namespace NAK.DesktopVRSwitch.VRModeTrackers;
public class IKSystemTracker : VRModeTracker
{
public override void TrackerInit()
{
VRModeSwitchManager.OnPreVRModeSwitch += OnPreSwitch;
VRModeSwitchManager.OnFailVRModeSwitch += OnFailedSwitch;
VRModeSwitchManager.OnPostVRModeSwitch += OnPostSwitch;
}
public override void TrackerDestroy()
{
VRModeSwitchManager.OnPreVRModeSwitch -= OnPreSwitch;
VRModeSwitchManager.OnFailVRModeSwitch -= OnFailedSwitch;
VRModeSwitchManager.OnPostVRModeSwitch -= OnPostSwitch;
}
private void OnPreSwitch(object sender, VRModeSwitchManager.VRModeEventArgs args)
{
BodySystem.TrackingEnabled = false;
BodySystem.TrackingPositionWeight = 0f;
BodySystem.TrackingLocomotionEnabled = false;
if (IKSystem.vrik != null)
IKSystem.vrik.enabled = false;
}
private void OnFailedSwitch(object sender, VRModeSwitchManager.VRModeEventArgs args)
{
BodySystem.TrackingEnabled = true;
BodySystem.TrackingPositionWeight = 1f;
BodySystem.TrackingLocomotionEnabled = true;
if (IKSystem.vrik != null)
IKSystem.vrik.enabled = true;
}
private void OnPostSwitch(object sender, VRModeSwitchManager.VRModeEventArgs args)
{
if (IKSystem.vrik != null)
UnityEngine.Object.DestroyImmediate(IKSystem.vrik);
// Make sure you are fully tracking
BodySystem.TrackingEnabled = true;
BodySystem.TrackingPositionWeight = 1f;
BodySystem.TrackingLocomotionEnabled = true;
BodySystem.isCalibratedAsFullBody = false;
BodySystem.isCalibrating = false;
BodySystem.isRecalibration = false;
// Make it so you don't instantly end up in FBT from Desktop
IKSystem.firstAvatarLoaded = ModSettings.EntryEnterCalibrationOnSwitch.Value;
// Turn off finger tracking just in case the user switched controllers
IKSystem.Instance.FingerSystem.controlActive = false;
SetupSteamVRTrackingModule(args.IsUsingVr);
}
private void SetupSteamVRTrackingModule(bool enableVR)
{
SteamVRTrackingModule openVRModule = IKSystem.Instance._trackingModules.OfType<SteamVRTrackingModule>().FirstOrDefault();
if (openVRModule != null)
{
if (enableVR)
openVRModule.ModuleStart();
else
openVRModule.ModuleDestroy();
}
else if (enableVR)
{
IKSystem.Instance.AddTrackingModule(new SteamVRTrackingModule());
}
}
}

View file

@ -0,0 +1,85 @@
using ABI_RC.Core.Savior;
using UnityEngine;
using UnityEngine.XR;
using Valve.VR;
namespace NAK.DesktopVRSwitch.VRModeTrackers;
public class MetaPortTracker : VRModeTracker
{
public override void TrackerInit()
{
VRModeSwitchManager.OnPostVRModeSwitch += OnPostSwitch;
}
public override void TrackerDestroy()
{
VRModeSwitchManager.OnPostVRModeSwitch -= OnPostSwitch;
}
private void OnPostSwitch(object sender, VRModeSwitchManager.VRModeEventArgs args)
{
DesktopVRSwitch.Logger.Msg($"Setting MetaPort isUsingVr to {args.IsUsingVr}.");
// Main thing most of the game checks for if using VR
MetaPort.Instance.isUsingVr = args.IsUsingVr;
// replace
UpdateRichPresence();
ResetSteamVROverrides(args.IsUsingVr);
}
private void UpdateRichPresence()
{
// Hacky way of updating rich presence
if (MetaPort.Instance.settings.GetSettingsBool("ImplementationRichPresenceDiscordEnabled", true))
{
DesktopVRSwitch.Logger.Msg("Forcing Discord Rich Presence update.");
MetaPort.Instance.settings.SetSettingsBool("ImplementationRichPresenceDiscordEnabled", false);
MetaPort.Instance.settings.SetSettingsBool("ImplementationRichPresenceDiscordEnabled", true);
}
if (MetaPort.Instance.settings.GetSettingsBool("ImplementationRichPresenceSteamEnabled", true))
{
DesktopVRSwitch.Logger.Msg("Forcing Steam Rich Presence update.");
MetaPort.Instance.settings.SetSettingsBool("ImplementationRichPresenceSteamEnabled", false);
MetaPort.Instance.settings.SetSettingsBool("ImplementationRichPresenceSteamEnabled", true);
}
}
private void ResetSteamVROverrides(bool intoVR)
{
if (intoVR)
{
// Testing
//XRSettings.gameViewRenderMode = DesktopVRSwitch.EntryRenderVRGameView.Value ? GameViewRenderMode.LeftEye : GameViewRenderMode.None;
XRSettings.eyeTextureResolutionScale = 1; // unsure if will cause issues with FSR?
SteamVR_Settings.instance.pauseGameWhenDashboardVisible = false;
if (MetaPort.Instance.settings.GetSettingsBool("InteractionTobiiEyeTracking", false))
MetaPort.Instance.TobiiXrInitializer.Initialize();
return;
}
// Reset physics time to Desktop default
Time.fixedDeltaTime = 0.02f;
// Reset queued frames
QualitySettings.maxQueuedFrames = 2;
// Reset framerate target
int graphicsFramerateTarget = MetaPort.Instance.settings.GetSettingInt("GraphicsFramerateTarget", 0);
ABI_RC.Core.CVRTools.SetFramerateTarget(graphicsFramerateTarget);
// Reset VSync setting
bool graphicsVSync = MetaPort.Instance.settings.GetSettingsBool("GraphicsVSync", false);
QualitySettings.vSyncCount = graphicsVSync ? 1 : 0;
// Reset anti-aliasing
int graphicsMsaaLevel = MetaPort.Instance.settings.GetSettingInt("GraphicsMsaaLevel", 0);
QualitySettings.antiAliasing = graphicsMsaaLevel;
// Won't do anything if not already running
MetaPort.Instance.TobiiXrInitializer.DeInitialize();
}
}

View file

@ -0,0 +1,73 @@
using ABI_RC.Systems.MovementSystem;
using System.Collections;
using UnityEngine;
namespace NAK.DesktopVRSwitch.VRModeTrackers;
public class MovementSystemTracker : VRModeTracker
{
private Vector3 preSwitchWorldPosition;
private Quaternion preSwitchWorldRotation;
public override void TrackerInit()
{
VRModeSwitchManager.OnPreVRModeSwitch += OnPreSwitch;
VRModeSwitchManager.OnFailVRModeSwitch += OnFailedSwitch;
VRModeSwitchManager.OnPostVRModeSwitch += OnPostSwitch;
}
public override void TrackerDestroy()
{
VRModeSwitchManager.OnPreVRModeSwitch -= OnPreSwitch;
VRModeSwitchManager.OnFailVRModeSwitch -= OnFailedSwitch;
VRModeSwitchManager.OnPostVRModeSwitch -= OnPostSwitch;
}
private void OnPreSwitch(object sender, VRModeSwitchManager.VRModeEventArgs args)
{
DesktopVRSwitch.Logger.Msg("Storing player world position and rotation.");
var pivotTransform = MovementSystem.Instance.rotationPivot.transform;
preSwitchWorldPosition = pivotTransform.position;
preSwitchWorldPosition.y = MovementSystem.Instance.transform.position.y;
preSwitchWorldRotation = pivotTransform.rotation;
MovementSystem.Instance.ChangeCrouch(false);
MovementSystem.Instance.ChangeProne(false);
MovementSystem.Instance.SetImmobilized(true);
}
private void OnFailedSwitch(object sender, VRModeSwitchManager.VRModeEventArgs args)
{
DesktopVRSwitch.Logger.Msg("Resetting MovementSystem mobility.");
MovementSystem.Instance.SetImmobilized(false);
}
private void OnPostSwitch(object sender, VRModeSwitchManager.VRModeEventArgs args)
{
// Lazy
MelonLoader.MelonCoroutines.Start(TeleportFrameAfter(args.IsUsingVr));
}
private IEnumerator TeleportFrameAfter(bool intoVR)
{
yield return null; // need to wait a frame
DesktopVRSwitch.Logger.Msg("Resetting MovementSystem mobility and applying stored position and rotation.");
MovementSystem.Instance.rotationPivot = Utils.GetPlayerCameraObject(intoVR).transform;
MovementSystem.Instance.TeleportToPosRot(preSwitchWorldPosition, preSwitchWorldRotation);
if (!intoVR)
MovementSystem.Instance.UpdateColliderCenter(MovementSystem.Instance.transform.position);
MovementSystem.Instance.ChangeCrouch(false);
MovementSystem.Instance.ChangeProne(false);
MovementSystem.Instance.SetImmobilized(false);
MovementSystem.Instance.independentHeadTurn = false;
MovementSystem.Instance.independentHeadToggle = false;
}
}

View file

@ -0,0 +1,24 @@
using ABI_RC.Core.Player;
namespace NAK.DesktopVRSwitch.VRModeTrackers;
public class PlayerSetupTracker : VRModeTracker
{
public override void TrackerInit()
{
VRModeSwitchManager.OnPostVRModeSwitch += OnPostSwitch;
}
public override void TrackerDestroy()
{
VRModeSwitchManager.OnPostVRModeSwitch -= OnPostSwitch;
}
private void OnPostSwitch(object sender, VRModeSwitchManager.VRModeEventArgs args)
{
DesktopVRSwitch.Logger.Msg("Switching active PlayerSetup camera rigs. Updating Desktop camera FOV.");
PlayerSetup.Instance.desktopCameraRig.SetActive(!args.IsUsingVr);
PlayerSetup.Instance.vrCameraRig.SetActive(args.IsUsingVr);
}
}

View file

@ -0,0 +1,25 @@
using ABI_RC.Systems.Camera;
namespace NAK.DesktopVRSwitch.VRModeTrackers;
public class PortableCameraTracker : VRModeTracker
{
public override void TrackerInit()
{
VRModeSwitchManager.OnPostVRModeSwitch += OnPostSwitch;
}
public override void TrackerDestroy()
{
VRModeSwitchManager.OnPostVRModeSwitch -= OnPostSwitch;
}
private void OnPostSwitch(object sender, VRModeSwitchManager.VRModeEventArgs args)
{
DesktopVRSwitch.Logger.Msg("Forcing PortableCamera canvas mirroring off.");
// Tell the game we are in mirror mode so it'll disable it (if enabled)
PortableCamera.Instance.mode = MirroringMode.Mirror;
PortableCamera.Instance.ChangeMirroring();
}
}

View file

@ -0,0 +1,7 @@
namespace NAK.DesktopVRSwitch.VRModeTrackers;
public abstract class VRModeTracker
{
public virtual void TrackerInit() { }
public virtual void TrackerDestroy() { }
}

View file

@ -0,0 +1,23 @@
using ABI_RC.Core.InteractionSystem;
namespace NAK.DesktopVRSwitch.VRModeTrackers;
public class ViewManagerTracker : VRModeTracker
{
public override void TrackerInit()
{
VRModeSwitchManager.OnPreVRModeSwitch += OnPreSwitch;
}
public override void TrackerDestroy()
{
VRModeSwitchManager.OnPreVRModeSwitch -= OnPreSwitch;
}
private void OnPreSwitch(object sender, VRModeSwitchManager.VRModeEventArgs args)
{
DesktopVRSwitch.Logger.Msg("Closing ViewManager - Main Menu.");
ViewManager.Instance.UiStateToggle(false);
}
}

View file

@ -0,0 +1,85 @@
#if !PLATFORM_ANDROID
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using ABI_RC.Core.Savior;
using Unity.XR.OpenVR;
using UnityEngine;
using UnityEngine.XR.Management;
using UnityEngine.XR.OpenXR;
using Valve.VR;
using Object = UnityEngine.Object;
namespace ABI_RC.Systems.VRModeSwitch
{
internal static class XRHandler
{
private static async Task InitializeXRLoader()
{
EnsureXRLoader();
XRGeneralSettings.Instance.Manager.InitializeLoaderSync();
await Task.Yield();
}
internal static async Task StartXR()
{
await InitializeXRLoader();
if (XRGeneralSettings.Instance.Manager.activeLoader != null)
XRGeneralSettings.Instance.Manager.StartSubsystems();
else
await StopXR(); // assuming StopXR is now an async method.
// Await a delay or equivalent method to wait for a frame.
await Task.Yield(); // This line is to simulate "waiting for the next frame" in an async way.
}
internal static Task StopXR()
{
if (!XRGeneralSettings.Instance.Manager.isInitializationComplete)
return Task.CompletedTask;
// Forces SteamVR to reinitialize SteamVR_Input next switch
SteamVR_ActionSet_Manager.DisableAllActionSets();
SteamVR_Input.initialized = false;
// Remove SteamVR behaviour & render
Object.DestroyImmediate(SteamVR_Behaviour.instance.gameObject);
SteamVR.enabled = false; // disposes SteamVR
// Disable UnityXR
XRGeneralSettings.Instance.Manager.StopSubsystems();
XRGeneralSettings.Instance.Manager.DeinitializeLoader();
return Task.CompletedTask;
// If we need to wait for something specific (like a frame), we use Task.Delay or equivalent.
// In this case, it seems like you don't need to wait after stopping XR,
// so we don't necessarily need an equivalent to 'yield return null' here.
}
private static void EnsureXRLoader()
{
Type selectedLoaderType = !CheckVR.Instance.forceOpenXr ? typeof(OpenVRLoader) : typeof(OpenXRLoader);
// dont do anything if we already have the loader selected
if (XRGeneralSettings.Instance.Manager.activeLoaders.Count > 0
&& XRGeneralSettings.Instance.Manager.activeLoaders[0].GetType() == selectedLoaderType)
return;
XRLoader newLoaderInstance = (XRLoader)ScriptableObject.CreateInstance(selectedLoaderType);
FieldInfo field = typeof(XRManagerSettings).GetField("m_Loaders",
BindingFlags.Instance | BindingFlags.NonPublic);
if (field == null) return;
// destroy old loaders, set the new laoder
// this should not happen normally, but changing loader during runtime sounds funni
if (field.GetValue(XRGeneralSettings.Instance.Manager) is List<XRLoader> currentLoaders)
foreach (XRLoader loader in currentLoaders.Where(loader => loader != null)) Object.Destroy(loader);
field.SetValue(XRGeneralSettings.Instance.Manager, new List<XRLoader> { newLoaderInstance });
}
}
}
#endif

View file

@ -0,0 +1,23 @@
{
"_id": 103,
"name": "DesktopVRSwitch",
"modversion": "4.4.2",
"gameversion": "2023r172",
"loaderversion": "0.6.1",
"modtype": "Mod",
"author": "NotAKidoS",
"description": "Allows you to switch between Desktop and VR with a keybind.\n**Press Control + F6 to switch or optionally through BTKUI.**\n\nWhile this mod is a nice convenience feature to have access to, not every ChilloutVR system or mod is built to support it. I cannot possibly cover every edge case or mitigate issues with every mod. **Use at your own discretion.**",
"searchtags": [
"desktop",
"vr",
"switch",
"restart"
],
"requirements": [
"BTKUILib"
],
"downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r21/DesktopVRSwitch.dll",
"sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/DesktopVRSwitch/",
"changelog": "- Fixes for 2023r172.\n- Mouse is now force unlocked once entering VR mode as QOL.",
"embedcolor": "3498db"
}