diff --git a/RelativeSync/Main.cs b/RelativeSync/Main.cs index 7f39255..247d1b8 100644 --- a/RelativeSync/Main.cs +++ b/RelativeSync/Main.cs @@ -15,6 +15,7 @@ public class RelativeSyncMod : MelonMod ModNetwork.Subscribe(); ApplyPatches(typeof(NetworkRootDataUpdatePatches)); + ApplyPatches(typeof(CVRSpawnablePatches)); ApplyPatches(typeof(PlayerSetupPatches)); ApplyPatches(typeof(PuppetMasterPatches)); diff --git a/RelativeSync/Networking/ModNetwork.cs b/RelativeSync/Networking/ModNetwork.cs index c196572..8463bc9 100644 --- a/RelativeSync/Networking/ModNetwork.cs +++ b/RelativeSync/Networking/ModNetwork.cs @@ -3,149 +3,154 @@ 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.HipPosition, _latestMovementParentSyncData.HipRotation); + + _latestMovementParentSyncData.HasSyncedThisData = true; + } + + public static void SetLatestRelativeSync( + int markerHash, + Vector3 position, Vector3 rotation, + Vector3 hipPosition, Vector3 hipRotation) + { + // check if the data has changed + if (_latestMovementParentSyncData.MarkerHash == markerHash + && _latestMovementParentSyncData.RootPosition == position + && _latestMovementParentSyncData.RootRotation == rotation + && _latestMovementParentSyncData.HipPosition == hipPosition + && _latestMovementParentSyncData.HipRotation == hipRotation) + return; // no need to update (shocking) + + _latestMovementParentSyncData.HasSyncedThisData = false; // reset + _latestMovementParentSyncData.MarkerHash = markerHash; + _latestMovementParentSyncData.RootPosition = position; + _latestMovementParentSyncData.RootRotation = rotation; + _latestMovementParentSyncData.HipPosition = hipPosition; + _latestMovementParentSyncData.HipRotation = hipRotation; + } + + private static void SendMessage(MessageType messageType, int markerHash, Vector3 position, Vector3 rotation, + Vector3 hipPosition, Vector3 hipRotation) + { + if (!IsConnectedToGameNetwork()) + return; + + using ModNetworkMessage modMsg = new(ModId); + modMsg.Write((byte)messageType); + modMsg.Write(markerHash); + modMsg.Write(position); + modMsg.Write(rotation); + modMsg.Write(hipPosition); + modMsg.Write(hipRotation); + modMsg.Send(); + + if (Debug_NetworkOutbound) + Debug.Log( + $"[Outbound] MessageType: {messageType}, MarkerHash: {markerHash}, Position: {position}, Rotation: {rotation}, HipPosition: {hipPosition}, HipRotation: {hipRotation}"); + } + + 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, receivedHipPosition, receivedHipRotation); + if (Debug_NetworkInbound) + Debug.Log($"[Inbound] Sender: {msg.Sender}, MarkerHash: {markerHash}, Position: {receivedPosition}, Rotation: {receivedRotation}, HipPosition: {receivedHipPosition}, HipRotation: {receivedHipRotation}"); + 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, Vector3 hipPosition, Vector3 hipRotation) + { + RelativeSyncManager.ApplyRelativeSync(sender, markerHash, position, rotation, hipPosition, hipRotation); + } + + #endregion } \ No newline at end of file diff --git a/RelativeSync/Patches.cs b/RelativeSync/Patches.cs index f145aa0..d72196e 100644 --- a/RelativeSync/Patches.cs +++ b/RelativeSync/Patches.cs @@ -58,4 +58,25 @@ internal static class NetworkRootDataUpdatePatches { ModNetwork.SendRelativeSyncUpdate(); // Send the relative sync update after the network root data update } +} + +internal static class CVRSpawnablePatches +{ + private static bool _allowUpdate; + + [HarmonyPrefix] + [HarmonyPatch(typeof(CVRSpawnable), nameof(CVRSpawnable.Update))] + private static bool Prefix_CVRSpawnable_Update() + { + return _allowUpdate; + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(CVRSpawnable), nameof(CVRSpawnable.FixedUpdate))] + private static void Postfix_CVRSpawnable_FixedUpdate(ref CVRSpawnable __instance) + { + _allowUpdate = true; + __instance.Update(); + _allowUpdate = false; + } } \ No newline at end of file diff --git a/RelativeSync/RelativeSync/Components/RelativeSyncController.cs b/RelativeSync/RelativeSync/Components/RelativeSyncController.cs index 2602d68..0720a7c 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,66 +46,76 @@ public class RelativeSyncController : MonoBehaviour return; Transform avatarTransform = animator.transform; - - Vector3 worldRootPos = avatarTransform.position; - Quaternion worldRootRot = avatarTransform.rotation; - - Vector3 relativeHipPos = default; - Quaternion relativeHipRot = default; - Transform hipTrans = animator.GetBoneTransform(HumanBodyBones.Hips); - if (hipTrans != null) - { - Vector3 hipPos = hipTrans.position; - Quaternion hipRot = hipTrans.rotation; - - relativeHipPos = Quaternion.Inverse(worldRootRot) * (hipPos - worldRootPos); - relativeHipRot = Quaternion.Inverse(worldRootRot) * hipRot; - } + Transform hipTrans = (animator.avatar != null && animator.isHuman) + ? animator.GetBoneTransform(HumanBodyBones.Hips) : null; + // 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; + ApplyRelativeRotation(avatarTransform, hipTrans, lerp); + ApplyRelativePosition(hipTrans, lerp); + + avatarTransform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity); // idk if needed + } - if (_relativeSyncMarker.ApplyRelativeRotation && _relativeSyncData.LocalRootRotation.sqrMagnitude < MaxMagnitude) + 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); + + Quaternion lastWorldHipRotation = markerRotation * Quaternion.Euler(_lastSyncData.LocalHipRotation); + Quaternion worldHipRotation = markerRotation * Quaternion.Euler(_relativeSyncData.LocalHipRotation); + + if (_relativeSyncMarker.OnlyApplyRelativeHeading) { - Quaternion rotation = targetTransform.rotation; - Quaternion worldRotation = rotation * targetLocalRotation; - Quaternion lastRotation = rotation * Quaternion.Euler(_lastSyncData.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); + + if (hipTransform != null) { - 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 = lastWorldHipRotation * Vector3.forward; + targetForward = worldHipRotation * Vector3.forward; currentForward = Vector3.ProjectOnPlane(currentForward, currentWorldUp).normalized; targetForward = Vector3.ProjectOnPlane(targetForward, currentWorldUp).normalized; - - lastRotation = Quaternion.LookRotation(currentForward, currentWorldUp); - worldRotation = Quaternion.LookRotation(targetForward, currentWorldUp); + lastWorldHipRotation = Quaternion.LookRotation(currentForward, currentWorldUp); + worldHipRotation = 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); - } + transform.rotation = Quaternion.Slerp(lastWorldRotation, worldRotation, lerp); + if (hipTransform != null) hipTransform.rotation = Quaternion.Slerp(lastWorldHipRotation, worldHipRotation, lerp); + } - // negate avatar transform movement - avatarTransform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity); + private void ApplyRelativePosition(Transform hipTransform, float lerp) + { + if (!_relativeSyncMarker.ApplyRelativePosition || + !(_relativeSyncData.LocalRootPosition.sqrMagnitude < MaxMagnitude)) + return; // not applying relative position or data is invalid - // 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; - } + 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 @@ -124,21 +130,28 @@ 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, + out Vector3 relativeHipPosition, out Vector3 relativeHipRotation); - // 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; + _lastSyncData.LocalHipPosition = relativeHipPosition; + _lastSyncData.LocalHipRotation = relativeHipRotation; } - public void SetRelativePositions(Vector3 position, Vector3 rotation) + public void SetRelativePositions( + Vector3 position, Vector3 rotation, + Vector3 hipPosition, Vector3 hipRotation) { // calculate update interval float prevUpdate = _lastUpdate; @@ -146,19 +159,22 @@ 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; _relativeSyncData.LocalRootRotation = rotation; + _relativeSyncData.LocalHipPosition = hipPosition; + _relativeSyncData.LocalHipRotation = hipRotation; } #endregion Public Methods - public struct RelativeSyncData + private struct RelativeSyncData { public Vector3 LocalRootPosition; public Vector3 LocalRootRotation; + public Vector3 LocalHipPosition; + public Vector3 LocalHipRotation; } } \ No newline at end of file diff --git a/RelativeSync/RelativeSync/Components/RelativeSyncMonitor.cs b/RelativeSync/RelativeSync/Components/RelativeSyncMonitor.cs index 0301dd0..fe386b9 100644 --- a/RelativeSync/RelativeSync/Components/RelativeSyncMonitor.cs +++ b/RelativeSync/RelativeSync/Components/RelativeSyncMonitor.cs @@ -37,8 +37,20 @@ 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, + out Vector3 relativeHipPosition, out Vector3 relativeHipRotation); + + ModNetwork.SetLatestRelativeSync( + _relativeSyncMarker.pathHash, + relativePosition, relativeRotation, + relativeHipPosition, relativeHipRotation); } private void CheckForRelativeSyncMarker() @@ -60,23 +72,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, Vector3.zero, Vector3.zero); } } \ No newline at end of file diff --git a/RelativeSync/RelativeSync/RelativeSyncManager.cs b/RelativeSync/RelativeSync/RelativeSyncManager.cs index ea5cdc9..c05bdf4 100644 --- a/RelativeSync/RelativeSync/RelativeSyncManager.cs +++ b/RelativeSync/RelativeSync/RelativeSyncManager.cs @@ -12,7 +12,10 @@ public static class RelativeSyncManager public static readonly Dictionary RelativeSyncTransforms = new(); public static readonly Dictionary RelativeSyncControllers = new(); - public static void ApplyRelativeSync(string userId, int target, Vector3 position, Vector3 rotation) + public static void ApplyRelativeSync( + string userId, int target, + Vector3 position, Vector3 rotation, + Vector3 hipPosition, Vector3 hipRotation) { if (!RelativeSyncControllers.TryGetValue(userId, out RelativeSyncController controller)) if (CVRPlayerManager.Instance.GetPlayerPuppetMaster(userId, out PuppetMaster pm)) @@ -20,6 +23,7 @@ public static class RelativeSyncManager if (controller == null) { + // TODO: if someone somehow constantly fails this, we should nuke them into orbit RelativeSyncMod.Logger.Error($"Failed to apply relative sync for user {userId}"); return; } @@ -28,7 +32,36 @@ public static class RelativeSyncManager RelativeSyncMarker syncMarker = null; if (target != NoTarget) RelativeSyncTransforms.TryGetValue(target, out syncMarker); - controller.SetRelativePositions(position, rotation); + controller.SetRelativePositions(position, rotation, hipPosition, hipRotation); 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