diff --git a/DesktopVRSwitch/DesktopVRSwitch.csproj b/DesktopVRSwitch/DesktopVRSwitch.csproj index de3e0bb..adb74e4 100644 --- a/DesktopVRSwitch/DesktopVRSwitch.csproj +++ b/DesktopVRSwitch/DesktopVRSwitch.csproj @@ -18,6 +18,9 @@ ..\..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp-firstpass.dll + + ..\..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Aura2_Core.dll + ..\..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll @@ -33,6 +36,12 @@ C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\SteamVR.dll + + ..\..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\ChilloutVR\Mods\UIExpansionKit.dll + + + ..\..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Unity.Postprocessing.Runtime.dll + ..\..\..\DakyModsCVR\ManagedLibs\Unity.TextMeshPro.dll diff --git a/DesktopVRSwitch/DesktopVRSwitchHelper.cs b/DesktopVRSwitch/DesktopVRSwitchHelper.cs new file mode 100644 index 0000000..72c873c --- /dev/null +++ b/DesktopVRSwitch/DesktopVRSwitchHelper.cs @@ -0,0 +1,416 @@ +using ABI_RC.Core; +using ABI_RC.Core.EventSystem; +using ABI_RC.Core.InteractionSystem; +using ABI_RC.Core.Player; +using ABI_RC.Core.Savior; +using ABI_RC.Core.UI; +using ABI_RC.Core.Util.Object_Behaviour; +using ABI_RC.Systems.Camera; +using ABI_RC.Systems.IK; +using ABI_RC.Systems.IK.SubSystems; +using ABI_RC.Systems.IK.TrackingModules; +using ABI_RC.Systems.MovementSystem; +using ABI_RC.Core.Networking; +using DesktopVRSwitch.Patches; +using HarmonyLib; +using MelonLoader; +using System.Collections; +using UnityEngine; +using UnityEngine.XR; +using Valve.VR; +using Object = UnityEngine.Object; + +namespace DesktopVRSwitch; + +public class DesktopVRSwitchHelper : MonoBehaviour +{ + public static DesktopVRSwitchHelper Instance; + + //settings + public bool SettingTimedErrorCatch = true; + public float SettingTimedErrorTimer = 10f; + + //internal shit + internal static bool isAttemptingSwitch = false; + internal static float timedSwitch = 0f; + internal static bool CurrentMode; + internal static Vector3 avatarWorldPos; + internal static Quaternion avatarWorldRot; + + public void SwitchMode(bool isTimedSwitch = false) + { + if (isAttemptingSwitch) return; + + isAttemptingSwitch = true; + MelonCoroutines.Start(AttemptPlatformSwitch()); + + //how long we wait until we assume an error occured + if (isTimedSwitch) + timedSwitch = Time.time + SettingTimedErrorTimer; + } + + public void Start() + { + Instance = this; + } + + public void Update() + { + // assuming CVRInputManager.switchMode button was originally for desktop/vr switching before being left to do literally nothing in rootlogic + if (Input.GetKeyDown(KeyCode.F6) && (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)) && !isAttemptingSwitch) + { + SwitchMode(true); + } + + if (!isAttemptingSwitch) return; + + //catch if coroutine just decided to not finish... which happens? + if (Time.time > timedSwitch) + { + MelonLogger.Error("Timer exceeded. Something is wrong and coroutine failed partway."); + isAttemptingSwitch = false; + if (SettingTimedErrorCatch) + SwitchMode(); + } + } + + //disables VRIK if it was on the current avatar during switch + //absolutely bruteforcing the stupid vr playspace offset issue + public void LateUpdate() + { + if (!isAttemptingSwitch) return; + BodySystem.TrackingEnabled = false; + BodySystem.TrackingPositionWeight = 0f; + PlayerSetup.Instance._avatar.transform.position = avatarWorldPos; + PlayerSetup.Instance._avatar.transform.rotation = avatarWorldRot; + MovementSystem.Instance.TeleportToPosRot(avatarWorldPos, avatarWorldRot, false); + MovementSystem.Instance.UpdateColliderCenter(avatarWorldPos); + } + + internal static IEnumerator AttemptPlatformSwitch(bool forceMode = false) + { + //forceMode will attempt to backtrack to last working mode (if you dont like the mess, fix it yourself thx) + CurrentMode = forceMode ? CurrentMode : MetaPort.Instance.isUsingVr; + bool VRMode = forceMode ? CurrentMode : !CurrentMode; + + CloseMenuElements(VRMode); + ToggleInputInteractions(false); + DisableMirrorCanvas(); + + //store current player position/rotation to correct VR/Desktop offsets + avatarWorldPos = PlayerSetup.Instance._avatar.transform.position; + avatarWorldRot = PlayerSetup.Instance._avatar.transform.rotation; + + //exit all movement states + MovementSystem.Instance.ChangeCrouch(false); + MovementSystem.Instance.ChangeProne(false); + MovementSystem.Instance.canMove = false; + MovementSystem.Instance.canRot = false; + + //load SteamVR + InitializeSteamVR(VRMode); + + yield + return new WaitForEndOfFrame(); + + SetMetaPort(VRMode); + + //reset rich presence + if (MetaPort.Instance.settings.GetSettingsBool("ImplementationRichPresenceDiscordEnabled", true)) + { + MetaPort.Instance.settings.SetSettingsBool("ImplementationRichPresenceDiscordEnabled", false); + MetaPort.Instance.settings.SetSettingsBool("ImplementationRichPresenceDiscordEnabled", true); + } + if (MetaPort.Instance.settings.GetSettingsBool("ImplementationRichPresenceSteamEnabled", true)) + { + MetaPort.Instance.settings.SetSettingsBool("ImplementationRichPresenceSteamEnabled", false); + MetaPort.Instance.settings.SetSettingsBool("ImplementationRichPresenceSteamEnabled", true); + } + + yield + return new WaitForEndOfFrame(); + + SwitchActiveCameraRigs(VRMode); + UpdateCameraFacingObject(); + RepositionCohtmlHud(VRMode); + UpdateHudOperations(VRMode); + SwitchPickupOrigins(); + + yield + return new WaitForEndOfFrame(); + + //needs to come after SetMovementSystem + UpdateGestureReconizerCam(); + + ResetCVRInputManager(); + + //gonna try doing this last + DisposeSteamVR(VRMode); + + ToggleInputInteractions(true); + + //reload current avatar + AssetManagement.Instance.LoadLocalAvatar(MetaPort.Instance.currentAvatarGuid); + + yield return new WaitUntil(() => !PlayerSetup.Instance.avatarIsLoading); + + isAttemptingSwitch = false; + + BodySystem.TrackingEnabled = true; + BodySystem.TrackingPositionWeight = 1f; + MovementSystem.Instance.canMove = true; + MovementSystem.Instance.canRot = true; + + if (!VRMode) + //collision center is set to match headpos in VR, but desktop doesnt reset it + MovementSystem.Instance.UpdateColliderCenter(PlayerSetup.Instance._avatar.transform.position); + + yield + return new WaitForEndOfFrame(); + + //one last teleport to correct VR offset + MovementSystem.Instance.TeleportToPosRot(avatarWorldPos, avatarWorldRot, false); + + yield + return null; + } + + //shitton of try catch below + + internal static void InitializeSteamVR(bool isVR) + { + if (isVR) + { + //force SteamVR to fully initialize, this does all and more than what i did with LoadDevice() + SteamVR.Initialize(true); + + //Just to make sure. Game does this natively when entering VR. + SteamVR_Settings.instance.pauseGameWhenDashboardVisible = false; + + //TODO: something needs to be done to reinitialize SteamVR_Input or SteamVR_Actions + //If you restart SteamVR after already have been in VRMode, the steamvr action handles break + //ive tried: + //SteamVR_Input.Initialize(true) + //SteamVR_Actions.PreInitialize() + //Destroying SteamVR_Settings on DesktopMode + //Destroying SteamVR_Behavior on DesktopMode + //Destroying SteamVR_Render on DesktopMode + //Combinations of all of these.. + //Its probably really simple, but I just cannot figure out how. + } + } + + internal static void DisposeSteamVR(bool isVR) + { + if (!isVR) + { + //force SteamVR to let go of Chillout + XRSettings.LoadDeviceByName("None"); + XRSettings.enabled = false; + + //destroy [SteamVR] gameobject as next SteamVR.Initialize creates a new one + Object.Destroy(SteamVR_Behaviour.instance.gameObject); + + //what even does this do that is actually important? + SteamVR.SafeDispose(); + } + } + + // shouldn't be that important, right? + internal static void CloseMenuElements(bool isVR) + { + if (ViewManager.Instance != null) + { + MelonLogger.Msg("Closed MainMenu Instance."); + ViewManager.Instance.UiStateToggle(false); + } + else + { + MelonLogger.Msg("MainMenu Instance not found!!!"); + } + if (ViewManager.Instance != null) + { + MelonLogger.Msg("Closed QuickMenu Instance."); + CVR_MenuManager.Instance.ToggleQuickMenu(false); + } + else + { + MelonLogger.Msg("QuickMenu Instance not found!!!"); + } + } + + internal static void ToggleInputInteractions(bool toggle) + { + //disable input during switch + try + { + MelonLogger.Msg($"Toggling input & interactions to " + toggle); + CVRInputManager.Instance.inputEnabled = toggle; + CVR_InteractableManager.enableInteractions = toggle; + } + catch + { + MelonLogger.Error("Toggling input & interactions failed. Is something invalid?"); + MelonLogger.Msg("CVRInputManager.Instance: " + CVRInputManager.Instance); + MelonLogger.Msg("CVR_InteractableManager: " + CVR_InteractableManager.enableInteractions); + throw; + } + } + internal static void SetMetaPort(bool isVR) + { + try + { + MelonLogger.Msg($"Set MetaPort isUsingVr to {isVR}."); + MetaPort.Instance.isUsingVr = isVR; + } + catch + { + MelonLogger.Error("Setting MetaPort isUsingVr failed. Is MetaPort.Instance invalid?"); + MelonLogger.Msg("MetaPort.Instance: " + MetaPort.Instance); + throw; + } + } + + internal static void SwitchActiveCameraRigs(bool isVR) + { + try + { + MelonLogger.Msg("Switched active camera rigs."); + PlayerSetup.Instance.desktopCameraRig.SetActive(!isVR); + PlayerSetup.Instance.vrCameraRig.SetActive(isVR); + } + catch + { + MelonLogger.Error("Error switching active cameras. Are the camera rigs invalid?"); + MelonLogger.Msg("PlayerSetup.Instance.desktopCameraRig: " + PlayerSetup.Instance.desktopCameraRig); + MelonLogger.Msg("PlayerSetup.Instance.vrCameraRig: " + PlayerSetup.Instance.vrCameraRig); + throw; + } + } + + internal static void RepositionCohtmlHud(bool isVR) + { + try + { + MelonLogger.Msg("Parented CohtmlHud to active camera."); + CohtmlHud.Instance.gameObject.transform.parent = isVR ? PlayerSetup.Instance.vrCamera.transform : PlayerSetup.Instance.desktopCamera.transform; + + //sets hud position, rotation, ~~and scale~~ based on MetaPort isUsingVr + CVRTools.ConfigureHudAffinity(); + CohtmlHud.Instance.gameObject.transform.localScale = new Vector3(1.2f, 1f, 1.2f); + } + catch + { + MelonLogger.Error("Error parenting CohtmlHud to active camera. Is CohtmlHud.Instance invalid?"); + MelonLogger.Msg("CohtmlHud.Instance: " + CohtmlHud.Instance); + throw; + } + } + + internal static void ResetCVRInputManager() + { + try + { + MelonLogger.Msg("Enabling CVRInputManager inputEnabled & disabling blockedByUi!"); + //CVRInputManager.Instance.reload = true; + //just in case + CVRInputManager.Instance.inputEnabled = true; + CVRInputManager.Instance.blockedByUi = 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; + } + catch + { + MelonLogger.Error("CVRInputManager reload failed. Is CVRInputManager.Instance invalid?"); + MelonLogger.Msg("CVRInputManager.Instance: " + CVRInputManager.Instance); + throw; + } + } + + //every nameplate canvas uses CameraFacingObject :stare: + internal static void UpdateCameraFacingObject() + { + try + { + MelonLogger.Msg("Updating all CameraFacingObject scripts to face new camera. (this fixes nameplates)"); + CameraFacingObject[] camfaceobjs = Object.FindObjectsOfType(); + + for (int i = 0; i < camfaceobjs.Count(); i++) + { + camfaceobjs[i].m_Camera = PlayerSetup.Instance.GetActiveCamera().GetComponent(); + } + } + catch + { + MelonLogger.Error("Error updating CameraFacingObject objects! Nameplates will be wonk..."); + throw; + } + } + + internal static void UpdateHudOperations(bool isVR) + { + try + { + MelonLogger.Msg("Set HudOperations worldLoadingItem and worldLoadStatus to their respective Desktop/Vr parent."); + HudOperations.Instance.worldLoadingItem = isVR ? HudOperations.Instance.worldLoadingItemVr : HudOperations.Instance.worldLoadingItemDesktop; + HudOperations.Instance.worldLoadStatus = isVR ? HudOperations.Instance.worldLoadStatusVr : HudOperations.Instance.worldLoadStatusDesktop; + } + catch + { + MelonLogger.Error("Error updating HudOperations LoadingItem & LoadStatus!"); + throw; + } + } + + internal static void DisableMirrorCanvas() + { + try + { + //tell the game we are in mirror mode so itll disable it (if enabled) + PortableCamera.Instance.mode = MirroringMode.Mirror; + PortableCamera.Instance.ChangeMirroring(); + } + catch + { + MelonLogger.Error("Error updating CVRGestureRecognizer camera!"); + throw; + } + } + + internal static void UpdateGestureReconizerCam() + { + try + { + MelonLogger.Msg("Set GestureReconizerCam camera to active camera."); + Traverse.Create(CVRGestureRecognizer.Instance).Field("_camera").SetValue(PlayerSetup.Instance.GetActiveCamera().GetComponent()); + } + catch + { + MelonLogger.Error("Error updating CVRGestureRecognizer camera!"); + throw; + } + } + + internal static void SwitchPickupOrigins() + { + try + { + MelonLogger.Msg("Switched pickup origins."); + CVRPickupObjectTracker[] pickups = Object.FindObjectsOfType(); + for (int i = 0; i < pickups.Count(); i++) + { + pickups[i].OnSwitch(); + } + } + catch + { + MelonLogger.Error("Error switching pickup origins!"); + throw; + } + } +} \ No newline at end of file diff --git a/DesktopVRSwitch/Main.cs b/DesktopVRSwitch/Main.cs index c8e1910..9d69fba 100644 --- a/DesktopVRSwitch/Main.cs +++ b/DesktopVRSwitch/Main.cs @@ -26,360 +26,50 @@ namespace DesktopVRSwitch; public class DesktopVRSwitch : MelonMod { - private static MelonPreferences_Category m_categoryDesktopVRSwitch; - private static MelonPreferences_Entry m_entryTimedErrorCatch; + internal const string SettingsCategory = "DesktopVRSwitch"; + internal static MelonPreferences_Category m_categoryDesktopVRSwitch; + internal static MelonPreferences_Entry m_entryTimedErrorCatch; + internal static MelonPreferences_Entry m_entryTimedErrorTimer; + public override void OnInitializeMelon() { - m_categoryDesktopVRSwitch = MelonPreferences.CreateCategory(nameof(DesktopVRSwitch)); - m_entryTimedErrorCatch = m_categoryDesktopVRSwitch.CreateEntry("Timed Error Catch", true, description: "Attempt to switch back if an error is found after 10 seconds."); + m_categoryDesktopVRSwitch = MelonPreferences.CreateCategory(SettingsCategory); + m_entryTimedErrorCatch = m_categoryDesktopVRSwitch.CreateEntry("Timed Error Catch", true, description: "Attempt to switch back if an error is found after n seconds."); + m_entryTimedErrorTimer = m_categoryDesktopVRSwitch.CreateEntry("Timed Error Timer", 10f, description: "Amount of seconds to wait before assuming there was an error."); + m_categoryDesktopVRSwitch.SaveToFile(false); + foreach (var setting in m_categoryDesktopVRSwitch.Entries) + { + setting.OnEntryValueChangedUntyped.Subscribe(OnUpdateSettings); + } + + //UIExpansionKit addon + if (MelonMod.RegisteredMelons.Any(it => it.Info.Name == "UI Expansion Kit")) + { + MelonLogger.Msg("Initializing UIExpansionKit support."); + UiExtensionsAddon.Init(); + } + MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer()); } - private static bool isAttemptingSwitch = false; - private static float timedSwitch = 0f; - private static bool CurrentMode; - private static Vector3 avatarPos; - private static Quaternion avatarRot; - - public override void OnUpdate() + System.Collections.IEnumerator WaitForLocalPlayer() { - // assuming CVRInputManager.switchMode button was originally for desktop/vr switching before being left to do literally nothing in rootlogic - if (Input.GetKeyDown(KeyCode.F6) && (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)) && !isAttemptingSwitch) - { - //start attempt - isAttemptingSwitch = true; - MelonCoroutines.Start(AttemptPlatformSwitch()); + while (PlayerSetup.Instance == null) + yield return null; - //how long we wait until we assume an error occured - if (m_entryTimedErrorCatch.Value) - timedSwitch = Time.time + 10f; - } + PlayerSetup.Instance.gameObject.AddComponent(); - //catch if coroutine just decided to not finish... which happens? - if (isAttemptingSwitch && Time.time > timedSwitch) - { - MelonLogger.Error("Timer exceeded. Something is wrong and coroutine failed partway."); - MelonCoroutines.Start(AttemptPlatformSwitch(true)); - } + while (DesktopVRSwitchHelper.Instance == null) + yield return null; - //correct player position while switching - if (isAttemptingSwitch && !MovementSystem.Instance.canMove) - { - MovementSystem.Instance.TeleportToPosRot(avatarPos, avatarRot, false); - MovementSystem.Instance.UpdateColliderCenter(avatarPos); - } + UpdateAllSettings(); } - private static IEnumerator AttemptPlatformSwitch(bool forceMode = false) + private void OnUpdateSettings(object arg1, object arg2) => UpdateAllSettings(); + private void UpdateAllSettings() { - //forceMode will attempt to backtrack to last working mode (if you dont like the mess, fix it yourself thx) - CurrentMode = forceMode ? CurrentMode : MetaPort.Instance.isUsingVr; - bool VRMode = forceMode ? CurrentMode : !CurrentMode; - - //store current player position/rotation to correct VR/Desktop offsets - avatarPos = PlayerSetup.Instance._avatar.transform.position; - avatarRot = PlayerSetup.Instance._avatar.transform.rotation; - - //prevent player from any movement while transitioning - MovementSystem.Instance.canMove = false; - BodySystem.TrackingEnabled = false; - - //load SteamVR - InitializeSteamVR(VRMode); - - CloseMenuElements(VRMode); - DisableMirrorCanvas(); - - yield - return new WaitForEndOfFrame(); - - SetMetaPort(VRMode); - - yield - return new WaitForEndOfFrame(); - - SetPlayerSetup(VRMode); - SwitchActiveCameraRigs(VRMode); - UpdateCameraFacingObject(); - RepositionCohtmlHud(VRMode); - UpdateHudOperations(VRMode); - SwitchPickupOrigins(); - - yield - return new WaitForEndOfFrame(); - - //needs to come after SetMovementSystem - UpdateGestureReconizerCam(); - - yield - return new WaitForEndOfFrame(); - - //right here is the fucker most likely to break - ReloadCVRInputManager(); - - //some menus have 0.5s wait(), so to be safe - yield - return new WaitForSeconds(0.5f); - - //reload current avatar - AssetManagement.Instance.LoadLocalAvatar(MetaPort.Instance.currentAvatarGuid); - - yield - return new WaitForSeconds(2f); - - if (!VRMode) - //collision center is set to match headpos in VR, but desktop doesnt reset it - MovementSystem.Instance.UpdateColliderCenter(PlayerSetup.Instance._avatar.transform.position); - - //gonna try doing this last - DisposeSteamVR(VRMode); - - MovementSystem.Instance.canMove = true; - BodySystem.TrackingEnabled = true; - - yield - return null; - isAttemptingSwitch = false; - } - - //shitton of try catch below - - private static void InitializeSteamVR(bool isVR) - { - if (isVR) - { - //force SteamVR to fully initialize, this does all and more than what i did with LoadDevice() - SteamVR.Initialize(true); - - //Just to make sure. Game does this natively when entering VR. - SteamVR_Settings.instance.pauseGameWhenDashboardVisible = false; - - //TODO: something needs to be done to reinitialize SteamVR_Input or SteamVR_Actions - //If you restart SteamVR after already have been in VRMode, the steamvr action handles break - //ive tried: - //SteamVR_Input.Initialize(true) - //SteamVR_Actions.PreInitialize() - //Destroying SteamVR_Settings on DesktopMode - //Destroying SteamVR_Behavior on DesktopMode - //Destroying SteamVR_Render on DesktopMode - //Combinations of all of these.. - //Its probably really simple, but I just cannot figure out how. - } - } - - private static void DisposeSteamVR(bool isVR) - { - if (!isVR) - { - //force SteamVR to let go of Chillout - XRSettings.LoadDeviceByName("None"); - XRSettings.enabled = false; - - //destroy [SteamVR] gameobject as next SteamVR.Initialize creates a new one - Object.Destroy(SteamVR_Behaviour.instance.gameObject); - - //what even does this do that is actually important? - SteamVR.SafeDispose(); - } - } - - // shouldn't be that important, right? - private static void CloseMenuElements(bool isVR) - { - if (ViewManager.Instance != null) - { - MelonLogger.Msg("Closed MainMenu Instance."); - ViewManager.Instance.UiStateToggle(false); - } - else - { - MelonLogger.Msg("MainMenu Instance not found!!!"); - } - if (ViewManager.Instance != null) - { - MelonLogger.Msg("Closed QuickMenu Instance."); - CVR_MenuManager.Instance.ToggleQuickMenu(false); - } - else - { - MelonLogger.Msg("QuickMenu Instance not found!!!"); - } - - //disable input during switch - CVRInputManager.Instance.inputEnabled = false; - } - - private static void SetMetaPort(bool isVR) - { - try - { - MelonLogger.Msg($"Set MetaPort isUsingVr to {isVR}."); - MetaPort.Instance.isUsingVr = isVR; - } - catch (Exception) - { - MelonLogger.Error("Setting MetaPort isUsingVr failed. Is MetaPort.Instance invalid?"); - MelonLogger.Msg("MetaPort.Instance: " + MetaPort.Instance); - throw; - } - } - - private static void SetPlayerSetup(bool isVR) - { - try - { - MelonLogger.Msg($"Set PlayerSetup instance to {isVR}."); - PlayerSetup.Instance._inVr = isVR; - } - catch (Exception) - { - MelonLogger.Error("Setting PlayerSetup _inVr failed. Is PlayerSetup.Instance invalid?"); - MelonLogger.Msg("PlayerSetup.Instance: " + PlayerSetup.Instance); - throw; - } - } - - private static void SwitchActiveCameraRigs(bool isVR) - { - try - { - MelonLogger.Msg("Switched active camera rigs."); - PlayerSetup.Instance.desktopCameraRig.SetActive(!isVR); - PlayerSetup.Instance.vrCameraRig.SetActive(isVR); - } - catch (Exception) - { - MelonLogger.Error("Error switching active cameras. Are the camera rigs invalid?"); - MelonLogger.Msg("PlayerSetup.Instance.desktopCameraRig: " + PlayerSetup.Instance.desktopCameraRig); - MelonLogger.Msg("PlayerSetup.Instance.vrCameraRig: " + PlayerSetup.Instance.vrCameraRig); - throw; - } - } - - private static void RepositionCohtmlHud(bool isVR) - { - try - { - MelonLogger.Msg("Parented CohtmlHud to active camera."); - CohtmlHud.Instance.gameObject.transform.parent = isVR ? PlayerSetup.Instance.vrCamera.transform : PlayerSetup.Instance.desktopCamera.transform; - - //sets hud position, rotation, ~~and scale~~ based on MetaPort isUsingVr - CVRTools.ConfigureHudAffinity(); - CohtmlHud.Instance.gameObject.transform.localScale = new Vector3(1.2f, 1f, 1.2f); - } - catch (Exception) - { - MelonLogger.Error("Error parenting CohtmlHud to active camera. Is CohtmlHud.Instance invalid?"); - MelonLogger.Msg("CohtmlHud.Instance: " + CohtmlHud.Instance); - throw; - } - } - - private static void ReloadCVRInputManager() - { - try - { - MelonLogger.Msg("Set CVRInputManager reload to True. Input should reload next frame..."); - CVRInputManager.Instance.reload = true; - //just in case - CVRInputManager.Instance.inputEnabled = true; - CVRInputManager.Instance.blockedByUi = 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; - } - catch (Exception) - { - MelonLogger.Error("CVRInputManager reload failed. Is CVRInputManager.Instance invalid?"); - MelonLogger.Msg("CVRInputManager.Instance: " + CVRInputManager.Instance); - throw; - } - } - - //every nameplate canvas uses CameraFacingObject :stare: - private static void UpdateCameraFacingObject() - { - try - { - MelonLogger.Msg("Updating all CameraFacingObject scripts to face new camera. (this fixes nameplates)"); - CameraFacingObject[] camfaceobjs = Object.FindObjectsOfType(); - - for (int i = 0; i < camfaceobjs.Count(); i++) - { - camfaceobjs[i].m_Camera = PlayerSetup.Instance.GetActiveCamera().GetComponent(); - } - } - catch (Exception) - { - MelonLogger.Error("Error updating CameraFacingObject objects! Nameplates will be wonk..."); - throw; - } - } - - private static void UpdateHudOperations(bool isVR) - { - try - { - MelonLogger.Msg("Set HudOperations worldLoadingItem and worldLoadStatus to their respective Desktop/Vr parent."); - HudOperations.Instance.worldLoadingItem = isVR ? HudOperations.Instance.worldLoadingItemVr : HudOperations.Instance.worldLoadingItemDesktop; - HudOperations.Instance.worldLoadStatus = isVR ? HudOperations.Instance.worldLoadStatusVr : HudOperations.Instance.worldLoadStatusDesktop; - } - catch (Exception) - { - MelonLogger.Error("Error updating HudOperations LoadingItem & LoadStatus!"); - throw; - } - } - - private static void DisableMirrorCanvas() - { - try - { - //tell the game we are in mirror mode so itll disable it (if enabled) - PortableCamera.Instance.mode = MirroringMode.Mirror; - PortableCamera.Instance.ChangeMirroring(); - } - catch (Exception) - { - MelonLogger.Error("Error updating CVRGestureRecognizer camera!"); - throw; - } - } - - private static void UpdateGestureReconizerCam() - { - try - { - MelonLogger.Msg("Set GestureReconizerCam camera to active camera."); - Traverse.Create(CVRGestureRecognizer.Instance).Field("_camera").SetValue(PlayerSetup.Instance.GetActiveCamera().GetComponent()); - } - catch (Exception) - { - MelonLogger.Error("Error updating CVRGestureRecognizer camera!"); - throw; - } - } - - private static void SwitchPickupOrigins() - { - try - { - MelonLogger.Msg("Switched pickup origins."); - CVRPickupObjectTracker[] pickups = Object.FindObjectsOfType(); - - for (int i = 0; i < pickups.Count(); i++) - { - pickups[i].OnSwitch(); - } - } - catch (Exception) - { - MelonLogger.Error("Error switching pickup origins!"); - throw; - } + if (!DesktopVRSwitchHelper.Instance) return; + DesktopVRSwitchHelper.Instance.SettingTimedErrorCatch = m_entryTimedErrorCatch.Value; + DesktopVRSwitchHelper.Instance.SettingTimedErrorTimer = m_entryTimedErrorTimer.Value; } } \ No newline at end of file diff --git a/DesktopVRSwitch/Patches/ReferenceCameraFix.cs b/DesktopVRSwitch/Patches/ReferenceCameraFix.cs new file mode 100644 index 0000000..d1bc27b --- /dev/null +++ b/DesktopVRSwitch/Patches/ReferenceCameraFix.cs @@ -0,0 +1,127 @@ +using ABI.CCK.Components; +using ABI_RC.Core.Base; +using ABI_RC.Core.Player; +using ABI_RC.Core.Savior; +using Aura2API; +using BeautifyEffect; +using HarmonyLib; +using UnityEngine; +using UnityEngine.AzureSky; +using UnityEngine.Rendering.PostProcessing; +using Object = UnityEngine.Object; + +namespace DesktopVRSwitch.Patches; + +[HarmonyPatch] +internal class ReferenceCameraFix +{ + [HarmonyPostfix] + [HarmonyPatch(typeof(CVRWorld), "SetDefaultCamValues")] + private static void CVRWorld_SetDefaultCamValues_Postfix(ref CVRWorld __instance) + { + Camera active; + Camera nonActive; + if (MetaPort.Instance.isUsingVr) + { + active = PlayerSetup.Instance.vrCamera.GetComponent(); + nonActive = PlayerSetup.Instance.desktopCamera.GetComponent(); + } + else + { + active = PlayerSetup.Instance.desktopCamera.GetComponent(); + nonActive = PlayerSetup.Instance.vrCamera.GetComponent(); + } + CopyActiveCamera(active, nonActive); + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(CVRWorld), "CopyRefCamValues")] + private static void CVRWorld_CopyRefCamValues_Postfix(ref CVRWorld __instance) + { + Camera active; + Camera nonActive; + if (MetaPort.Instance.isUsingVr) + { + active = PlayerSetup.Instance.vrCamera.GetComponent(); + nonActive = PlayerSetup.Instance.desktopCamera.GetComponent(); + } + else + { + active = PlayerSetup.Instance.desktopCamera.GetComponent(); + nonActive = PlayerSetup.Instance.vrCamera.GetComponent(); + } + CopyActiveCamera(active, nonActive); + } + + private static void CopyActiveCamera(Camera activeCamera, Camera inactiveCamera) + { + //steal basic settings + inactiveCamera.farClipPlane = activeCamera.farClipPlane; + inactiveCamera.nearClipPlane = activeCamera.nearClipPlane; + inactiveCamera.cullingMask = activeCamera.cullingMask; + inactiveCamera.depthTextureMode = activeCamera.depthTextureMode; + + //steal post processing if added + PostProcessLayer ppLayerActive = activeCamera.GetComponent(); + PostProcessLayer ppLayerNonActive = inactiveCamera.GetComponent(); + if (ppLayerActive != null && ppLayerNonActive != null) + { + ppLayerNonActive.enabled = ppLayerActive.enabled; + ppLayerNonActive.volumeLayer = ppLayerActive.volumeLayer; + } + + //what even is this aura camera stuff + AuraCamera auraActive = activeCamera.GetComponent(); + AuraCamera auraNonActive = inactiveCamera.AddComponentIfMissing(); + if (auraActive != null && auraNonActive != null) + { + auraNonActive.enabled = auraActive.enabled; + auraNonActive.frustumSettings = auraActive.frustumSettings; + } + else + { + auraNonActive.enabled = false; + } + + //flare layer thing? the sun :_:_:_:_:_: + FlareLayer flareActive = activeCamera.GetComponent(); + FlareLayer flareNonActive = inactiveCamera.AddComponentIfMissing(); + if (flareActive != null && flareNonActive != null) + { + flareNonActive.enabled = flareActive.enabled; + } + else + { + flareNonActive.enabled = false; + } + + //set correct farclipplane so effect is correct on switching world after switch + PlayerSetup.Instance.transitionEffectVr.material.SetFloat("ClippingPlane", activeCamera.farClipPlane); + PlayerSetup.Instance.transitionEffectDesktop.material.SetFloat("ClippingPlane", activeCamera.farClipPlane); + + //and now what the fuck is fog scattering + AzureFogScattering azureFogActive = activeCamera.GetComponent(); + AzureFogScattering azureFogNonActive = inactiveCamera.AddComponentIfMissing(); + if (azureFogActive != null && azureFogNonActive != null) + { + azureFogNonActive.fogScatteringMaterial = azureFogActive.fogScatteringMaterial; + } + else + { + Object.Destroy(inactiveCamera.GetComponent()); + } + + //why is there so many thingsssssssss + Beautify beautifyActive = activeCamera.GetComponent(); + Beautify beautifyNonActive = inactiveCamera.AddComponentIfMissing(); + if (beautifyActive != null && beautifyNonActive != null) + { + beautifyNonActive.quality = beautifyActive.quality; + beautifyNonActive.profile = beautifyActive.profile; + } + else + { + Object.Destroy(inactiveCamera.gameObject.GetComponent()); + } + } +} \ No newline at end of file diff --git a/DesktopVRSwitch/Properties/AssemblyInfo.cs b/DesktopVRSwitch/Properties/AssemblyInfo.cs index 03ba712..b051b24 100644 --- a/DesktopVRSwitch/Properties/AssemblyInfo.cs +++ b/DesktopVRSwitch/Properties/AssemblyInfo.cs @@ -25,6 +25,6 @@ using System.Reflection; namespace DesktopVRSwitch.Properties; internal static class AssemblyInfoParams { - public const string Version = "2.0.0"; + public const string Version = "3.0.0"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/DesktopVRSwitch/UIExpansionKitAddon.cs b/DesktopVRSwitch/UIExpansionKitAddon.cs new file mode 100644 index 0000000..de9e9d7 --- /dev/null +++ b/DesktopVRSwitch/UIExpansionKitAddon.cs @@ -0,0 +1,14 @@ +using System.Runtime.CompilerServices; +using UIExpansionKit.API; + +namespace DesktopVRSwitch; +public static class UiExtensionsAddon +{ + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Init() + { + var settings = ExpansionKitApi.GetSettingsCategory(DesktopVRSwitch.SettingsCategory); + settings.AddSimpleButton("Switch VRMode", SwitchModeButton); + } + internal static void SwitchModeButton() => DesktopVRSwitchHelper.Instance.SwitchMode(true); +} \ No newline at end of file diff --git a/DesktopVRSwitch/format.json b/DesktopVRSwitch/format.json index 6f77ed3..df073eb 100644 --- a/DesktopVRSwitch/format.json +++ b/DesktopVRSwitch/format.json @@ -1,9 +1,9 @@ { - "_id": -1, + "_id": 103, "name": "DesktopVRSwitch", - "modversion": "2.0.0", - "gameversion": "2022r168", - "loaderversion": "0.5.4", + "modversion": "3.0.0", + "gameversion": "2022r169p1", + "loaderversion": "0.5.7", "modtype": "Mod", "author": "NotAKidoS", "description": "Allows you to switch between Desktop and VR with a keybind.\n**Press Control + F6 to switch.**\n\nWhile this mod is a nice convienence feature to have access to, not every chillout 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.**", @@ -16,8 +16,8 @@ "requirements": [ "None" ], - "downloadlink": "https://github.com/NotAKidOnSteam/DesktopVRSwitch/releases/download/r2/DesktopVRSwitch.dll", + "downloadlink": "https://github.com/NotAKidOnSteam/DesktopVRSwitch/releases/download/r3/DesktopVRSwitch.dll", "sourcelink": "https://github.com/NotAKidOnSteam/DesktopVRSwitch/", - "changelog": "Initial Release", + "changelog": "Initial CVRMG Release", "embedcolor": "3498db" } \ No newline at end of file