From 4123a1f25d9047137bc764adcaf4bc923835c2cf Mon Sep 17 00:00:00 2001 From: NotAKidoS <37721153+NotAKidoS@users.noreply.github.com> Date: Wed, 27 Aug 2025 01:23:37 -0500 Subject: [PATCH 1/7] [ASTExtension] Fixes for 2025r180 --- ASTExtension/Extensions/PlayerSetupExtensions.cs | 4 ++-- ASTExtension/Integrations/BTKUI/BtkUiAddon.cs | 6 +++--- ASTExtension/Main.cs | 15 +++++++++------ ASTExtension/Properties/AssemblyInfo.cs | 4 ++-- ASTExtension/format.json | 10 +++++----- 5 files changed, 21 insertions(+), 18 deletions(-) diff --git a/ASTExtension/Extensions/PlayerSetupExtensions.cs b/ASTExtension/Extensions/PlayerSetupExtensions.cs index 7fa9e5b..661a94a 100644 --- a/ASTExtension/Extensions/PlayerSetupExtensions.cs +++ b/ASTExtension/Extensions/PlayerSetupExtensions.cs @@ -9,13 +9,13 @@ public static class PlayerSetupExtensions // immediate measurement of the player's avatar height public static float GetCurrentAvatarHeight(this PlayerSetup playerSetup) { - if (playerSetup._avatar == null) + if (!playerSetup.IsAvatarLoaded) { ASTExtensionMod.Logger.Error("GetCurrentAvatarHeight: Avatar is null"); return 0f; } - Vector3 localScale = playerSetup._avatar.transform.localScale; + Vector3 localScale = playerSetup.AvatarTransform.localScale; Vector3 initialScale = playerSetup.initialScale; float initialHeight = playerSetup._initialAvatarHeight; Vector3 scaleDifference = CVRTools.DivideVectors(localScale - initialScale, initialScale); diff --git a/ASTExtension/Integrations/BTKUI/BtkUiAddon.cs b/ASTExtension/Integrations/BTKUI/BtkUiAddon.cs index e0e294a..e075e88 100644 --- a/ASTExtension/Integrations/BTKUI/BtkUiAddon.cs +++ b/ASTExtension/Integrations/BTKUI/BtkUiAddon.cs @@ -49,7 +49,7 @@ public static partial class BtkUiAddon if (!CVRPlayerManager.Instance.GetPlayerPuppetMaster(_selectedPlayer, out PuppetMaster player)) return; - if (player._avatar == null) + if (!player.IsAvatarLoaded) return; float height = player.netIkController.GetRemoteHeight(); @@ -64,8 +64,8 @@ public static partial class BtkUiAddon if (!CVRPlayerManager.Instance.GetPlayerPuppetMaster(_selectedPlayer, out PuppetMaster player)) return; - AvatarAnimatorManager localAnimator = PlayerSetup.Instance.animatorManager; - AvatarAnimatorManager remoteAnimator = player.animatorManager; + AvatarAnimatorManager localAnimator = PlayerSetup.Instance.AnimatorManager; + AvatarAnimatorManager remoteAnimator = player.AnimatorManager; if (!localAnimator.IsInitialized || !remoteAnimator.IsInitialized) return; diff --git a/ASTExtension/Main.cs b/ASTExtension/Main.cs index 461dc16..4ddf871 100644 --- a/ASTExtension/Main.cs +++ b/ASTExtension/Main.cs @@ -81,7 +81,7 @@ public class ASTExtensionMod : MelonMod ); HarmonyInstance.Patch( - typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar), + typeof(PlayerBase).GetMethod(nameof(PlayerBase.ClearAvatar), BindingFlags.Public | BindingFlags.Instance), prefix: new HarmonyMethod(typeof(ASTExtensionMod).GetMethod(nameof(OnClearAvatar), BindingFlags.NonPublic | BindingFlags.Static)) @@ -112,8 +112,11 @@ public class ASTExtensionMod : MelonMod Instance.OnLocalAvatarLoad(); } - private static void OnClearAvatar(ref CVRAvatar ____avatarDescriptor) - => Instance.OnLocalAvatarClear(____avatarDescriptor); + private static void OnClearAvatar(ref PlayerBase __instance) + { + if (!__instance.IsLocalPlayer) return; + Instance.OnLocalAvatarClear(__instance.AvatarDescriptor); + } #endregion Harmony Patches @@ -227,7 +230,7 @@ public class ASTExtensionMod : MelonMod { parameterName = null; - AvatarAnimatorManager animatorManager = PlayerSetup.Instance.animatorManager; + AvatarAnimatorManager animatorManager = PlayerSetup.Instance.AnimatorManager; if (!animatorManager.IsInitialized) { Logger.Error("AnimatorManager is not initialized!"); @@ -254,7 +257,7 @@ public class ASTExtensionMod : MelonMod maxHeight = 0f; modifier = 1f; - AvatarAnimatorManager animatorManager = PlayerSetup.Instance.animatorManager; + AvatarAnimatorManager animatorManager = PlayerSetup.Instance.AnimatorManager; if (!animatorManager.IsInitialized) { Logger.Error("AnimatorManager is not initialized!"); @@ -319,7 +322,7 @@ public class ASTExtensionMod : MelonMod if (!_currentAvatarSupported) return; - AvatarAnimatorManager animatorManager = PlayerSetup.Instance.animatorManager; + AvatarAnimatorManager animatorManager = PlayerSetup.Instance.AnimatorManager; if (!animatorManager.IsInitialized) { Logger.Error("AnimatorManager is not initialized!"); diff --git a/ASTExtension/Properties/AssemblyInfo.cs b/ASTExtension/Properties/AssemblyInfo.cs index 0200119..2e4d7b4 100644 --- a/ASTExtension/Properties/AssemblyInfo.cs +++ b/ASTExtension/Properties/AssemblyInfo.cs @@ -17,7 +17,7 @@ using System.Reflection; downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ASTExtension" )] -[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")] +[assembly: MelonGame("ChilloutVR", "ChilloutVR")] [assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] [assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)] [assembly: MelonColor(255, 246, 25, 99)] // red-pink @@ -27,6 +27,6 @@ using System.Reflection; namespace NAK.ASTExtension.Properties; internal static class AssemblyInfoParams { - public const string Version = "1.0.3"; + public const string Version = "1.0.4"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/ASTExtension/format.json b/ASTExtension/format.json index 934110a..abcdbfc 100644 --- a/ASTExtension/format.json +++ b/ASTExtension/format.json @@ -1,9 +1,9 @@ { "_id": 223, "name": "ASTExtension", - "modversion": "1.0.3", - "gameversion": "2025r179", - "loaderversion": "0.6.1", + "modversion": "1.0.4", + "gameversion": "2025r180", + "loaderversion": "0.7.2", "modtype": "Mod", "author": "NotAKidoS", "description": "Extension mod for [Avatar Scale Tool](https://github.com/NotAKidoS/AvatarScaleTool):\n- VR Gesture to scale\n- Persistent height\n- Copy height from others\n\nBest used with Avatar Scale Tool, but will attempt to work with found scaling setups.\nRequires already having Avatar Scaling on the avatar. This is **not** Universal Scaling.", @@ -17,8 +17,8 @@ "requirements": [ "BTKUILib" ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/ASTExtension.dll", + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/ASTExtension.dll", "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ASTExtension/", - "changelog": "- Recompiled for 2025r179", + "changelog": "- Fixes for 2025r180", "embedcolor": "#f61963" } \ No newline at end of file From c9acb000882dc5673246425e155a4ce59a9c4ed5 Mon Sep 17 00:00:00 2001 From: NotAKidoS <37721153+NotAKidoS@users.noreply.github.com> Date: Wed, 27 Aug 2025 01:23:45 -0500 Subject: [PATCH 2/7] [DoubleTapJumpToExitSeat] Fixes for 2025r180 --- DoubleTapJumpToExitSeat/Main.cs | 4 ++-- DoubleTapJumpToExitSeat/Properties/AssemblyInfo.cs | 4 ++-- DoubleTapJumpToExitSeat/format.json | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/DoubleTapJumpToExitSeat/Main.cs b/DoubleTapJumpToExitSeat/Main.cs index c3b5a6f..18dd9fb 100644 --- a/DoubleTapJumpToExitSeat/Main.cs +++ b/DoubleTapJumpToExitSeat/Main.cs @@ -73,8 +73,8 @@ public class DoubleTapJumpToExitSeatMod : MelonMod // Steal sync if (__instance.lockControls) { - if (__instance._spawnable != null) __instance._spawnable.ForceUpdate(4); - if (__instance._objectSync != null) __instance._objectSync.ForceUpdate(4); + if (__instance._spawnable) __instance._spawnable.ForceUpdate(4); + if (__instance._objectSync) __instance._objectSync.ForceUpdate(4); } return false; // don't call original method diff --git a/DoubleTapJumpToExitSeat/Properties/AssemblyInfo.cs b/DoubleTapJumpToExitSeat/Properties/AssemblyInfo.cs index fd30036..2e85366 100644 --- a/DoubleTapJumpToExitSeat/Properties/AssemblyInfo.cs +++ b/DoubleTapJumpToExitSeat/Properties/AssemblyInfo.cs @@ -17,7 +17,7 @@ using System.Reflection; downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/DoubleTapJumpToExitSeat" )] -[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")] +[assembly: MelonGame("ChilloutVR", "ChilloutVR")] [assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] [assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)] [assembly: MelonColor(255, 246, 25, 99)] // red-pink @@ -27,6 +27,6 @@ using System.Reflection; namespace NAK.DoubleTapJumpToExitSeat.Properties; internal static class AssemblyInfoParams { - public const string Version = "1.0.0"; + public const string Version = "1.0.1"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/DoubleTapJumpToExitSeat/format.json b/DoubleTapJumpToExitSeat/format.json index ab8e14e..e14365c 100644 --- a/DoubleTapJumpToExitSeat/format.json +++ b/DoubleTapJumpToExitSeat/format.json @@ -1,9 +1,9 @@ { - "_id": -1, + "_id": 255, "name": "DoubleTapJumpToExitSeat", - "modversion": "1.0.0", - "gameversion": "2025r179", - "loaderversion": "0.6.1", + "modversion": "1.0.1", + "gameversion": "2025r180", + "loaderversion": "0.7.2", "modtype": "Mod", "author": "NotAKidoS", "description": "Replaces seat exit controls with a double-tap of the jump button, avoiding accidental exits from joystick drift or opening the menu.", @@ -16,8 +16,8 @@ "requirements": [ "None" ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/DoubleTapJumpToExitSeat.dll", + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/DoubleTapJumpToExitSeat.dll", "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/DoubleTapJumpToExitSeat/", - "changelog": "- Initial release", + "changelog": "- Fixes for 2025r180", "embedcolor": "#f61963" } \ No newline at end of file From 30c069388c0e76414f2e152df9413d7c5be29311 Mon Sep 17 00:00:00 2001 From: NotAKidoS <37721153+NotAKidoS@users.noreply.github.com> Date: Wed, 27 Aug 2025 01:23:56 -0500 Subject: [PATCH 3/7] [PathCamDisabler] Fixes for 2025r180 --- PathCamDisabler/Properties/AssemblyInfo.cs | 4 ++-- PathCamDisabler/format.json | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/PathCamDisabler/Properties/AssemblyInfo.cs b/PathCamDisabler/Properties/AssemblyInfo.cs index 8e01437..5dedc26 100644 --- a/PathCamDisabler/Properties/AssemblyInfo.cs +++ b/PathCamDisabler/Properties/AssemblyInfo.cs @@ -17,7 +17,7 @@ using System.Reflection; downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/PathCamDisabler" )] -[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")] +[assembly: MelonGame("ChilloutVR", "ChilloutVR")] [assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] [assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)] [assembly: MelonColor(255, 246, 25, 99)] // red-pink @@ -27,6 +27,6 @@ using System.Reflection; namespace NAK.PathCamDisabler.Properties; internal static class AssemblyInfoParams { - public const string Version = "1.0.3"; + public const string Version = "1.0.4"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/PathCamDisabler/format.json b/PathCamDisabler/format.json index 6a8d8df..44362ba 100644 --- a/PathCamDisabler/format.json +++ b/PathCamDisabler/format.json @@ -1,9 +1,9 @@ { "_id": 110, "name": "PathCamDisabler", - "modversion": "1.0.3", - "gameversion": "2025r179", - "loaderversion": "0.6.1", + "modversion": "1.0.4", + "gameversion": "2025r180", + "loaderversion": "0.7.2", "modtype": "Mod", "author": "NotAKidoS", "description": "Adds option to disable the Path Camera Controller to free up your numkeys.\nAdditional option to disable flight binding.", @@ -16,8 +16,8 @@ "requirements": [ "None" ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/PathCamDisabler.dll", + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/PathCamDisabler.dll", "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/PathCamDisabler/", - "changelog": "- Recompiled for 2025r179", + "changelog": "- Fixes for 2025r180", "embedcolor": "#f61963" } \ No newline at end of file From c368daab4fa132d9b33ff51b8d4a004eb4c55562 Mon Sep 17 00:00:00 2001 From: NotAKidoS <37721153+NotAKidoS@users.noreply.github.com> Date: Wed, 27 Aug 2025 01:34:56 -0500 Subject: [PATCH 4/7] killed RelativeSync, now its just RelativeSyncJitterFix --- RelativeSync/Main.cs | 48 ---- RelativeSync/ModSettings.cs | 43 ---- RelativeSync/Networking/ModNetwork.cs | 151 ------------- RelativeSync/Patches.cs | 108 --------- RelativeSync/README.md | 30 --- .../Components/RelativeSyncController.cs | 206 ------------------ .../Components/RelativeSyncMarker.cs | 110 ---------- .../Components/RelativeSyncMonitor.cs | 79 ------- .../RelativeSync/RelativeSyncManager.cs | 71 ------ RelativeSync/format.json | 23 -- RelativeSyncJitterFix/Main.cs | 25 +++ RelativeSyncJitterFix/Patches.cs | 22 ++ .../Properties/AssemblyInfo.cs | 18 +- RelativeSyncJitterFix/README.md | 21 ++ .../RelativeSyncJitterFix.csproj | 0 RelativeSyncJitterFix/format.json | 23 ++ 16 files changed, 100 insertions(+), 878 deletions(-) delete mode 100644 RelativeSync/Main.cs delete mode 100644 RelativeSync/ModSettings.cs delete mode 100644 RelativeSync/Networking/ModNetwork.cs delete mode 100644 RelativeSync/Patches.cs delete mode 100644 RelativeSync/README.md delete mode 100644 RelativeSync/RelativeSync/Components/RelativeSyncController.cs delete mode 100644 RelativeSync/RelativeSync/Components/RelativeSyncMarker.cs delete mode 100644 RelativeSync/RelativeSync/Components/RelativeSyncMonitor.cs delete mode 100644 RelativeSync/RelativeSync/RelativeSyncManager.cs delete mode 100644 RelativeSync/format.json create mode 100644 RelativeSyncJitterFix/Main.cs create mode 100644 RelativeSyncJitterFix/Patches.cs rename {RelativeSync => RelativeSyncJitterFix}/Properties/AssemblyInfo.cs (65%) create mode 100644 RelativeSyncJitterFix/README.md rename RelativeSync/RelativeSync.csproj => RelativeSyncJitterFix/RelativeSyncJitterFix.csproj (100%) create mode 100644 RelativeSyncJitterFix/format.json diff --git a/RelativeSync/Main.cs b/RelativeSync/Main.cs deleted file mode 100644 index 0a50472..0000000 --- a/RelativeSync/Main.cs +++ /dev/null @@ -1,48 +0,0 @@ -using MelonLoader; -using NAK.RelativeSync.Networking; -using NAK.RelativeSync.Patches; - -namespace NAK.RelativeSync; - -public class RelativeSyncMod : MelonMod -{ - internal static MelonLogger.Instance Logger; - - public override void OnInitializeMelon() - { - Logger = LoggerInstance; - - ModNetwork.Subscribe(); - ModSettings.Initialize(); - - // Experimental sync hack - ApplyPatches(typeof(CVRSpawnablePatches)); - - // Send relative sync update after network root data update - ApplyPatches(typeof(NetworkRootDataUpdatePatches)); - - // Add components if missing (for relative sync monitor and controller) - ApplyPatches(typeof(PlayerSetupPatches)); - ApplyPatches(typeof(PuppetMasterPatches)); - - // Add components if missing (for relative sync markers) - ApplyPatches(typeof(CVRSeatPatches)); - ApplyPatches(typeof(CVRMovementParentPatches)); - - // So we run after the client moves the remote player - ApplyPatches(typeof(NetIKController_Patches)); - } - - private void ApplyPatches(Type type) - { - try - { - HarmonyInstance.PatchAll(type); - } - catch (Exception e) - { - LoggerInstance.Msg($"Failed while patching {type.Name}!"); - LoggerInstance.Error(e); - } - } -} \ No newline at end of file diff --git a/RelativeSync/ModSettings.cs b/RelativeSync/ModSettings.cs deleted file mode 100644 index f8567ad..0000000 --- a/RelativeSync/ModSettings.cs +++ /dev/null @@ -1,43 +0,0 @@ -using MelonLoader; -using NAK.RelativeSync.Networking; - -namespace NAK.RelativeSync; - -internal static class ModSettings -{ - internal const string ModName = nameof(RelativeSync); - - #region Melon Preferences - - private static readonly MelonPreferences_Category Category = - MelonPreferences.CreateCategory(ModName); - - private static readonly MelonPreferences_Entry DebugLogInbound = - Category.CreateEntry("DebugLogInbound", false, - "Debug Log Inbound", description: "Log inbound network messages."); - - private static readonly MelonPreferences_Entry DebugLogOutbound = - Category.CreateEntry("DebugLogOutbound", false, - "Debug Log Outbound", description: "Log outbound network messages."); - - private static readonly MelonPreferences_Entry ExpSyncedObjectHack = - Category.CreateEntry("ExpSyncedObjectHack", true, - "Exp Spawnable Sync Fix", description: "Forces CVRSpawnable to update position in FixedUpdate. May help reduce local jitter on synced movement parents."); - - #endregion Melon Preferences - - internal static void Initialize() - { - foreach (MelonPreferences_Entry setting in Category.Entries) - setting.OnEntryValueChangedUntyped.Subscribe(OnSettingsChanged); - - OnSettingsChanged(); - } - - private static void OnSettingsChanged(object oldValue = null, object newValue = null) - { - ModNetwork.Debug_NetworkInbound = DebugLogInbound.Value; - ModNetwork.Debug_NetworkOutbound = DebugLogOutbound.Value; - Patches.CVRSpawnablePatches.UseHack = ExpSyncedObjectHack.Value; - } -} \ No newline at end of file diff --git a/RelativeSync/Networking/ModNetwork.cs b/RelativeSync/Networking/ModNetwork.cs deleted file mode 100644 index 713f27c..0000000 --- a/RelativeSync/Networking/ModNetwork.cs +++ /dev/null @@ -1,151 +0,0 @@ -using ABI_RC.Core.Networking; -using ABI_RC.Systems.ModNetwork; -using DarkRift; -using UnityEngine; - -namespace NAK.RelativeSync.Networking; - -public static class ModNetwork -{ - public static bool Debug_NetworkInbound = false; - public static bool Debug_NetworkOutbound = false; - - private static bool _isSubscribedToModNetwork; - - private struct MovementParentSyncData - { - public bool HasSyncedThisData; - public int MarkerHash; - public Vector3 RootPosition; - public Vector3 RootRotation; - // public Vector3 HipPosition; - // public Vector3 HipRotation; - } - - private static MovementParentSyncData _latestMovementParentSyncData; - - #region Constants - - private const string ModId = "MelonMod.NAK.RelativeSync"; - - #endregion - - #region Enums - - private enum MessageType : byte - { - MovementParentOrChair = 0 - //RelativePickup = 1, - //RelativeAttachment = 2, - } - - #endregion - - #region Mod Network Internals - - internal static void Subscribe() - { - ModNetworkManager.Subscribe(ModId, OnMessageReceived); - - _isSubscribedToModNetwork = ModNetworkManager.IsSubscribed(ModId); - if (!_isSubscribedToModNetwork) - Debug.LogError("Failed to subscribe to Mod Network!"); - } - - // Called right after NetworkRootDataUpdate.Submit() - internal static void SendRelativeSyncUpdate() - { - if (!_isSubscribedToModNetwork) - return; - - if (_latestMovementParentSyncData.HasSyncedThisData) - return; - - SendMessage(MessageType.MovementParentOrChair, _latestMovementParentSyncData.MarkerHash, - _latestMovementParentSyncData.RootPosition, _latestMovementParentSyncData.RootRotation); - - _latestMovementParentSyncData.HasSyncedThisData = true; - } - - public static void SetLatestRelativeSync( - int markerHash, - Vector3 position, Vector3 rotation) - { - // check if the data has changed - if (_latestMovementParentSyncData.MarkerHash == markerHash - && _latestMovementParentSyncData.RootPosition == position - && _latestMovementParentSyncData.RootRotation == rotation) - return; // no need to update (shocking) - - _latestMovementParentSyncData.HasSyncedThisData = false; // reset - _latestMovementParentSyncData.MarkerHash = markerHash; - _latestMovementParentSyncData.RootPosition = position; - _latestMovementParentSyncData.RootRotation = rotation; - } - - private static void SendMessage(MessageType messageType, int markerHash, Vector3 position, Vector3 rotation) - { - if (!IsConnectedToGameNetwork()) - return; - - using ModNetworkMessage modMsg = new(ModId); - modMsg.Write((byte)messageType); - modMsg.Write(markerHash); - modMsg.Write(position); - modMsg.Write(rotation); - modMsg.Send(); - - if (Debug_NetworkOutbound) - Debug.Log( - $"[Outbound] MessageType: {messageType}, MarkerHash: {markerHash}, Position: {position}, " + - $"Rotation: {rotation}"); - } - - private static void OnMessageReceived(ModNetworkMessage msg) - { - msg.Read(out byte msgTypeRaw); - - if (!Enum.IsDefined(typeof(MessageType), msgTypeRaw)) - return; - - switch ((MessageType)msgTypeRaw) - { - case MessageType.MovementParentOrChair: - msg.Read(out int markerHash); - msg.Read(out Vector3 receivedPosition); - msg.Read(out Vector3 receivedRotation); - // msg.Read(out Vector3 receivedHipPosition); - // msg.Read(out Vector3 receivedHipRotation); - - OnNetworkPositionUpdateReceived(msg.Sender, markerHash, receivedPosition, receivedRotation); - - if (Debug_NetworkInbound) - Debug.Log($"[Inbound] Sender: {msg.Sender}, MarkerHash: {markerHash}, " + - $"Position: {receivedPosition}, Rotation: {receivedRotation}"); - break; - default: - Debug.LogError($"Invalid message type received from: {msg.Sender}"); - break; - } - } - - #endregion - - #region Private Methods - - private static bool IsConnectedToGameNetwork() - { - return NetworkManager.Instance != null - && NetworkManager.Instance.GameNetwork != null - && NetworkManager.Instance.GameNetwork.ConnectionState == ConnectionState.Connected; - } - - private static void OnNetworkPositionUpdateReceived( - string sender, int markerHash, - Vector3 position, Vector3 rotation) - { - RelativeSyncManager.ApplyRelativeSync(sender, markerHash, position, rotation); - } - - #endregion -} \ No newline at end of file diff --git a/RelativeSync/Patches.cs b/RelativeSync/Patches.cs deleted file mode 100644 index 10acf15..0000000 --- a/RelativeSync/Patches.cs +++ /dev/null @@ -1,108 +0,0 @@ -using ABI_RC.Core.Base; -using ABI_RC.Core.InteractionSystem; -using ABI_RC.Core.Networking.Jobs; -using ABI_RC.Core.Player; -using ABI.CCK.Components; -using HarmonyLib; -using NAK.RelativeSync.Components; -using NAK.RelativeSync.Networking; -using UnityEngine; - -namespace NAK.RelativeSync.Patches; - -internal static class PlayerSetupPatches -{ - [HarmonyPostfix] - [HarmonyPatch(typeof(PlayerSetup), nameof(PlayerSetup.Start))] - private static void Postfix_PlayerSetup_Start(ref PlayerSetup __instance) - { - __instance.AddComponentIfMissing(); - } -} - -internal static class PuppetMasterPatches -{ - [HarmonyPostfix] - [HarmonyPatch(typeof(PuppetMaster), nameof(PuppetMaster.Start))] - private static void Postfix_PuppetMaster_Start(ref PuppetMaster __instance) - { - __instance.AddComponentIfMissing(); - } -} - -internal static class CVRSeatPatches -{ - [HarmonyPostfix] - [HarmonyPatch(typeof(CVRSeat), nameof(CVRSeat.Awake))] - private static void Postfix_CVRSeat_Awake(ref CVRSeat __instance) - { - __instance.AddComponentIfMissing(); - } -} - -internal static class CVRMovementParentPatches -{ - [HarmonyPostfix] - [HarmonyPatch(typeof(CVRMovementParent), nameof(CVRMovementParent.Start))] - private static void Postfix_CVRMovementParent_Start(ref CVRMovementParent __instance) - { - __instance.AddComponentIfMissing(); - } -} - -internal static class NetworkRootDataUpdatePatches -{ - [HarmonyPostfix] - [HarmonyPatch(typeof(NetworkRootDataUpdate), nameof(NetworkRootDataUpdate.Submit))] - private static void Postfix_NetworkRootDataUpdater_Submit() - { - ModNetwork.SendRelativeSyncUpdate(); // Send the relative sync update after the network root data update - } -} - -internal static class CVRSpawnablePatches -{ - internal static bool UseHack; - - private static bool _canUpdate; - - [HarmonyPrefix] - [HarmonyPatch(typeof(CVRSpawnable), nameof(CVRSpawnable.Update))] - private static bool Prefix_CVRSpawnable_Update() - => !UseHack || _canUpdate; - - [HarmonyPostfix] - [HarmonyPatch(typeof(CVRSpawnable), nameof(CVRSpawnable.FixedUpdate))] - private static void Postfix_CVRSpawnable_FixedUpdate(ref CVRSpawnable __instance) - { - if (!UseHack) return; - - _canUpdate = true; - __instance.Update(); - _canUpdate = false; - } -} - -internal static class NetIKController_Patches -{ - [HarmonyPostfix] - [HarmonyPatch(typeof(NetIKController), nameof(NetIKController.LateUpdate))] - private static void Postfix_NetIKController_LateUpdate(ref NetIKController __instance) - { - if (!RelativeSyncManager.NetIkControllersToRelativeSyncControllers.TryGetValue(__instance, - out RelativeSyncController syncController)) - return; - - // Apply relative sync after the network IK has been applied - syncController.OnPostNetIkControllerLateUpdate(); - } - - [HarmonyPrefix] - [HarmonyPatch(typeof(NetIKController), nameof(NetIKController.GetLocalPlayerPosition))] - private static bool Prefix_NetIKController_GetLocalPlayerPosition(ref NetIKController __instance, ref Vector3 __result) - { - // why is the original method so bad - __result = PlayerSetup.Instance.activeCam.transform.position; - return false; - } -} \ No newline at end of file diff --git a/RelativeSync/README.md b/RelativeSync/README.md deleted file mode 100644 index 4ab0455..0000000 --- a/RelativeSync/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# RelativeSync - -Relative sync for Movement Parent & Chairs. Requires both users to have the mod installed. Synced over Mod Network. - -https://github.com/NotAKidoS/NAK_CVR_Mods/assets/37721153/ae6c6e4b-7529-42e2-bd2c-afa050849906 - -## Mod Settings -- **Debug Network Inbound**: Log network messages received from other players. -- **Debug Network Outbound**: Log network messages sent to other players. -- **Exp Spawnable Sync Hack**: Forces CVRSpawnable to update position in FixedUpdate. This can help with local jitter while on a remote synced movement parent. -- **Exp Disable Interpolation on BBCC**: Disables interpolation on BetterBetterCharacterController. This can help with local jitter while on any movement parent. - -## Known Issues -- Movement Parents on remote users will still locally jitter. - - PuppetMaster/NetIkController applies received position updates in LateUpdate, while character controller updates in FixedUpdate. -- Movement Parents using CVRObjectSync synced by remote users will still locally jitter. - - CVRObjectSync applies received position updates in LateUpdate, while character controller updates in FixedUpdate. -- Slight interpolation issue with humanoid avatar hips while standing on a Movement Parent. - - Requires further investigation. I believe it to be because hips are not synced properly, requiring me to relative sync the hips as well. - ---- - -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. diff --git a/RelativeSync/RelativeSync/Components/RelativeSyncController.cs b/RelativeSync/RelativeSync/Components/RelativeSyncController.cs deleted file mode 100644 index a94cde0..0000000 --- a/RelativeSync/RelativeSync/Components/RelativeSyncController.cs +++ /dev/null @@ -1,206 +0,0 @@ -using ABI_RC.Core.Player; -using ABI_RC.Systems.Movement; -using UnityEngine; - -namespace NAK.RelativeSync.Components; - -[DefaultExecutionOrder(9000)] // make sure this runs after NetIKController, but before Totally Wholesome LineController (9999) -public class RelativeSyncController : MonoBehaviour -{ - private const float MaxMagnitude = 750000000000f; - - private float _updateInterval = 0.05f; - private float _lastUpdate; - - private string _userId; - private PuppetMaster puppetMaster { get; set; } - private RelativeSyncMarker _relativeSyncMarker; - - private RelativeSyncData _relativeSyncData; - private RelativeSyncData _lastSyncData; - - #region Unity Events - - private void Start() - { - puppetMaster = GetComponent(); - - _userId = puppetMaster._playerDescriptor.ownerId; - RelativeSyncManager.RelativeSyncControllers.Add(_userId, this); - RelativeSyncManager.NetIkControllersToRelativeSyncControllers.Add(puppetMaster.netIkController, this); - } - - private void OnDestroy() - { - RelativeSyncManager.RelativeSyncControllers.Remove(_userId); - - if (puppetMaster == null - || puppetMaster.netIkController == null) - { - // remove by value ? - foreach (var kvp in RelativeSyncManager.NetIkControllersToRelativeSyncControllers) - { - if (kvp.Value != this) continue; - RelativeSyncManager.NetIkControllersToRelativeSyncControllers.Remove(kvp.Key); - break; - } - return; - } - RelativeSyncManager.NetIkControllersToRelativeSyncControllers.Remove(puppetMaster.netIkController); - } - - internal void OnPostNetIkControllerLateUpdate() - { - // if (puppetMaster._isHidden) - // return; - - if (_relativeSyncMarker == null) - return; - - if (!_relativeSyncMarker.IsComponentActive) - return; - - Animator animator = puppetMaster._animator; - if (animator == null) - return; - - Transform avatarTransform = animator.transform; - Transform hipTrans = (animator.avatar != null && animator.isHuman) - ? animator.GetBoneTransform(HumanBodyBones.Hips) : null; - - Vector3 relativeHipPos = default; - Quaternion relativeHipRot = default; - if (hipTrans != null) - { - Vector3 worldRootPos = avatarTransform.position; - Quaternion worldRootRot = avatarTransform.rotation; - - Vector3 hipPos = hipTrans.position; - Quaternion hipRot = hipTrans.rotation; - - relativeHipPos = Quaternion.Inverse(worldRootRot) * (hipPos - worldRootPos); - relativeHipRot = Quaternion.Inverse(worldRootRot) * hipRot; - } - - // TODO: handle the case where hip is not synced but is found on remote client - - float lerp = Mathf.Min((Time.time - _lastUpdate) / _updateInterval, 1f); - - ApplyRelativeRotation(avatarTransform, hipTrans, lerp); - ApplyRelativePosition(hipTrans, lerp); - - // idk if needed (both player root & avatar root are set to same world position) -_-_-_- - avatarTransform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity); - - // fix hip syncing because it is not relative to root, it is synced in world space -_- - if (hipTrans != null) - { - hipTrans.position = transform.position + transform.rotation * relativeHipPos; - hipTrans.rotation = transform.rotation * relativeHipRot; - } - - // Reprocess the root distance so we don't fuck avatar distance hider - NetIKController netIkController = puppetMaster.netIkController; - netIkController._rootDistance = Vector3.Distance((netIkController._collider.transform.position + netIkController._collider.center), - netIkController.GetLocalPlayerPosition()) - (netIkController._collider.radius + BetterBetterCharacterController.Instance.Radius); - } - - private void ApplyRelativeRotation(Transform avatarTransform, Transform hipTransform, float lerp) - { - if (!_relativeSyncMarker.ApplyRelativeRotation || - !(_relativeSyncData.LocalRootRotation.sqrMagnitude < MaxMagnitude)) - return; // not applying relative rotation or data is invalid - - Quaternion markerRotation = _relativeSyncMarker.transform.rotation; - Quaternion lastWorldRotation = markerRotation * Quaternion.Euler(_lastSyncData.LocalRootRotation); - Quaternion worldRotation = markerRotation * Quaternion.Euler(_relativeSyncData.LocalRootRotation); - - if (_relativeSyncMarker.OnlyApplyRelativeHeading) - { - Vector3 currentWorldUp = avatarTransform.up; - - Vector3 currentForward = lastWorldRotation * Vector3.forward; - Vector3 targetForward = worldRotation * Vector3.forward; - - currentForward = Vector3.ProjectOnPlane(currentForward, currentWorldUp).normalized; - targetForward = Vector3.ProjectOnPlane(targetForward, currentWorldUp).normalized; - - lastWorldRotation = Quaternion.LookRotation(currentForward, currentWorldUp); - worldRotation = Quaternion.LookRotation(targetForward, currentWorldUp); - } - - transform.rotation = Quaternion.Slerp(lastWorldRotation, worldRotation, lerp); - } - - private void ApplyRelativePosition(Transform hipTransform, float lerp) - { - if (!_relativeSyncMarker.ApplyRelativePosition || - !(_relativeSyncData.LocalRootPosition.sqrMagnitude < MaxMagnitude)) - return; // not applying relative position or data is invalid - - Transform targetTransform = _relativeSyncMarker.transform; - - Vector3 lastWorldPosition = targetTransform.TransformPoint(_lastSyncData.LocalRootPosition); - Vector3 worldPosition = targetTransform.TransformPoint(_relativeSyncData.LocalRootPosition); - transform.position = Vector3.Lerp(lastWorldPosition, worldPosition, lerp); - - // if (hipTransform == null) - // return; - // - // Vector3 lastWorldHipPosition = targetTransform.TransformPoint(_lastSyncData.LocalHipPosition); - // Vector3 worldHipPosition = targetTransform.TransformPoint(_relativeSyncData.LocalHipPosition); - // hipTransform.position = Vector3.Lerp(lastWorldHipPosition, worldHipPosition, lerp); - } - - #endregion Unity Events - - #region Public Methods - - public void SetRelativeSyncMarker(RelativeSyncMarker target) - { - if (_relativeSyncMarker == target) - return; - - _relativeSyncMarker = target; - - // calculate relative position and rotation so lerp can smooth it out (hack) - if (_relativeSyncMarker == null) - return; - - Animator avatarAnimator = puppetMaster._animator; - if (avatarAnimator == null) - return; // i dont care to bother - - RelativeSyncManager.GetRelativeAvatarPositionsFromMarker( - avatarAnimator, _relativeSyncMarker.transform, - out Vector3 relativePosition, out Vector3 relativeRotation); - - // set last sync data to current position and rotation so we don't lerp from the last marker - _lastSyncData.LocalRootPosition = relativePosition; - _lastSyncData.LocalRootRotation = relativeRotation; - _lastUpdate = Time.time; // reset update time - } - - public void SetRelativePositions(Vector3 position, Vector3 rotation) - { - // calculate update interval - float prevUpdate = _lastUpdate; - _lastUpdate = Time.time; - _updateInterval = _lastUpdate - prevUpdate; - - // cycle last sync data - _lastSyncData = _relativeSyncData; - - // set new sync data - _relativeSyncData.LocalRootPosition = position; - _relativeSyncData.LocalRootRotation = rotation; - } - - #endregion Public Methods - - private struct RelativeSyncData - { - public Vector3 LocalRootPosition; - public Vector3 LocalRootRotation; - } -} \ No newline at end of file diff --git a/RelativeSync/RelativeSync/Components/RelativeSyncMarker.cs b/RelativeSync/RelativeSync/Components/RelativeSyncMarker.cs deleted file mode 100644 index 9627146..0000000 --- a/RelativeSync/RelativeSync/Components/RelativeSyncMarker.cs +++ /dev/null @@ -1,110 +0,0 @@ -using ABI_RC.Core.InteractionSystem; -using ABI_RC.Core.Player; -using ABI_RC.Core.Savior; -using ABI.CCK.Components; -using UnityEngine; - -namespace NAK.RelativeSync.Components; - -public class RelativeSyncMarker : MonoBehaviour -{ - public int pathHash { get; private set; } - - public bool IsComponentActive - => _component.isActiveAndEnabled; - - public bool ApplyRelativePosition = true; - public bool ApplyRelativeRotation = true; - public bool OnlyApplyRelativeHeading; - - private MonoBehaviour _component; - - private void Start() - { - RegisterWithManager(); - ConfigureForPotentialMovementParent(); - } - - private void OnDestroy() - { - RelativeSyncManager.RelativeSyncTransforms.Remove(pathHash); - } - - public void OnHavingSoftTacosNow() - => RegisterWithManager(); - - private void RegisterWithManager() - { - // Remove old hash in case this is a re-registration - RelativeSyncManager.RelativeSyncTransforms.Remove(pathHash); - - string path = GetGameObjectPath(transform); - int hash = path.GetHashCode(); - - // check if it already exists (this **should** only matter in worlds) - if (RelativeSyncManager.RelativeSyncTransforms.ContainsKey(hash)) - { - RelativeSyncMod.Logger.Warning($"Duplicate RelativeSyncMarker found at path {path}"); - if (!FindAvailableHash(ref hash)) // super lazy fix idfc - { - RelativeSyncMod.Logger.Error($"Failed to find available hash for RelativeSyncMarker after 16 tries! {path}"); - return; - } - } - - pathHash = hash; - RelativeSyncManager.RelativeSyncTransforms.Add(hash, this); - } - - private void ConfigureForPotentialMovementParent() - { - if (!gameObject.TryGetComponent(out CVRMovementParent movementParent)) - { - _component = GetComponent(); // users cant animate enabled state so i dont think matters - return; - } - _component = movementParent; - - // TODO: a refactor may be needed to handle the orientation mode being animated - - // respect orientation mode & gravity zone - ApplyRelativeRotation = movementParent.orientationMode == CVRMovementParent.OrientationMode.RotateWithParent; - OnlyApplyRelativeHeading = movementParent.GetComponent() == null; - } - - private static string GetGameObjectPath(Transform transform) - { - // props already have a unique instance identifier at root - // worlds uhhhh, dont duplicate the same thing over and over thx - // avatars on remote/local client have diff path, we need to account for it -_- - - string path = transform.name; - while (transform.parent != null) - { - transform = transform.parent; - - // only true at root of local player object - if (transform.CompareTag("Player")) - { - path = MetaPort.Instance.ownerId + "/" + path; - break; - } // remote player object root is already player guid - - path = transform.name + "/" + path; - } - - return path; - } - - private static bool FindAvailableHash(ref int hash) - { - for (int i = 0; i < 16; i++) - { - hash += 1; - if (!RelativeSyncManager.RelativeSyncTransforms.ContainsKey(hash)) return true; - } - - // failed to find a hash in 16 tries, dont care - return false; - } -} diff --git a/RelativeSync/RelativeSync/Components/RelativeSyncMonitor.cs b/RelativeSync/RelativeSync/Components/RelativeSyncMonitor.cs deleted file mode 100644 index 134234c..0000000 --- a/RelativeSync/RelativeSync/Components/RelativeSyncMonitor.cs +++ /dev/null @@ -1,79 +0,0 @@ -using ABI_RC.Core.Player; -using ABI_RC.Systems.Movement; -using NAK.RelativeSync.Networking; -using UnityEngine; - -namespace NAK.RelativeSync.Components; - -[DefaultExecutionOrder(int.MaxValue)] -public class RelativeSyncMonitor : MonoBehaviour -{ - private BetterBetterCharacterController _characterController { get; set; } - - private RelativeSyncMarker _relativeSyncMarker; - private RelativeSyncMarker _lastRelativeSyncMarker; - - private void Start() - { - _characterController = GetComponent(); - } - - private void LateUpdate() - { - if (_characterController == null) - return; - - CheckForRelativeSyncMarker(); - - if (_relativeSyncMarker == null) - { - if (_lastRelativeSyncMarker == null) - return; - - // send empty position and rotation to stop syncing - SendEmptyPositionAndRotation(); - _lastRelativeSyncMarker = null; - return; - } - - _lastRelativeSyncMarker = _relativeSyncMarker; - - Animator avatarAnimator = PlayerSetup.Instance._animator; - if (avatarAnimator == null) - return; // i dont care to bother - - RelativeSyncManager.GetRelativeAvatarPositionsFromMarker( - avatarAnimator, _relativeSyncMarker.transform, - out Vector3 relativePosition, out Vector3 relativeRotation); - - ModNetwork.SetLatestRelativeSync( - _relativeSyncMarker.pathHash, - relativePosition, relativeRotation); - } - - private void CheckForRelativeSyncMarker() - { - if (_characterController._isSitting && _characterController._lastCvrSeat) - { - RelativeSyncMarker newMarker = _characterController._lastCvrSeat.GetComponent(); - _relativeSyncMarker = newMarker; - return; - } - - if (_characterController._previousMovementParent != null) - { - RelativeSyncMarker newMarker = _characterController._previousMovementParent.GetComponent(); - _relativeSyncMarker = newMarker; - return; - } - - // none found - _relativeSyncMarker = null; - } - - private void SendEmptyPositionAndRotation() - { - ModNetwork.SetLatestRelativeSync(RelativeSyncManager.NoTarget, - Vector3.zero, Vector3.zero); - } -} \ No newline at end of file diff --git a/RelativeSync/RelativeSync/RelativeSyncManager.cs b/RelativeSync/RelativeSync/RelativeSyncManager.cs deleted file mode 100644 index 052ca3c..0000000 --- a/RelativeSync/RelativeSync/RelativeSyncManager.cs +++ /dev/null @@ -1,71 +0,0 @@ -using ABI_RC.Core.Base; -using ABI_RC.Core.Player; -using NAK.RelativeSync.Components; -using UnityEngine; - -namespace NAK.RelativeSync; - -public static class RelativeSyncManager -{ - public const int NoTarget = -1; - - public static readonly Dictionary RelativeSyncTransforms = new(); - public static readonly Dictionary RelativeSyncControllers = new(); - public static readonly Dictionary NetIkControllersToRelativeSyncControllers = new(); - - public static void ApplyRelativeSync(string userId, int target, Vector3 position, Vector3 rotation) - { - if (!RelativeSyncControllers.TryGetValue(userId, out RelativeSyncController controller)) - { - if (CVRPlayerManager.Instance.GetPlayerPuppetMaster(userId, out PuppetMaster pm)) - { - controller = pm.AddComponentIfMissing(); - RelativeSyncMod.Logger.Msg($"Found PuppetMaster for user {userId}. This user is now eligible for relative sync."); - } - else - { - RelativeSyncControllers.Add(userId, null); // add null controller to prevent future lookups - RelativeSyncMod.Logger.Warning($"Failed to find PuppetMaster for user {userId}. This is likely because the user is blocked or has blocked you. This user will not be eligible for relative sync until next game restart."); - } - } - - if (controller == null) - return; - - // find target transform - RelativeSyncMarker syncMarker = null; - if (target != NoTarget) RelativeSyncTransforms.TryGetValue(target, out syncMarker); - - controller.SetRelativeSyncMarker(syncMarker); - controller.SetRelativePositions(position, rotation); - } - - public static void GetRelativeAvatarPositionsFromMarker( - Animator avatarAnimator, Transform markerTransform, - out Vector3 relativePosition, out Vector3 relativeRotation) - // out Vector3 relativeHipPosition, out Vector3 relativeHipRotation) - { - Transform avatarTransform = avatarAnimator.transform; - - // because our syncing is retarded, we need to sync relative from the avatar root... - Vector3 avatarRootPosition = avatarTransform.position; // PlayerSetup.Instance.GetPlayerPosition() - Quaternion avatarRootRotation = avatarTransform.rotation; // PlayerSetup.Instance.GetPlayerRotation() - - relativePosition = markerTransform.InverseTransformPoint(avatarRootPosition); - relativeRotation = (Quaternion.Inverse(markerTransform.rotation) * avatarRootRotation).eulerAngles; - - // Transform hipTrans = (avatarAnimator.avatar != null && avatarAnimator.isHuman) - // ? avatarAnimator.GetBoneTransform(HumanBodyBones.Hips) : null; - // - // if (hipTrans == null) - // { - // relativeHipPosition = Vector3.zero; - // relativeHipRotation = Vector3.zero; - // } - // else - // { - // relativeHipPosition = markerTransform.InverseTransformPoint(hipTrans.position); - // relativeHipRotation = (Quaternion.Inverse(markerTransform.rotation) * hipTrans.rotation).eulerAngles; - // } - } -} \ No newline at end of file diff --git a/RelativeSync/format.json b/RelativeSync/format.json deleted file mode 100644 index a5346fa..0000000 --- a/RelativeSync/format.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "_id": 211, - "name": "RelativeSync", - "modversion": "1.0.5", - "gameversion": "2025r179", - "loaderversion": "0.6.1", - "modtype": "Mod", - "author": "NotAKidoS", - "description": "Relative sync for Movement Parent & Chairs. Requires both users to have the mod installed. Synced over Mod Network.\n\nProvides some Experimental settings to also fix local jitter on movement parents.", - "searchtags": [ - "relative", - "sync", - "movement", - "chair" - ], - "requirements": [ - "None" - ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/RelativeSync.dll", - "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/RelativeSync/", - "changelog": "- Recompiled for 2025r179\n- Fixed execution order of RelativeSyncController so moving at high speeds with passengers does not disrupt voice or avatar distance hider", - "embedcolor": "#f61963" -} \ No newline at end of file diff --git a/RelativeSyncJitterFix/Main.cs b/RelativeSyncJitterFix/Main.cs new file mode 100644 index 0000000..19863d1 --- /dev/null +++ b/RelativeSyncJitterFix/Main.cs @@ -0,0 +1,25 @@ +using MelonLoader; + +namespace NAK.RelativeSyncJitterFix; + +public class RelativeSyncJitterFixMod : MelonMod +{ + public override void OnInitializeMelon() + { + // Experimental sync hack + ApplyPatches(typeof(Patches.CVRSpawnablePatches)); + } + + private void ApplyPatches(Type type) + { + try + { + HarmonyInstance.PatchAll(type); + } + catch (Exception e) + { + LoggerInstance.Msg($"Failed while patching {type.Name}!"); + LoggerInstance.Error(e); + } + } +} \ No newline at end of file diff --git a/RelativeSyncJitterFix/Patches.cs b/RelativeSyncJitterFix/Patches.cs new file mode 100644 index 0000000..71a67f7 --- /dev/null +++ b/RelativeSyncJitterFix/Patches.cs @@ -0,0 +1,22 @@ +using ABI.CCK.Components; +using HarmonyLib; + +namespace NAK.RelativeSyncJitterFix.Patches; + +internal static class CVRSpawnablePatches +{ + private static bool _canUpdate; + + [HarmonyPostfix] + [HarmonyPatch(typeof(CVRSpawnable), nameof(CVRSpawnable.FixedUpdate))] + private static void Postfix_CVRSpawnable_FixedUpdate(ref CVRSpawnable __instance) + { + _canUpdate = true; + __instance.Update(); + _canUpdate = false; + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(CVRSpawnable), nameof(CVRSpawnable.Update))] + private static bool Prefix_CVRSpawnable_Update() => _canUpdate; +} \ No newline at end of file diff --git a/RelativeSync/Properties/AssemblyInfo.cs b/RelativeSyncJitterFix/Properties/AssemblyInfo.cs similarity index 65% rename from RelativeSync/Properties/AssemblyInfo.cs rename to RelativeSyncJitterFix/Properties/AssemblyInfo.cs index ef60248..ff4fcd8 100644 --- a/RelativeSync/Properties/AssemblyInfo.cs +++ b/RelativeSyncJitterFix/Properties/AssemblyInfo.cs @@ -1,32 +1,32 @@ -using NAK.RelativeSync.Properties; +using NAK.RelativeSyncJitterFix.Properties; using MelonLoader; using System.Reflection; [assembly: AssemblyVersion(AssemblyInfoParams.Version)] [assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] [assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyTitle(nameof(NAK.RelativeSync))] +[assembly: AssemblyTitle(nameof(NAK.RelativeSyncJitterFix))] [assembly: AssemblyCompany(AssemblyInfoParams.Author)] -[assembly: AssemblyProduct(nameof(NAK.RelativeSync))] +[assembly: AssemblyProduct(nameof(NAK.RelativeSyncJitterFix))] [assembly: MelonInfo( - typeof(NAK.RelativeSync.RelativeSyncMod), - nameof(NAK.RelativeSync), + typeof(NAK.RelativeSyncJitterFix.RelativeSyncJitterFixMod), + nameof(NAK.RelativeSyncJitterFix), AssemblyInfoParams.Version, AssemblyInfoParams.Author, - downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/RelativeSync" + downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/RelativeSyncJitterFix" )] -[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")] +[assembly: MelonGame("ChilloutVR", "ChilloutVR")] [assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] [assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)] [assembly: MelonColor(255, 246, 25, 99)] // red-pink [assembly: MelonAuthorColor(255, 158, 21, 32)] // red [assembly: HarmonyDontPatchAll] -namespace NAK.RelativeSync.Properties; +namespace NAK.RelativeSyncJitterFix.Properties; internal static class AssemblyInfoParams { - public const string Version = "1.0.5"; + public const string Version = "1.0.0"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/RelativeSyncJitterFix/README.md b/RelativeSyncJitterFix/README.md new file mode 100644 index 0000000..7978ed2 --- /dev/null +++ b/RelativeSyncJitterFix/README.md @@ -0,0 +1,21 @@ +# RelativeSyncJitterFix + +Relative sync jitter fix is the single harmony patch that could not make it into the native release of RelativeSync. +Changes when props apply their incoming sync data to be before the character controller simulation. + +## Known Issues +- Movement Parents on remote users will still locally jitter. + - PuppetMaster/NetIkController applies received position updates in LateUpdate, while character controller updates in FixedUpdate. +- Movement Parents using CVRObjectSync synced by remote users will still locally jitter. + - CVRObjectSync applies received position updates in LateUpdate, while character controller updates in FixedUpdate. + +--- + +Here is the block of text where I tell you this mod is not affiliated with or endorsed by ChilloutVR. +https://docs.chilloutvr.net/official/legal/tos/#7-modding-our-games + +> This mod is an independent creation not affiliated with, supported by, or approved by ChilloutVR. + +> 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 ChilloutVR. diff --git a/RelativeSync/RelativeSync.csproj b/RelativeSyncJitterFix/RelativeSyncJitterFix.csproj similarity index 100% rename from RelativeSync/RelativeSync.csproj rename to RelativeSyncJitterFix/RelativeSyncJitterFix.csproj diff --git a/RelativeSyncJitterFix/format.json b/RelativeSyncJitterFix/format.json new file mode 100644 index 0000000..b128e2a --- /dev/null +++ b/RelativeSyncJitterFix/format.json @@ -0,0 +1,23 @@ +{ + "_id": -1, + "name": "RelativeSyncJitterFix", + "modversion": "1.0.0", + "gameversion": "2025r180", + "loaderversion": "0.7.2", + "modtype": "Mod", + "author": "NotAKidoS", + "description": "Relative sync jitter fix is the single harmony patch that could not make it into the native release of RelativeSync.\nChanges when props apply their incoming sync data to be before the character controller simulation.", + "searchtags": [ + "relative", + "sync", + "movement", + "chair" + ], + "requirements": [ + "None" + ], + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/RelativeSyncJitterFix.dll", + "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/RelativeSyncJitterFix/", + "changelog": "- Removed RelativeSync except for a single harmony patch", + "embedcolor": "#f61963" +} \ No newline at end of file From 1496c25184aa87d04fb0ee7402e6c19d28a9fbc7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 27 Aug 2025 06:35:11 +0000 Subject: [PATCH 5/7] [NAK_CVR_Mods] Update mod list in README --- README.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 76534fa..2d2371e 100644 --- a/README.md +++ b/README.md @@ -6,22 +6,22 @@ | Name | Description | Download | |------|-------------|----------| -| [ASTExtension](ASTExtension/README.md) | Extension mod for [Avatar Scale Tool](https://github.com/NotAKidoS/AvatarScaleTool): | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/ASTExtension.dll) | -| [AvatarQueueSystemTweaks](AvatarQueueSystemTweaks/README.md) | Small tweaks to the Avatar Queue System. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/AvatarQueueSystemTweaks.dll) | +| [ASTExtension](ASTExtension/README.md) | Extension mod for [Avatar Scale Tool](https://github.com/NotAKidoS/AvatarScaleTool): | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/ASTExtension.dll) | +| [AvatarQueueSystemTweaks](AvatarQueueSystemTweaks/README.md) | Small tweaks to the Avatar Queue System. | No Download | | [ConfigureCalibrationPose](ConfigureCalibrationPose/README.md) | Select FBT calibration pose. | No Download | -| [CustomSpawnPoint](CustomSpawnPoint/README.md) | Replaces the unused Images button in the World Details page with a button to set a custom spawn point. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/CustomSpawnPoint.dll) | -| [DoubleTapJumpToExitSeat](DoubleTapJumpToExitSeat/README.md) | Replaces seat exit controls with a double-tap of the jump button, avoiding accidental exits from joystick drift or opening the menu. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/DoubleTapJumpToExitSeat.dll) | -| [FuckToes](FuckToes/README.md) | Prevents VRIK from autodetecting toes in Halfbody or Fullbody. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/FuckToes.dll) | -| [KeepVelocityOnExitFlight](KeepVelocityOnExitFlight/README.md) | Keeps the player's velocity when exiting flight mode. Makes it possible to fling yourself like in Garry's Mod. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/KeepVelocityOnExitFlight.dll) | -| [LazyPrune](LazyPrune/README.md) | Prevents loaded objects from immediately unloading on destruction. Should prevent needlessly unloading & reloading all avatars/props on world rejoin or GS reconnection. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/LazyPrune.dll) | -| [PropLoadingHexagon](PropLoadingHexagon/README.md) | https://github.com/NotAKidoS/NAK_CVR_Mods/assets/37721153/a892c765-71c1-47f3-a781-bdb9b60ba117 | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/PropLoadingHexagon.dll) | -| [RCCVirtualSteeringWheel](RCCVirtualSteeringWheel/README.md) | Allows you to physically grab rigged RCC steering wheels in VR to provide steering input. No explicit setup required other than defining the Steering Wheel transform within the RCC component. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/RCCVirtualSteeringWheel.dll) | -| [RelativeSync](RelativeSync/README.md) | Relative sync for Movement Parent & Chairs. Requires both users to have the mod installed. Synced over Mod Network. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/RelativeSync.dll) | -| [ShareBubbles](ShareBubbles/README.md) | Share Bubbles! Allows you to drop down bubbles containing Avatars & Props. Requires both users to have the mod installed. Synced over Mod Network. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/ShareBubbles.dll) | -| [SmootherRay](SmootherRay/README.md) | Smoothes your controller while the raycast lines are visible. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/SmootherRay.dll) | -| [Stickers](Stickers/README.md) | Stickers! Allows you to place small images on any surface. Requires both users to have the mod installed. Synced over Mod Network. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/Stickers.dll) | -| [ThirdPerson](ThirdPerson/README.md) | Original repo: https://github.com/oestradiol/CVR-Mods | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/ThirdPerson.dll) | -| [YouAreMyPropNowWeAreHavingSoftTacosLater](YouAreMyPropNowWeAreHavingSoftTacosLater/README.md) | Lets you bring held, attached, and occupied props through world loads. This is configurable in the mod settings. | No Download | +| [CustomSpawnPoint](CustomSpawnPoint/README.md) | Replaces the unused Images button in the World Details page with a button to set a custom spawn point. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/CustomSpawnPoint.dll) | +| [DoubleTapJumpToExitSeat](DoubleTapJumpToExitSeat/README.md) | Replaces seat exit controls with a double-tap of the jump button, avoiding accidental exits from joystick drift or opening the menu. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/DoubleTapJumpToExitSeat.dll) | +| [FuckToes](FuckToes/README.md) | Prevents VRIK from autodetecting toes in Halfbody or Fullbody. | No Download | +| [KeepVelocityOnExitFlight](KeepVelocityOnExitFlight/README.md) | Keeps the player's velocity when exiting flight mode. Makes it possible to fling yourself like in Garry's Mod. | No Download | +| [LazyPrune](LazyPrune/README.md) | Prevents loaded objects from immediately unloading on destruction. Should prevent needlessly unloading & reloading all avatars/props on world rejoin or GS reconnection. | No Download | +| [PropLoadingHexagon](PropLoadingHexagon/README.md) | https://github.com/NotAKidoS/NAK_CVR_Mods/assets/37721153/a892c765-71c1-47f3-a781-bdb9b60ba117 | No Download | +| [RCCVirtualSteeringWheel](RCCVirtualSteeringWheel/README.md) | Allows you to physically grab rigged RCC steering wheels in VR to provide steering input. No explicit setup required other than defining the Steering Wheel transform within the RCC component. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/RCCVirtualSteeringWheel.dll) | +| [RelativeSyncJitterFix](RelativeSyncJitterFix/README.md) | Relative sync jitter fix is the single harmony patch that could not make it into the native release of RelativeSync. | No Download | +| [ShareBubbles](ShareBubbles/README.md) | Share Bubbles! Allows you to drop down bubbles containing Avatars & Props. Requires both users to have the mod installed. Synced over Mod Network. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/ShareBubbles.dll) | +| [SmootherRay](SmootherRay/README.md) | Smoothes your controller while the raycast lines are visible. | No Download | +| [Stickers](Stickers/README.md) | Stickers! Allows you to place small images on any surface. Requires both users to have the mod installed. Synced over Mod Network. | No Download | +| [ThirdPerson](ThirdPerson/README.md) | Original repo: https://github.com/oestradiol/CVR-Mods | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/ThirdPerson.dll) | +| [YouAreMyPropNowWeAreHavingSoftTacosLater](YouAreMyPropNowWeAreHavingSoftTacosLater/README.md) | Lets you bring held, attached, and occupied props through world loads. This is configurable in the mod settings. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/YouAreMyPropNowWeAreHavingSoftTacosLater.dll) | ### Experimental Mods @@ -29,7 +29,7 @@ |------|-------------|----------| | [CVRLuaToolsExtension](.Experimental/CVRLuaToolsExtension/README.md) | Extension mod for [CVRLuaTools](https://github.com/NotAKidoS/CVRLuaTools) Hot Reload functionality. | No Download | | [CustomRichPresence](.Experimental/CustomRichPresence/README.md) | Lets you customize the Steam & Discord rich presence messages & values. | No Download | -| [LuaNetworkVariables](.Experimental/LuaNetworkVariables/README.md) | Adds a simple module for creating network variables & events *kinda* similar to Garry's Mod. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/LuaNetworkVariables.dll) | +| [LuaNetworkVariables](.Experimental/LuaNetworkVariables/README.md) | Adds a simple module for creating network variables & events *kinda* similar to Garry's Mod. | No Download | | [LuaTTS](.Experimental/LuaTTS/README.md) | Provides access to the built-in text-to-speech (TTS) functionality to lua scripts. Allows you to make the local player speak. | No Download | | [OriginShift](.Experimental/OriginShift/README.md) | Experimental mod that allows world origin to be shifted to prevent floating point precision issues. | No Download | | [ScriptingSpoofer](.Experimental/ScriptingSpoofer/README.md) | Prevents **local** scripts from accessing your Username or UserID by spoofing them with random values each session. | No Download | From 6bef1d0c9694a3704b5747a619a23eaa7defbf03 Mon Sep 17 00:00:00 2001 From: NotAKidoS <37721153+NotAKidoS@users.noreply.github.com> Date: Wed, 27 Aug 2025 05:21:26 -0500 Subject: [PATCH 6/7] [Tinyboard] Initial release --- Tinyboard/Main.cs | 346 +++++++++++++++++++++++++++ Tinyboard/Properties/AssemblyInfo.cs | 32 +++ Tinyboard/README.md | 19 ++ Tinyboard/Tinyboard.csproj | 6 + Tinyboard/format.json | 23 ++ 5 files changed, 426 insertions(+) create mode 100644 Tinyboard/Main.cs create mode 100644 Tinyboard/Properties/AssemblyInfo.cs create mode 100644 Tinyboard/README.md create mode 100644 Tinyboard/Tinyboard.csproj create mode 100644 Tinyboard/format.json diff --git a/Tinyboard/Main.cs b/Tinyboard/Main.cs new file mode 100644 index 0000000..540d150 --- /dev/null +++ b/Tinyboard/Main.cs @@ -0,0 +1,346 @@ +using System.Reflection; +using ABI_RC.Core.InteractionSystem; +using ABI_RC.Core.Savior; +using ABI_RC.Core.UI; +using ABI_RC.Core.UI.UIRework.Managers; +using ABI_RC.Systems.VRModeSwitch; +using ABI_RC.VideoPlayer.Scripts; +using HarmonyLib; +using MelonLoader; +using TMPro; +using UnityEngine; +using UnityEngine.UI; + +namespace NAK.Tinyboard; + +public class TinyboardMod : MelonMod +{ + #region Melon Preferences + + private static readonly MelonPreferences_Category Category = + MelonPreferences.CreateCategory(nameof(Tinyboard)); + + private static readonly MelonPreferences_Entry EntrySmartAlignToMenu = + Category.CreateEntry( + identifier: "smart_align_to_menu", + true, + display_name: "Smart Align To Menu", + description: "Should the keyboard align to the menu it was opened from? (Main Menu, World-Anchored Quick Menu)"); + + private static readonly MelonPreferences_Entry EntryEnforceTitle = + Category.CreateEntry( + identifier: "enforce_title", + true, + display_name: "Enforce Title", + description: "Should the keyboard enforce a title when opened from an input field or main menu?"); + + private static readonly MelonPreferences_Entry EntryResizeKeyboard = + Category.CreateEntry( + identifier: "resize_keyboard", + true, + display_name: "Resize Keyboard", + description: "Should the keyboard be resized to match XSOverlays width?"); + + private static readonly MelonPreferences_Entry EntryUseModifiers = + Category.CreateEntry( + identifier: "use_scale_distance_modifiers", + true, + display_name: "Use Scale/Distance/Offset Modifiers", + description: "Should the scale/distance/offset modifiers be used?"); + + private static readonly MelonPreferences_Entry EntryDesktopScaleModifier = + Category.CreateEntry( + identifier: "desktop_scale_modifier", + 0.75f, + display_name: "Desktop Scale Modifier", + description: "Scale modifier for desktop mode."); + + private static readonly MelonPreferences_Entry EntryDesktopDistance = + Category.CreateEntry( + identifier: "desktop_distance_modifier", + 0f, + display_name: "Desktop Distance Modifier", + description: "Distance modifier for desktop mode."); + + private static readonly MelonPreferences_Entry EntryDesktopVerticalAdjustment = + Category.CreateEntry( + identifier: "desktop_vertical_adjustment", + 0.1f, + display_name: "Desktop Vertical Adjustment", + description: "Vertical adjustment for desktop mode."); + + private static readonly MelonPreferences_Entry EntryVRScaleModifier = + Category.CreateEntry( + identifier: "vr_scale_modifier", + 0.85f, + display_name: "VR Scale Modifier", + description: "Scale modifier for VR mode."); + + private static readonly MelonPreferences_Entry EntryVRDistance = + Category.CreateEntry( + identifier: "vr_distance_modifier", + 0.2f, + display_name: "VR Distance Modifier", + description: "Distance modifier for VR mode."); + + private static readonly MelonPreferences_Entry EntryVRVerticalAdjustment = + Category.CreateEntry( + identifier: "vr_vertical_adjustment", + 0f, + display_name: "VR Vertical Adjustment", + description: "Vertical adjustment for VR mode."); + + #endregion Melon Preferences + + private static Transform _tinyBoardOffset; + private static void ApplyTinyBoardOffsetsForVRMode() + { + if (!EntryUseModifiers.Value) + { + _tinyBoardOffset.localScale = Vector3.one; + _tinyBoardOffset.localPosition = Vector3.zero; + return; + } + float distanceModifier; + float scaleModifier; + float verticalAdjustment; + if (MetaPort.Instance.isUsingVr) + { + scaleModifier = EntryVRScaleModifier.Value; + distanceModifier = EntryVRDistance.Value; + verticalAdjustment = EntryVRVerticalAdjustment.Value; + } + else + { + scaleModifier = EntryDesktopScaleModifier.Value; + distanceModifier = EntryDesktopDistance.Value; + verticalAdjustment = EntryDesktopVerticalAdjustment.Value; + } + _tinyBoardOffset.localScale = Vector3.one * scaleModifier; + _tinyBoardOffset.localPosition = new Vector3(0f, verticalAdjustment, distanceModifier); + } + + private static void ApplyTinyBoardWidthResize() + { + KeyboardManager km = KeyboardManager.Instance; + CohtmlControlledView cohtmlView = km.cohtmlView; + Transform keyboardTransform = cohtmlView.transform; + + int targetWidthPixels = EntryResizeKeyboard.Value ? 1330 : 1520; + float targetScaleX = EntryResizeKeyboard.Value ? 1.4f : 1.6f; + + cohtmlView.Width = targetWidthPixels; + Vector3 currentScale = keyboardTransform.localScale; + currentScale.x = targetScaleX; + keyboardTransform.localScale = currentScale; + } + + public override void OnInitializeMelon() + { + // add our shim transform to scale the menu down by 0.75 + HarmonyInstance.Patch( + typeof(CVRKeyboardPositionHelper).GetMethod(nameof(CVRKeyboardPositionHelper.Awake), + BindingFlags.NonPublic | BindingFlags.Instance), + postfix: new HarmonyMethod(typeof(TinyboardMod).GetMethod(nameof(OnCVRKeyboardPositionHelperAwake), + BindingFlags.NonPublic | BindingFlags.Static)) + ); + + // reposition the keyboard when it is opened to match the menu position if it is opened from a menu + HarmonyInstance.Patch( + typeof(MenuPositionHelperBase).GetMethod(nameof(MenuPositionHelperBase.OnMenuOpen), + BindingFlags.NonPublic | BindingFlags.Instance), + postfix: new HarmonyMethod(typeof(TinyboardMod).GetMethod(nameof(OnMenuPositionHelperBaseOnMenuOpen), + BindingFlags.NonPublic | BindingFlags.Static)) + ); + + // enforces a title for the keyboard in cases it did not already have one + HarmonyInstance.Patch( + typeof(KeyboardManager).GetMethod(nameof(KeyboardManager.ShowKeyboard), + BindingFlags.Public | BindingFlags.Instance), + prefix: new HarmonyMethod(typeof(TinyboardMod).GetMethod(nameof(OnKeyboardManagerShowKeyboard), + BindingFlags.NonPublic | BindingFlags.Static)) + ); + + // resize keyboard to match XSOverlays width + HarmonyInstance.Patch( + typeof(KeyboardManager).GetMethod(nameof(KeyboardManager.Start), + BindingFlags.NonPublic | BindingFlags.Instance), + postfix: new HarmonyMethod(typeof(TinyboardMod).GetMethod(nameof(OnKeyboardManagerStart), + BindingFlags.NonPublic | BindingFlags.Static)) + ); + + // update offsets when switching VR modes + VRModeSwitchEvents.OnPostVRModeSwitch.AddListener((_) => ApplyTinyBoardOffsetsForVRMode()); + + // listen for setting changes + EntryUseModifiers.OnEntryValueChanged.Subscribe((_,_) => ApplyTinyBoardOffsetsForVRMode()); + EntryDesktopScaleModifier.OnEntryValueChanged.Subscribe((_,_) => ApplyTinyBoardOffsetsForVRMode()); + EntryVRScaleModifier.OnEntryValueChanged.Subscribe((_,_) => ApplyTinyBoardOffsetsForVRMode()); + EntryDesktopDistance.OnEntryValueChanged.Subscribe((_,_) => ApplyTinyBoardOffsetsForVRMode()); + EntryVRDistance.OnEntryValueChanged.Subscribe((_,_) => ApplyTinyBoardOffsetsForVRMode()); + EntryResizeKeyboard.OnEntryValueChanged.Subscribe((_,_) => ApplyTinyBoardWidthResize()); + } + + private static void OnCVRKeyboardPositionHelperAwake(CVRKeyboardPositionHelper __instance) + { + _tinyBoardOffset = new GameObject("NAKTinyBoard").transform; + + Transform offsetTransform = __instance.transform.GetChild(0); + _tinyBoardOffset.SetParent(offsetTransform, false); + + ApplyTinyBoardOffsetsForVRMode(); + + Transform menuTransform = __instance.menuTransform; + menuTransform.SetParent(_tinyBoardOffset, false); + } + + private static void OnMenuPositionHelperBaseOnMenuOpen(MenuPositionHelperBase __instance) + { + if (!EntrySmartAlignToMenu.Value) return; + if (__instance is not CVRKeyboardPositionHelper { IsMenuOpen: true }) return; + + // Check if the open source was an open menu + KeyboardManager.OpenSource? openSource = KeyboardManager.Instance._keyboardOpenSource; + + MenuPositionHelperBase menuPositionHelper; + switch (openSource) + { + case KeyboardManager.OpenSource.MainMenu: + menuPositionHelper = CVRMainMenuPositionHelper.Instance; + break; + case KeyboardManager.OpenSource.QuickMenu: + menuPositionHelper = CVRQuickMenuPositionHelper.Instance; + if (!menuPositionHelper.IsUsingWorldAnchoredMenu) return; // hand anchored quick menu, don't touch + break; + default: return; + } + + // get modifiers + float rootScaleModifier = __instance.transform.lossyScale.x; + float keyboardDistanceModifier = __instance.MenuDistanceModifier; + float menuDistanceModifier = menuPositionHelper.MenuDistanceModifier; + + // get difference between modifiers + float distanceModifier = keyboardDistanceModifier - menuDistanceModifier; + + // place keyboard at menu position + difference in modifiers + Transform menuOffsetTransform = menuPositionHelper._offsetTransform; + Quaternion keyboardRotation = menuOffsetTransform.rotation; + Vector3 keyboardPosition = menuOffsetTransform.position + + menuOffsetTransform.forward * (rootScaleModifier * distanceModifier); + + // place keyboard as if it was opened with player camera in same place as menu was + __instance._offsetTransform.SetPositionAndRotation(keyboardPosition, keyboardRotation); + } + + private static void OnKeyboardManagerStart() => ApplyTinyBoardWidthResize(); + + /* + public void ShowKeyboard( + string currentText, + Action callback, + string placeholder = null, + string successText = "Success", + int maxCharacterCount = 0, + bool hidden = false, + bool multiLine = false, + string title = null, + OpenSource openSource = OpenSource.Other) + */ + + // using mix of index and args params because otherwise explodes with invalid IL ? + private static void OnKeyboardManagerShowKeyboard(ref string __7, ref string __2, object[] __args) + { + if (!EntryEnforceTitle.Value) return; + + // ReSharper disable thrice InlineTemporaryVariable + ref string title = ref __7; + ref string placeholder = ref __2; + if (!string.IsNullOrWhiteSpace(title)) return; + + Action callback = __args[1] as Action; + KeyboardManager.OpenSource? openSource = __args[8] as KeyboardManager.OpenSource?; + + if (callback?.Target != null) + { + var target = callback.Target; + switch (openSource) + { + case KeyboardManager.OpenSource.CVRInputFieldKeyboardHandler: + TrySetPlaceholderFromKeyboardHandler(target, ref title, ref placeholder); + break; + case KeyboardManager.OpenSource.MainMenu: + title = TryExtractTitleFromMainMenu(target); + break; + } + } + + if (!string.IsNullOrWhiteSpace(placeholder)) + { + // fallback to placeholder if no title found + if (string.IsNullOrWhiteSpace(title)) title = placeholder; + + // clear placeholder if it is longer than 10 characters + if (placeholder.Length > 10) placeholder = string.Empty; + } + } + + private static void TrySetPlaceholderFromKeyboardHandler(object target, ref string title, ref string placeholder) + { + Type type = target.GetType(); + + TMP_InputField tmpInput = type.GetField("input", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(target) as TMP_InputField; + if (tmpInput != null) + { + if (tmpInput.GetComponentInParent()) title = "VideoPlayer URL or Search"; + if (tmpInput.placeholder is TMP_Text ph) + { + placeholder = ph.text; + return; + } + placeholder = PrettyString(tmpInput.gameObject.name); + return; + } + + InputField legacyInput = type.GetField("inputField", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(target) as InputField; + if (legacyInput != null) + { + if (legacyInput.placeholder is Text ph) + { + placeholder = ph.text; + return; + } + placeholder = PrettyString(legacyInput.gameObject.name); + return; + } + } + + private static string TryExtractTitleFromMainMenu(object target) + { + Type type = target.GetType(); + string targetId = type.GetField("targetId", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(target) as string; + return string.IsNullOrWhiteSpace(targetId) ? null : PrettyString(targetId); + } + + private static string PrettyString(string str) + { + int len = str.Length; + Span buffer = stackalloc char[len * 2]; + int pos = 0; + bool newWord = true; + for (int i = 0; i < len; i++) + { + char c = str[i]; + if (c is '_' or '-') + { + buffer[pos++] = ' '; + newWord = true; + continue; + } + if (char.IsUpper(c) && i > 0 && !newWord) buffer[pos++] = ' '; + buffer[pos++] = newWord ? char.ToUpperInvariant(c) : c; + newWord = false; + } + return new string(buffer[..pos]); + } +} \ No newline at end of file diff --git a/Tinyboard/Properties/AssemblyInfo.cs b/Tinyboard/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..b3048a5 --- /dev/null +++ b/Tinyboard/Properties/AssemblyInfo.cs @@ -0,0 +1,32 @@ +using MelonLoader; +using NAK.Tinyboard.Properties; +using System.Reflection; + +[assembly: AssemblyVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyTitle(nameof(NAK.Tinyboard))] +[assembly: AssemblyCompany(AssemblyInfoParams.Author)] +[assembly: AssemblyProduct(nameof(NAK.Tinyboard))] + +[assembly: MelonInfo( + typeof(NAK.Tinyboard.TinyboardMod), + nameof(NAK.Tinyboard), + AssemblyInfoParams.Version, + AssemblyInfoParams.Author, + downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/Tinyboard" +)] + +[assembly: MelonGame("ChilloutVR", "ChilloutVR")] +[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] +[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)] +[assembly: MelonColor(255, 246, 25, 99)] // red-pink +[assembly: MelonAuthorColor(255, 158, 21, 32)] // red +[assembly: HarmonyDontPatchAll] + +namespace NAK.Tinyboard.Properties; +internal static class AssemblyInfoParams +{ + public const string Version = "1.0.0"; + public const string Author = "NotAKidoS"; +} \ No newline at end of file diff --git a/Tinyboard/README.md b/Tinyboard/README.md new file mode 100644 index 0000000..7ff0922 --- /dev/null +++ b/Tinyboard/README.md @@ -0,0 +1,19 @@ +# Tinyboard + +Makes the keyboard small and smart. + +Few small tweaks to the keyboard: +- Shrinks the keyboard to a size that isn't fit for grandma. +- Adjusts keyboard placement logic to align with the menu that it spawns from. +- Enforces a title on the keyboard input if one is not found. + +--- + +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. diff --git a/Tinyboard/Tinyboard.csproj b/Tinyboard/Tinyboard.csproj new file mode 100644 index 0000000..5a8badc --- /dev/null +++ b/Tinyboard/Tinyboard.csproj @@ -0,0 +1,6 @@ + + + + YouAreMineNow + + diff --git a/Tinyboard/format.json b/Tinyboard/format.json new file mode 100644 index 0000000..da65a56 --- /dev/null +++ b/Tinyboard/format.json @@ -0,0 +1,23 @@ +{ + "_id": -1, + "name": "Tinyboard", + "modversion": "1.0.0", + "gameversion": "2025r180", + "loaderversion": "0.7.2", + "modtype": "Mod", + "author": "NotAKidoS", + "description": "Few small tweaks to the keyboard:\n- Shrinks the keyboard to a size that isn't fit for grandma.\n- Adjusts keyboard placement logic to align with the menu that it spawns from.\n- Enforces a title on the keyboard input if one is not found.", + "searchtags": [ + "keyboard", + "menu", + "ui", + "input" + ], + "requirements": [ + "None" + ], + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/Tinyboard.dll", + "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/Tinyboard/", + "changelog": "- Initial release", + "embedcolor": "#f61963" +} \ No newline at end of file From 226b3695378b56fc63a51cbfc8fa1f153309afa6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 27 Aug 2025 10:22:00 +0000 Subject: [PATCH 7/7] [NAK_CVR_Mods] Update mod list in README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2d2371e..ed9da33 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,12 @@ | [LazyPrune](LazyPrune/README.md) | Prevents loaded objects from immediately unloading on destruction. Should prevent needlessly unloading & reloading all avatars/props on world rejoin or GS reconnection. | No Download | | [PropLoadingHexagon](PropLoadingHexagon/README.md) | https://github.com/NotAKidoS/NAK_CVR_Mods/assets/37721153/a892c765-71c1-47f3-a781-bdb9b60ba117 | No Download | | [RCCVirtualSteeringWheel](RCCVirtualSteeringWheel/README.md) | Allows you to physically grab rigged RCC steering wheels in VR to provide steering input. No explicit setup required other than defining the Steering Wheel transform within the RCC component. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/RCCVirtualSteeringWheel.dll) | -| [RelativeSyncJitterFix](RelativeSyncJitterFix/README.md) | Relative sync jitter fix is the single harmony patch that could not make it into the native release of RelativeSync. | No Download | +| [RelativeSyncJitterFix](RelativeSyncJitterFix/README.md) | Relative sync jitter fix is the single harmony patch that could not make it into the native release of RelativeSync. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/RelativeSyncJitterFix.dll) | | [ShareBubbles](ShareBubbles/README.md) | Share Bubbles! Allows you to drop down bubbles containing Avatars & Props. Requires both users to have the mod installed. Synced over Mod Network. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/ShareBubbles.dll) | | [SmootherRay](SmootherRay/README.md) | Smoothes your controller while the raycast lines are visible. | No Download | | [Stickers](Stickers/README.md) | Stickers! Allows you to place small images on any surface. Requires both users to have the mod installed. Synced over Mod Network. | No Download | | [ThirdPerson](ThirdPerson/README.md) | Original repo: https://github.com/oestradiol/CVR-Mods | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/ThirdPerson.dll) | +| [Tinyboard](Tinyboard/README.md) | Makes the keyboard small and smart. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/Tinyboard.dll) | | [YouAreMyPropNowWeAreHavingSoftTacosLater](YouAreMyPropNowWeAreHavingSoftTacosLater/README.md) | Lets you bring held, attached, and occupied props through world loads. This is configurable in the mod settings. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/YouAreMyPropNowWeAreHavingSoftTacosLater.dll) | ### Experimental Mods