mirror of
https://github.com/NotAKidoS/NAK_CVR_Mods.git
synced 2025-09-02 14:29:25 +00:00
[RelativeSync] Fixed buncha stuff, cleanup
This commit is contained in:
parent
b97265ef37
commit
b786ecd51c
11 changed files with 429 additions and 231 deletions
|
@ -13,12 +13,22 @@ public class RelativeSyncMod : MelonMod
|
||||||
Logger = LoggerInstance;
|
Logger = LoggerInstance;
|
||||||
|
|
||||||
ModNetwork.Subscribe();
|
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));
|
ApplyPatches(typeof(NetworkRootDataUpdatePatches));
|
||||||
|
|
||||||
|
// Add components if missing (for relative sync monitor and controller)
|
||||||
ApplyPatches(typeof(PlayerSetupPatches));
|
ApplyPatches(typeof(PlayerSetupPatches));
|
||||||
ApplyPatches(typeof(PuppetMasterPatches));
|
ApplyPatches(typeof(PuppetMasterPatches));
|
||||||
|
|
||||||
|
// Add components if missing (for relative sync markers)
|
||||||
ApplyPatches(typeof(CVRSeatPatches));
|
ApplyPatches(typeof(CVRSeatPatches));
|
||||||
ApplyPatches(typeof(CVRMovementParentPatches));
|
ApplyPatches(typeof(CVRMovementParentPatches));
|
||||||
}
|
}
|
||||||
|
|
48
RelativeSync/ModSettings.cs
Normal file
48
RelativeSync/ModSettings.cs
Normal file
|
@ -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<bool> DebugLogInbound =
|
||||||
|
Category.CreateEntry("DebugLogInbound", false,
|
||||||
|
"Debug Log Inbound", description: "Log inbound network messages.");
|
||||||
|
|
||||||
|
private static readonly MelonPreferences_Entry<bool> DebugLogOutbound =
|
||||||
|
Category.CreateEntry("DebugLogOutbound", false,
|
||||||
|
"Debug Log Outbound", description: "Log outbound network messages.");
|
||||||
|
|
||||||
|
private static readonly MelonPreferences_Entry<bool> 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<bool> 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,8 +3,8 @@ using ABI_RC.Systems.ModNetwork;
|
||||||
using DarkRift;
|
using DarkRift;
|
||||||
using UnityEngine;
|
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_NetworkInbound = false;
|
||||||
|
@ -12,15 +12,17 @@ namespace NAK.RelativeSync.Networking
|
||||||
|
|
||||||
private static bool _isSubscribedToModNetwork;
|
private static bool _isSubscribedToModNetwork;
|
||||||
|
|
||||||
private struct RelativeSyncData
|
private struct MovementParentSyncData
|
||||||
{
|
{
|
||||||
public bool HasSyncedThisData;
|
public bool HasSyncedThisData;
|
||||||
public int MarkerHash;
|
public int MarkerHash;
|
||||||
public Vector3 Position;
|
public Vector3 RootPosition;
|
||||||
public Vector3 Rotation;
|
public Vector3 RootRotation;
|
||||||
|
// public Vector3 HipPosition;
|
||||||
|
// public Vector3 HipRotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static RelativeSyncData _latestRelativeSyncData;
|
private static MovementParentSyncData _latestMovementParentSyncData;
|
||||||
|
|
||||||
#region Constants
|
#region Constants
|
||||||
|
|
||||||
|
@ -32,8 +34,9 @@ namespace NAK.RelativeSync.Networking
|
||||||
|
|
||||||
private enum MessageType : byte
|
private enum MessageType : byte
|
||||||
{
|
{
|
||||||
SyncPosition = 0,
|
MovementParentOrChair = 0
|
||||||
RelativeSyncStatus = 1
|
//RelativePickup = 1,
|
||||||
|
//RelativeAttachment = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -49,31 +52,35 @@ namespace NAK.RelativeSync.Networking
|
||||||
Debug.LogError("Failed to subscribe to Mod Network!");
|
Debug.LogError("Failed to subscribe to Mod Network!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Called right after NetworkRootDataUpdate.Submit()
|
||||||
internal static void SendRelativeSyncUpdate()
|
internal static void SendRelativeSyncUpdate()
|
||||||
{
|
{
|
||||||
if (!_isSubscribedToModNetwork)
|
if (!_isSubscribedToModNetwork)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!_latestRelativeSyncData.HasSyncedThisData)
|
if (_latestMovementParentSyncData.HasSyncedThisData)
|
||||||
{
|
return;
|
||||||
SendMessage(MessageType.SyncPosition, _latestRelativeSyncData.MarkerHash,
|
|
||||||
_latestRelativeSyncData.Position, _latestRelativeSyncData.Rotation);
|
SendMessage(MessageType.MovementParentOrChair, _latestMovementParentSyncData.MarkerHash,
|
||||||
_latestRelativeSyncData.HasSyncedThisData = true;
|
_latestMovementParentSyncData.RootPosition, _latestMovementParentSyncData.RootRotation);
|
||||||
}
|
|
||||||
|
_latestMovementParentSyncData.HasSyncedThisData = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SetLatestRelativeSync(int markerHash, Vector3 position, Vector3 rotation)
|
public static void SetLatestRelativeSync(
|
||||||
|
int markerHash,
|
||||||
|
Vector3 position, Vector3 rotation)
|
||||||
{
|
{
|
||||||
// check if the data has changed
|
// check if the data has changed
|
||||||
if (_latestRelativeSyncData.MarkerHash == markerHash
|
if (_latestMovementParentSyncData.MarkerHash == markerHash
|
||||||
&& _latestRelativeSyncData.Position == position
|
&& _latestMovementParentSyncData.RootPosition == position
|
||||||
&& _latestRelativeSyncData.Rotation == rotation)
|
&& _latestMovementParentSyncData.RootRotation == rotation)
|
||||||
return; // no need to update
|
return; // no need to update (shocking)
|
||||||
|
|
||||||
_latestRelativeSyncData.HasSyncedThisData = false; // reset
|
_latestMovementParentSyncData.HasSyncedThisData = false; // reset
|
||||||
_latestRelativeSyncData.MarkerHash = markerHash;
|
_latestMovementParentSyncData.MarkerHash = markerHash;
|
||||||
_latestRelativeSyncData.Position = position;
|
_latestMovementParentSyncData.RootPosition = position;
|
||||||
_latestRelativeSyncData.Rotation = rotation;
|
_latestMovementParentSyncData.RootRotation = rotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SendMessage(MessageType messageType, int markerHash, Vector3 position, Vector3 rotation)
|
private static void SendMessage(MessageType messageType, int markerHash, Vector3 position, Vector3 rotation)
|
||||||
|
@ -87,6 +94,11 @@ namespace NAK.RelativeSync.Networking
|
||||||
modMsg.Write(position);
|
modMsg.Write(position);
|
||||||
modMsg.Write(rotation);
|
modMsg.Write(rotation);
|
||||||
modMsg.Send();
|
modMsg.Send();
|
||||||
|
|
||||||
|
if (Debug_NetworkOutbound)
|
||||||
|
Debug.Log(
|
||||||
|
$"[Outbound] MessageType: {messageType}, MarkerHash: {markerHash}, Position: {position}, " +
|
||||||
|
$"Rotation: {rotation}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void OnMessageReceived(ModNetworkMessage msg)
|
private static void OnMessageReceived(ModNetworkMessage msg)
|
||||||
|
@ -98,18 +110,19 @@ namespace NAK.RelativeSync.Networking
|
||||||
|
|
||||||
switch ((MessageType)msgTypeRaw)
|
switch ((MessageType)msgTypeRaw)
|
||||||
{
|
{
|
||||||
case MessageType.SyncPosition:
|
case MessageType.MovementParentOrChair:
|
||||||
msg.Read(out int markerHash);
|
msg.Read(out int markerHash);
|
||||||
msg.Read(out Vector3 receivedPosition);
|
msg.Read(out Vector3 receivedPosition);
|
||||||
msg.Read(out Vector3 receivedRotation);
|
msg.Read(out Vector3 receivedRotation);
|
||||||
|
// msg.Read(out Vector3 receivedHipPosition);
|
||||||
|
// msg.Read(out Vector3 receivedHipRotation);
|
||||||
|
|
||||||
OnNetworkPositionUpdateReceived(msg.Sender, markerHash, receivedPosition, receivedRotation);
|
OnNetworkPositionUpdateReceived(msg.Sender, markerHash, receivedPosition, receivedRotation);
|
||||||
|
|
||||||
|
if (Debug_NetworkInbound)
|
||||||
|
Debug.Log($"[Inbound] Sender: {msg.Sender}, MarkerHash: {markerHash}, " +
|
||||||
|
$"Position: {receivedPosition}, Rotation: {receivedRotation}");
|
||||||
break;
|
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:
|
default:
|
||||||
Debug.LogError($"Invalid message type received from: {msg.Sender}");
|
Debug.LogError($"Invalid message type received from: {msg.Sender}");
|
||||||
break;
|
break;
|
||||||
|
@ -118,15 +131,6 @@ namespace NAK.RelativeSync.Networking
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
public static void SendNetworkPosition(int markerHash, Vector3 newPosition, Vector3 newRotation)
|
|
||||||
{
|
|
||||||
SetLatestRelativeSync(markerHash, newPosition, newRotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
#region Private Methods
|
||||||
|
|
||||||
private static bool IsConnectedToGameNetwork()
|
private static bool IsConnectedToGameNetwork()
|
||||||
|
@ -136,16 +140,12 @@ namespace NAK.RelativeSync.Networking
|
||||||
&& NetworkManager.Instance.GameNetwork.ConnectionState == ConnectionState.Connected;
|
&& NetworkManager.Instance.GameNetwork.ConnectionState == ConnectionState.Connected;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void OnNetworkPositionUpdateReceived(string sender, int markerHash, Vector3 position, Vector3 rotation)
|
private static void OnNetworkPositionUpdateReceived(
|
||||||
|
string sender, int markerHash,
|
||||||
|
Vector3 position, Vector3 rotation)
|
||||||
{
|
{
|
||||||
RelativeSyncManager.ApplyRelativeSync(sender, markerHash, position, rotation);
|
RelativeSyncManager.ApplyRelativeSync(sender, markerHash, position, rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void OnRelativeSyncStatusReceived(string sender, System.Guid guid, bool isRelativeSync)
|
|
||||||
{
|
|
||||||
// todo: implement
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
|
|
@ -2,10 +2,12 @@
|
||||||
using ABI_RC.Core.InteractionSystem;
|
using ABI_RC.Core.InteractionSystem;
|
||||||
using ABI_RC.Core.Networking.Jobs;
|
using ABI_RC.Core.Networking.Jobs;
|
||||||
using ABI_RC.Core.Player;
|
using ABI_RC.Core.Player;
|
||||||
|
using ABI_RC.Systems.Movement;
|
||||||
using ABI.CCK.Components;
|
using ABI.CCK.Components;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using NAK.RelativeSync.Components;
|
using NAK.RelativeSync.Components;
|
||||||
using NAK.RelativeSync.Networking;
|
using NAK.RelativeSync.Networking;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
namespace NAK.RelativeSync.Patches;
|
namespace NAK.RelativeSync.Patches;
|
||||||
|
|
||||||
|
@ -59,3 +61,51 @@ internal static class NetworkRootDataUpdatePatches
|
||||||
ModNetwork.SendRelativeSyncUpdate(); // Send the relative sync update after the network root data update
|
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<Rigidbody>();
|
||||||
|
NoInterpolation = _noInterpolation; // get initial value as patch runs later than settings init
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,6 +27,6 @@ using System.Reflection;
|
||||||
namespace NAK.RelativeSync.Properties;
|
namespace NAK.RelativeSync.Properties;
|
||||||
internal static class AssemblyInfoParams
|
internal static class AssemblyInfoParams
|
||||||
{
|
{
|
||||||
public const string Version = "1.0.0";
|
public const string Version = "1.0.1";
|
||||||
public const string Author = "NotAKidoS";
|
public const string Author = "NotAKidoS";
|
||||||
}
|
}
|
|
@ -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
|
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.
|
Here is the block of text where I tell you this mod is not affiliated with or endorsed by ABI.
|
||||||
|
|
|
@ -6,16 +6,13 @@ namespace NAK.RelativeSync.Components;
|
||||||
[DefaultExecutionOrder(int.MaxValue)] // make sure this runs after NetIKController
|
[DefaultExecutionOrder(int.MaxValue)] // make sure this runs after NetIKController
|
||||||
public class RelativeSyncController : MonoBehaviour
|
public class RelativeSyncController : MonoBehaviour
|
||||||
{
|
{
|
||||||
private static float MaxMagnitude = 750000000000f;
|
private const float MaxMagnitude = 750000000000f;
|
||||||
|
|
||||||
private float _updateInterval = 0.05f;
|
private float _updateInterval = 0.05f;
|
||||||
private float _lastUpdate;
|
private float _lastUpdate;
|
||||||
|
|
||||||
private string _userId;
|
private string _userId;
|
||||||
private PuppetMaster puppetMaster { get; set; }
|
private PuppetMaster puppetMaster { get; set; }
|
||||||
private NetIKController netIkController { get; set; }
|
|
||||||
|
|
||||||
// private bool _syncMarkerChangedSinceLastSync;
|
|
||||||
private RelativeSyncMarker _relativeSyncMarker;
|
private RelativeSyncMarker _relativeSyncMarker;
|
||||||
|
|
||||||
private RelativeSyncData _relativeSyncData;
|
private RelativeSyncData _relativeSyncData;
|
||||||
|
@ -26,7 +23,6 @@ public class RelativeSyncController : MonoBehaviour
|
||||||
private void Start()
|
private void Start()
|
||||||
{
|
{
|
||||||
puppetMaster = GetComponent<PuppetMaster>();
|
puppetMaster = GetComponent<PuppetMaster>();
|
||||||
netIkController = GetComponent<NetIKController>();
|
|
||||||
|
|
||||||
_userId = puppetMaster._playerDescriptor.ownerId;
|
_userId = puppetMaster._playerDescriptor.ownerId;
|
||||||
RelativeSyncManager.RelativeSyncControllers.Add(_userId, this);
|
RelativeSyncManager.RelativeSyncControllers.Add(_userId, this);
|
||||||
|
@ -50,15 +46,16 @@ public class RelativeSyncController : MonoBehaviour
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Transform avatarTransform = animator.transform;
|
Transform avatarTransform = animator.transform;
|
||||||
|
Transform hipTrans = (animator.avatar != null && animator.isHuman)
|
||||||
Vector3 worldRootPos = avatarTransform.position;
|
? animator.GetBoneTransform(HumanBodyBones.Hips) : null;
|
||||||
Quaternion worldRootRot = avatarTransform.rotation;
|
|
||||||
|
|
||||||
Vector3 relativeHipPos = default;
|
Vector3 relativeHipPos = default;
|
||||||
Quaternion relativeHipRot = default;
|
Quaternion relativeHipRot = default;
|
||||||
Transform hipTrans = animator.GetBoneTransform(HumanBodyBones.Hips);
|
|
||||||
if (hipTrans != null)
|
if (hipTrans != null)
|
||||||
{
|
{
|
||||||
|
Vector3 worldRootPos = avatarTransform.position;
|
||||||
|
Quaternion worldRootRot = avatarTransform.rotation;
|
||||||
|
|
||||||
Vector3 hipPos = hipTrans.position;
|
Vector3 hipPos = hipTrans.position;
|
||||||
Quaternion hipRot = hipTrans.rotation;
|
Quaternion hipRot = hipTrans.rotation;
|
||||||
|
|
||||||
|
@ -66,42 +63,14 @@ public class RelativeSyncController : MonoBehaviour
|
||||||
relativeHipRot = Quaternion.Inverse(worldRootRot) * hipRot;
|
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);
|
float lerp = Mathf.Min((Time.time - _lastUpdate) / _updateInterval, 1f);
|
||||||
|
|
||||||
Vector3 targetLocalPosition = _relativeSyncData.LocalRootPosition;
|
ApplyRelativeRotation(avatarTransform, hipTrans, lerp);
|
||||||
Quaternion targetLocalRotation = Quaternion.Euler(_relativeSyncData.LocalRootRotation);
|
ApplyRelativePosition(hipTrans, lerp);
|
||||||
Transform targetTransform = _relativeSyncMarker.transform;
|
|
||||||
|
|
||||||
if (_relativeSyncMarker.ApplyRelativeRotation && _relativeSyncData.LocalRootRotation.sqrMagnitude < MaxMagnitude)
|
// idk if needed (both player root & avatar root are set to same world position) -_-_-_-
|
||||||
{
|
|
||||||
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
|
|
||||||
avatarTransform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
|
avatarTransform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
|
||||||
|
|
||||||
// fix hip syncing because it is not relative to root, it is synced in world space -_-
|
// fix hip syncing because it is not relative to root, it is synced in world space -_-
|
||||||
|
@ -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
|
#endregion Unity Events
|
||||||
|
|
||||||
#region Public Methods
|
#region Public Methods
|
||||||
|
@ -124,21 +140,24 @@ public class RelativeSyncController : MonoBehaviour
|
||||||
_relativeSyncMarker = target;
|
_relativeSyncMarker = target;
|
||||||
|
|
||||||
// calculate relative position and rotation so lerp can smooth it out (hack)
|
// calculate relative position and rotation so lerp can smooth it out (hack)
|
||||||
if (_relativeSyncMarker != null)
|
if (_relativeSyncMarker == null)
|
||||||
{
|
return;
|
||||||
Transform avatarTransform = puppetMaster._animator.transform;
|
|
||||||
Transform markerTransform = _relativeSyncMarker.transform;
|
Animator avatarAnimator = puppetMaster._animator;
|
||||||
Vector3 localPosition = markerTransform.InverseTransformPoint(avatarTransform.position);
|
if (avatarAnimator == null)
|
||||||
Quaternion localRotation = Quaternion.Inverse(markerTransform.rotation) * avatarTransform.rotation;
|
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
|
// set last sync data to current position and rotation so we don't lerp from the last marker
|
||||||
_lastSyncData.LocalRootPosition = localPosition;
|
_lastSyncData.LocalRootPosition = relativePosition;
|
||||||
_lastSyncData.LocalRootRotation = localRotation.eulerAngles;
|
_lastSyncData.LocalRootRotation = relativeRotation;
|
||||||
//Debug.Log($"SetRelativeSyncMarker: {_relativeSyncMarker.name}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetRelativePositions(Vector3 position, Vector3 rotation)
|
public void SetRelativePositions(
|
||||||
|
Vector3 position, Vector3 rotation)
|
||||||
{
|
{
|
||||||
// calculate update interval
|
// calculate update interval
|
||||||
float prevUpdate = _lastUpdate;
|
float prevUpdate = _lastUpdate;
|
||||||
|
@ -146,8 +165,7 @@ public class RelativeSyncController : MonoBehaviour
|
||||||
_updateInterval = _lastUpdate - prevUpdate;
|
_updateInterval = _lastUpdate - prevUpdate;
|
||||||
|
|
||||||
// cycle last sync data
|
// cycle last sync data
|
||||||
_lastSyncData.LocalRootPosition = _relativeSyncData.LocalRootPosition;
|
_lastSyncData = _relativeSyncData;
|
||||||
_lastSyncData.LocalRootRotation = _relativeSyncData.LocalRootRotation;
|
|
||||||
|
|
||||||
// set new sync data
|
// set new sync data
|
||||||
_relativeSyncData.LocalRootPosition = position;
|
_relativeSyncData.LocalRootPosition = position;
|
||||||
|
@ -156,7 +174,7 @@ public class RelativeSyncController : MonoBehaviour
|
||||||
|
|
||||||
#endregion Public Methods
|
#endregion Public Methods
|
||||||
|
|
||||||
public struct RelativeSyncData
|
private struct RelativeSyncData
|
||||||
{
|
{
|
||||||
public Vector3 LocalRootPosition;
|
public Vector3 LocalRootPosition;
|
||||||
public Vector3 LocalRootRotation;
|
public Vector3 LocalRootRotation;
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using ABI.CCK.Components;
|
using ABI_RC.Core.Player;
|
||||||
|
using ABI_RC.Core.Savior;
|
||||||
|
using ABI.CCK.Components;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace NAK.RelativeSync.Components;
|
namespace NAK.RelativeSync.Components;
|
||||||
|
@ -14,8 +16,21 @@ public class RelativeSyncMarker : MonoBehaviour
|
||||||
private void Start()
|
private void Start()
|
||||||
{
|
{
|
||||||
string path = GetGameObjectPath(transform);
|
string path = GetGameObjectPath(transform);
|
||||||
pathHash = path.GetHashCode();
|
int hash = path.GetHashCode();
|
||||||
RelativeSyncManager.RelativeSyncTransforms.Add(pathHash, this);
|
|
||||||
|
// 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();
|
ConfigureForPotentialMovementParent();
|
||||||
}
|
}
|
||||||
|
@ -39,12 +54,37 @@ public class RelativeSyncMarker : MonoBehaviour
|
||||||
|
|
||||||
private static string GetGameObjectPath(Transform transform)
|
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;
|
string path = transform.name;
|
||||||
while (transform.parent != null)
|
while (transform.parent != null)
|
||||||
{
|
{
|
||||||
transform = transform.parent;
|
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;
|
path = transform.name + "/" + path;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 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;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -38,7 +38,17 @@ public class RelativeSyncMonitor : MonoBehaviour
|
||||||
|
|
||||||
_lastRelativeSyncMarker = _relativeSyncMarker;
|
_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()
|
private void CheckForRelativeSyncMarker()
|
||||||
|
@ -61,22 +71,9 @@ public class RelativeSyncMonitor : MonoBehaviour
|
||||||
_relativeSyncMarker = null;
|
_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()
|
private void SendEmptyPositionAndRotation()
|
||||||
{
|
{
|
||||||
ModNetwork.SendNetworkPosition(RelativeSyncManager.NoTarget, Vector3.zero, Vector3.zero);
|
ModNetwork.SetLatestRelativeSync(RelativeSyncManager.NoTarget,
|
||||||
|
Vector3.zero, Vector3.zero);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -31,4 +31,33 @@ public static class RelativeSyncManager
|
||||||
controller.SetRelativePositions(position, rotation);
|
controller.SetRelativePositions(position, rotation);
|
||||||
controller.SetRelativeSyncMarker(syncMarker);
|
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;
|
||||||
|
// }
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"_id": -1,
|
"_id": 211,
|
||||||
"name": "RelativeSync",
|
"name": "RelativeSync",
|
||||||
"modversion": "1.0.0",
|
"modversion": "1.0.1",
|
||||||
"gameversion": "2024r175",
|
"gameversion": "2024r175",
|
||||||
"loaderversion": "0.6.1",
|
"loaderversion": "0.6.1",
|
||||||
"modtype": "Mod",
|
"modtype": "Mod",
|
||||||
"author": "NotAKidoS",
|
"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": [
|
"searchtags": [
|
||||||
"relative",
|
"relative",
|
||||||
"sync",
|
"sync",
|
||||||
|
@ -16,8 +16,8 @@
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"None"
|
"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/",
|
"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"
|
"embedcolor": "#507e64"
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue