From b786ecd51cf660e6c28d10cbf1efbc6e5468bad1 Mon Sep 17 00:00:00 2001 From: NotAKidoS <37721153+NotAKidOnSteam@users.noreply.github.com> Date: Wed, 29 May 2024 13:41:11 -0500 Subject: [PATCH] [RelativeSync] Fixed buncha stuff, cleanup --- RelativeSync/Main.cs | 10 + RelativeSync/ModSettings.cs | 48 +++ RelativeSync/Networking/ModNetwork.cs | 284 +++++++++--------- RelativeSync/Patches.cs | 50 +++ RelativeSync/Properties/AssemblyInfo.cs | 2 +- RelativeSync/README.md | 6 + .../Components/RelativeSyncController.cs | 142 +++++---- .../Components/RelativeSyncMarker.cs | 46 ++- .../Components/RelativeSyncMonitor.cs | 33 +- .../RelativeSync/RelativeSyncManager.cs | 29 ++ RelativeSync/format.json | 10 +- 11 files changed, 429 insertions(+), 231 deletions(-) create mode 100644 RelativeSync/ModSettings.cs diff --git a/RelativeSync/Main.cs b/RelativeSync/Main.cs index 7f39255..83ec331 100644 --- a/RelativeSync/Main.cs +++ b/RelativeSync/Main.cs @@ -13,12 +13,22 @@ public class RelativeSyncMod : MelonMod Logger = LoggerInstance; ModNetwork.Subscribe(); + ModSettings.Initialize(); + // Experimental sync hack + ApplyPatches(typeof(CVRSpawnablePatches)); + + // Experimental no interpolation on Better Better Character Controller + ApplyPatches(typeof(BetterBetterCharacterControllerPatches)); + + // 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)); } diff --git a/RelativeSync/ModSettings.cs b/RelativeSync/ModSettings.cs new file mode 100644 index 0000000..833db71 --- /dev/null +++ b/RelativeSync/ModSettings.cs @@ -0,0 +1,48 @@ +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", false, + "Exp Spawnable Sync Fix", description: "Forces CVRSpawnable to update position in FixedUpdate. May help reduce local jitter on synced movement parents."); + + private static readonly MelonPreferences_Entry ExpNoInterpolationOnBBCC = + Category.CreateEntry("ExpNoInterpolationOnBBCC", false, + "Exp Disable Interpolation on BBCC", description: "Disable interpolation on Better Better Character Controller. 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; + Patches.BetterBetterCharacterControllerPatches.NoInterpolation = ExpNoInterpolationOnBBCC.Value; + } +} \ No newline at end of file diff --git a/RelativeSync/Networking/ModNetwork.cs b/RelativeSync/Networking/ModNetwork.cs index c196572..713f27c 100644 --- a/RelativeSync/Networking/ModNetwork.cs +++ b/RelativeSync/Networking/ModNetwork.cs @@ -3,149 +3,149 @@ using ABI_RC.Systems.ModNetwork; using DarkRift; using UnityEngine; -namespace NAK.RelativeSync.Networking +namespace NAK.RelativeSync.Networking; + +public static class ModNetwork { - public static class ModNetwork + public static bool Debug_NetworkInbound = false; + public static bool Debug_NetworkOutbound = false; + + private static bool _isSubscribedToModNetwork; + + private struct MovementParentSyncData { - public static bool Debug_NetworkInbound = false; - public static bool Debug_NetworkOutbound = false; - - private static bool _isSubscribedToModNetwork; - - private struct RelativeSyncData - { - public bool HasSyncedThisData; - public int MarkerHash; - public Vector3 Position; - public Vector3 Rotation; - } - - private static RelativeSyncData _latestRelativeSyncData; - - #region Constants - - private const string ModId = "MelonMod.NAK.RelativeSync"; - - #endregion - - #region Enums - - private enum MessageType : byte - { - SyncPosition = 0, - RelativeSyncStatus = 1 - } - - #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!"); - } - - internal static void SendRelativeSyncUpdate() - { - if (!_isSubscribedToModNetwork) - return; - - if (!_latestRelativeSyncData.HasSyncedThisData) - { - SendMessage(MessageType.SyncPosition, _latestRelativeSyncData.MarkerHash, - _latestRelativeSyncData.Position, _latestRelativeSyncData.Rotation); - _latestRelativeSyncData.HasSyncedThisData = true; - } - } - - private static void SetLatestRelativeSync(int markerHash, Vector3 position, Vector3 rotation) - { - // check if the data has changed - if (_latestRelativeSyncData.MarkerHash == markerHash - && _latestRelativeSyncData.Position == position - && _latestRelativeSyncData.Rotation == rotation) - return; // no need to update - - _latestRelativeSyncData.HasSyncedThisData = false; // reset - _latestRelativeSyncData.MarkerHash = markerHash; - _latestRelativeSyncData.Position = position; - _latestRelativeSyncData.Rotation = 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(); - } - - private static void OnMessageReceived(ModNetworkMessage msg) - { - msg.Read(out byte msgTypeRaw); - - if (!Enum.IsDefined(typeof(MessageType), msgTypeRaw)) - return; - - switch ((MessageType)msgTypeRaw) - { - case MessageType.SyncPosition: - msg.Read(out int markerHash); - msg.Read(out Vector3 receivedPosition); - msg.Read(out Vector3 receivedRotation); - OnNetworkPositionUpdateReceived(msg.Sender, markerHash, receivedPosition, receivedRotation); - break; - // case MessageType.RelativeSyncStatus: - // msg.Read(out string guidStr); - // msg.Read(out bool isRelativeSync); - // System.Guid guid = new System.Guid(guidStr); - // OnRelativeSyncStatusReceived(msg.Sender, guid, isRelativeSync); - // break; - default: - Debug.LogError($"Invalid message type received from: {msg.Sender}"); - break; - } - } - - #endregion - - #region Public Methods - - public static void SendNetworkPosition(int markerHash, Vector3 newPosition, Vector3 newRotation) - { - SetLatestRelativeSync(markerHash, newPosition, newRotation); - } - - #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); - } - - private static void OnRelativeSyncStatusReceived(string sender, System.Guid guid, bool isRelativeSync) - { - // todo: implement - } - - #endregion + 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 index f145aa0..59449a5 100644 --- a/RelativeSync/Patches.cs +++ b/RelativeSync/Patches.cs @@ -2,10 +2,12 @@ using ABI_RC.Core.InteractionSystem; using ABI_RC.Core.Networking.Jobs; using ABI_RC.Core.Player; +using ABI_RC.Systems.Movement; using ABI.CCK.Components; using HarmonyLib; using NAK.RelativeSync.Components; using NAK.RelativeSync.Networking; +using UnityEngine; namespace NAK.RelativeSync.Patches; @@ -58,4 +60,52 @@ internal static class NetworkRootDataUpdatePatches { 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 BetterBetterCharacterControllerPatches +{ + private static bool _noInterpolation; + internal static bool NoInterpolation + { + get => _noInterpolation; + set + { + _noInterpolation = value; + if (_rigidbody == null) return; + _rigidbody.interpolation = value ? RigidbodyInterpolation.None : RigidbodyInterpolation.Interpolate; + } + } + + private static Rigidbody _rigidbody; + + [HarmonyPostfix] + [HarmonyPatch(typeof(BetterBetterCharacterController), nameof(BetterBetterCharacterController.Start))] + private static void Postfix_BetterBetterCharacterController_Update(ref BetterBetterCharacterController __instance) + { + _rigidbody = __instance.GetComponent(); + NoInterpolation = _noInterpolation; // get initial value as patch runs later than settings init + } } \ No newline at end of file diff --git a/RelativeSync/Properties/AssemblyInfo.cs b/RelativeSync/Properties/AssemblyInfo.cs index a42a018..a8dda42 100644 --- a/RelativeSync/Properties/AssemblyInfo.cs +++ b/RelativeSync/Properties/AssemblyInfo.cs @@ -27,6 +27,6 @@ using System.Reflection; namespace NAK.RelativeSync.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/RelativeSync/README.md b/RelativeSync/README.md index 6654906..6afaf2c 100644 --- a/RelativeSync/README.md +++ b/RelativeSync/README.md @@ -4,6 +4,12 @@ Relative sync for Movement Parent & Chairs. Requires both users to have the mod https://github.com/NotAKidOnSteam/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. + --- Here is the block of text where I tell you this mod is not affiliated with or endorsed by ABI. diff --git a/RelativeSync/RelativeSync/Components/RelativeSyncController.cs b/RelativeSync/RelativeSync/Components/RelativeSyncController.cs index 2602d68..0b612d5 100644 --- a/RelativeSync/RelativeSync/Components/RelativeSyncController.cs +++ b/RelativeSync/RelativeSync/Components/RelativeSyncController.cs @@ -6,16 +6,13 @@ namespace NAK.RelativeSync.Components; [DefaultExecutionOrder(int.MaxValue)] // make sure this runs after NetIKController public class RelativeSyncController : MonoBehaviour { - private static float MaxMagnitude = 750000000000f; - + private const float MaxMagnitude = 750000000000f; + private float _updateInterval = 0.05f; private float _lastUpdate; private string _userId; private PuppetMaster puppetMaster { get; set; } - private NetIKController netIkController { get; set; } - - // private bool _syncMarkerChangedSinceLastSync; private RelativeSyncMarker _relativeSyncMarker; private RelativeSyncData _relativeSyncData; @@ -26,7 +23,6 @@ public class RelativeSyncController : MonoBehaviour private void Start() { puppetMaster = GetComponent(); - netIkController = GetComponent(); _userId = puppetMaster._playerDescriptor.ownerId; RelativeSyncManager.RelativeSyncControllers.Add(_userId, this); @@ -50,60 +46,33 @@ public class RelativeSyncController : MonoBehaviour return; Transform avatarTransform = animator.transform; - - Vector3 worldRootPos = avatarTransform.position; - Quaternion worldRootRot = avatarTransform.rotation; - + Transform hipTrans = (animator.avatar != null && animator.isHuman) + ? animator.GetBoneTransform(HumanBodyBones.Hips) : null; + Vector3 relativeHipPos = default; Quaternion relativeHipRot = default; - Transform hipTrans = animator.GetBoneTransform(HumanBodyBones.Hips); 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); - Vector3 targetLocalPosition = _relativeSyncData.LocalRootPosition; - Quaternion targetLocalRotation = Quaternion.Euler(_relativeSyncData.LocalRootRotation); - Transform targetTransform = _relativeSyncMarker.transform; - - if (_relativeSyncMarker.ApplyRelativeRotation && _relativeSyncData.LocalRootRotation.sqrMagnitude < MaxMagnitude) - { - Quaternion rotation = targetTransform.rotation; - Quaternion worldRotation = rotation * targetLocalRotation; - Quaternion lastRotation = rotation * Quaternion.Euler(_lastSyncData.LocalRootRotation); - - if (_relativeSyncMarker.OnlyApplyRelativeHeading) - { - Vector3 currentForward = lastRotation * Vector3.forward; - Vector3 targetForward = worldRotation * Vector3.forward; - Vector3 currentWorldUp = avatarTransform.up; // up direction of player before we touch it - - // project forward vectors to the ground plane - currentForward = Vector3.ProjectOnPlane(currentForward, currentWorldUp).normalized; - targetForward = Vector3.ProjectOnPlane(targetForward, currentWorldUp).normalized; - - lastRotation = Quaternion.LookRotation(currentForward, currentWorldUp); - worldRotation = Quaternion.LookRotation(targetForward, currentWorldUp); - } - - transform.rotation = Quaternion.Slerp(lastRotation, worldRotation, lerp); - } - - if (_relativeSyncMarker.ApplyRelativePosition && _relativeSyncData.LocalRootPosition.sqrMagnitude < MaxMagnitude) - { - Vector3 worldPosition = targetTransform.TransformPoint(targetLocalPosition); - transform.position = Vector3.Lerp(targetTransform.TransformPoint(_lastSyncData.LocalRootPosition), worldPosition, lerp); - } - - // negate avatar transform movement + 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) { @@ -112,6 +81,53 @@ public class RelativeSyncController : MonoBehaviour } } + 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 @@ -124,21 +140,24 @@ public class RelativeSyncController : MonoBehaviour _relativeSyncMarker = target; // calculate relative position and rotation so lerp can smooth it out (hack) - if (_relativeSyncMarker != null) - { - Transform avatarTransform = puppetMaster._animator.transform; - Transform markerTransform = _relativeSyncMarker.transform; - Vector3 localPosition = markerTransform.InverseTransformPoint(avatarTransform.position); - Quaternion localRotation = Quaternion.Inverse(markerTransform.rotation) * avatarTransform.rotation; + 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 = localPosition; - _lastSyncData.LocalRootRotation = localRotation.eulerAngles; - //Debug.Log($"SetRelativeSyncMarker: {_relativeSyncMarker.name}"); - } + // set last sync data to current position and rotation so we don't lerp from the last marker + _lastSyncData.LocalRootPosition = relativePosition; + _lastSyncData.LocalRootRotation = relativeRotation; } - public void SetRelativePositions(Vector3 position, Vector3 rotation) + public void SetRelativePositions( + Vector3 position, Vector3 rotation) { // calculate update interval float prevUpdate = _lastUpdate; @@ -146,8 +165,7 @@ public class RelativeSyncController : MonoBehaviour _updateInterval = _lastUpdate - prevUpdate; // cycle last sync data - _lastSyncData.LocalRootPosition = _relativeSyncData.LocalRootPosition; - _lastSyncData.LocalRootRotation = _relativeSyncData.LocalRootRotation; + _lastSyncData = _relativeSyncData; // set new sync data _relativeSyncData.LocalRootPosition = position; @@ -156,7 +174,7 @@ public class RelativeSyncController : MonoBehaviour #endregion Public Methods - public struct RelativeSyncData + private struct RelativeSyncData { public Vector3 LocalRootPosition; public Vector3 LocalRootRotation; diff --git a/RelativeSync/RelativeSync/Components/RelativeSyncMarker.cs b/RelativeSync/RelativeSync/Components/RelativeSyncMarker.cs index 8a3b45c..d4b4300 100644 --- a/RelativeSync/RelativeSync/Components/RelativeSyncMarker.cs +++ b/RelativeSync/RelativeSync/Components/RelativeSyncMarker.cs @@ -1,4 +1,6 @@ -using ABI.CCK.Components; +using ABI_RC.Core.Player; +using ABI_RC.Core.Savior; +using ABI.CCK.Components; using UnityEngine; namespace NAK.RelativeSync.Components; @@ -14,8 +16,21 @@ public class RelativeSyncMarker : MonoBehaviour private void Start() { string path = GetGameObjectPath(transform); - pathHash = path.GetHashCode(); - RelativeSyncManager.RelativeSyncTransforms.Add(pathHash, this); + 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); ConfigureForPotentialMovementParent(); } @@ -39,12 +54,37 @@ public class RelativeSyncMarker : MonoBehaviour 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 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; + } } \ No newline at end of file diff --git a/RelativeSync/RelativeSync/Components/RelativeSyncMonitor.cs b/RelativeSync/RelativeSync/Components/RelativeSyncMonitor.cs index 0301dd0..134234c 100644 --- a/RelativeSync/RelativeSync/Components/RelativeSyncMonitor.cs +++ b/RelativeSync/RelativeSync/Components/RelativeSyncMonitor.cs @@ -37,8 +37,18 @@ public class RelativeSyncMonitor : MonoBehaviour } _lastRelativeSyncMarker = _relativeSyncMarker; - - SendCurrentPositionAndRotation(); + + 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() @@ -60,23 +70,10 @@ public class RelativeSyncMonitor : MonoBehaviour // none found _relativeSyncMarker = null; } - - private void SendCurrentPositionAndRotation() - { - // because our syncing is retarded, we need to sync relative from the avatar root... - Transform avatarRoot = PlayerSetup.Instance._avatar.transform; - Vector3 avatarRootPosition = avatarRoot.position; // PlayerSetup.Instance.GetPlayerPosition() - Quaternion avatarRootRotation = avatarRoot.rotation; // PlayerSetup.Instance.GetPlayerRotation() - - Transform markerTransform = _relativeSyncMarker.transform; - Vector3 localPosition = markerTransform.InverseTransformPoint(avatarRootPosition); - Quaternion localRotation = Quaternion.Inverse(markerTransform.rotation) * avatarRootRotation; - - ModNetwork.SendNetworkPosition(_relativeSyncMarker.pathHash, localPosition, localRotation.eulerAngles); - } - + private void SendEmptyPositionAndRotation() { - ModNetwork.SendNetworkPosition(RelativeSyncManager.NoTarget, Vector3.zero, Vector3.zero); + 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 index ea5cdc9..682926c 100644 --- a/RelativeSync/RelativeSync/RelativeSyncManager.cs +++ b/RelativeSync/RelativeSync/RelativeSyncManager.cs @@ -31,4 +31,33 @@ public static class RelativeSyncManager controller.SetRelativePositions(position, rotation); controller.SetRelativeSyncMarker(syncMarker); } + + 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 index 7658bfa..4f73ac9 100644 --- a/RelativeSync/format.json +++ b/RelativeSync/format.json @@ -1,12 +1,12 @@ { - "_id": -1, + "_id": 211, "name": "RelativeSync", - "modversion": "1.0.0", + "modversion": "1.0.1", "gameversion": "2024r175", "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.", + "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", @@ -16,8 +16,8 @@ "requirements": [ "None" ], - "downloadlink": "https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/download/r27/RelativeSync.dll", + "downloadlink": "https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/download/r28/RelativeSync.dll", "sourcelink": "https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/RelativeSync/", - "changelog": "- Initial Release", + "changelog": "- Fixed RelativeSyncMarker not generating correct path hash for local player movement parents.\n- Added Network Debug settings.\n- Added experimental options that fix **local** jitter on movement parents.", "embedcolor": "#507e64" } \ No newline at end of file