mirror of
https://github.com/NotAKidoS/NAK_CVR_Mods.git
synced 2025-09-02 06:19:22 +00:00
[RelativeSync] Initial Release
This commit is contained in:
parent
4f177633a4
commit
ca7df56e76
11 changed files with 647 additions and 0 deletions
38
RelativeSync/Main.cs
Normal file
38
RelativeSync/Main.cs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
using MelonLoader;
|
||||||
|
using NAK.RelativeSync.Networking;
|
||||||
|
using NAK.RelativeSync.Patches;
|
||||||
|
|
||||||
|
namespace NAK.RelativeSync;
|
||||||
|
|
||||||
|
public class RelativeSyncMod : MelonMod
|
||||||
|
{
|
||||||
|
internal static MelonLogger.Instance Logger;
|
||||||
|
|
||||||
|
public override void OnInitializeMelon()
|
||||||
|
{
|
||||||
|
Logger = LoggerInstance;
|
||||||
|
|
||||||
|
ModNetwork.Subscribe();
|
||||||
|
|
||||||
|
ApplyPatches(typeof(NetworkRootDataUpdatePatches));
|
||||||
|
|
||||||
|
ApplyPatches(typeof(PlayerSetupPatches));
|
||||||
|
ApplyPatches(typeof(PuppetMasterPatches));
|
||||||
|
|
||||||
|
ApplyPatches(typeof(CVRSeatPatches));
|
||||||
|
ApplyPatches(typeof(CVRMovementParentPatches));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyPatches(Type type)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
HarmonyInstance.PatchAll(type);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LoggerInstance.Msg($"Failed while patching {type.Name}!");
|
||||||
|
LoggerInstance.Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
151
RelativeSync/Networking/ModNetwork.cs
Normal file
151
RelativeSync/Networking/ModNetwork.cs
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
using ABI_RC.Core.Networking;
|
||||||
|
using ABI_RC.Systems.ModNetwork;
|
||||||
|
using DarkRift;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NAK.RelativeSync.Networking
|
||||||
|
{
|
||||||
|
public static class ModNetwork
|
||||||
|
{
|
||||||
|
public static bool Debug_NetworkInbound = false;
|
||||||
|
public static bool Debug_NetworkOutbound = false;
|
||||||
|
|
||||||
|
private static bool _isSubscribedToModNetwork;
|
||||||
|
|
||||||
|
private struct 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
|
||||||
|
}
|
||||||
|
}
|
61
RelativeSync/Patches.cs
Normal file
61
RelativeSync/Patches.cs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
using ABI_RC.Core.Base;
|
||||||
|
using ABI_RC.Core.InteractionSystem;
|
||||||
|
using ABI_RC.Core.Networking.Jobs;
|
||||||
|
using ABI_RC.Core.Player;
|
||||||
|
using ABI.CCK.Components;
|
||||||
|
using HarmonyLib;
|
||||||
|
using NAK.RelativeSync.Components;
|
||||||
|
using NAK.RelativeSync.Networking;
|
||||||
|
|
||||||
|
namespace NAK.RelativeSync.Patches;
|
||||||
|
|
||||||
|
internal static class PlayerSetupPatches
|
||||||
|
{
|
||||||
|
[HarmonyPostfix]
|
||||||
|
[HarmonyPatch(typeof(PlayerSetup), nameof(PlayerSetup.Start))]
|
||||||
|
private static void Postfix_PlayerSetup_Start(ref PlayerSetup __instance)
|
||||||
|
{
|
||||||
|
__instance.AddComponentIfMissing<RelativeSyncMonitor>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class PuppetMasterPatches
|
||||||
|
{
|
||||||
|
[HarmonyPostfix]
|
||||||
|
[HarmonyPatch(typeof(PuppetMaster), nameof(PuppetMaster.Start))]
|
||||||
|
private static void Postfix_PuppetMaster_Start(ref PuppetMaster __instance)
|
||||||
|
{
|
||||||
|
__instance.AddComponentIfMissing<RelativeSyncController>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class CVRSeatPatches
|
||||||
|
{
|
||||||
|
[HarmonyPostfix]
|
||||||
|
[HarmonyPatch(typeof(CVRSeat), nameof(CVRSeat.Awake))]
|
||||||
|
private static void Postfix_CVRSeat_Awake(ref CVRSeat __instance)
|
||||||
|
{
|
||||||
|
__instance.AddComponentIfMissing<RelativeSyncMarker>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class CVRMovementParentPatches
|
||||||
|
{
|
||||||
|
[HarmonyPostfix]
|
||||||
|
[HarmonyPatch(typeof(CVRMovementParent), nameof(CVRMovementParent.Start))]
|
||||||
|
private static void Postfix_CVRMovementParent_Start(ref CVRMovementParent __instance)
|
||||||
|
{
|
||||||
|
__instance.AddComponentIfMissing<RelativeSyncMarker>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class NetworkRootDataUpdatePatches
|
||||||
|
{
|
||||||
|
[HarmonyPostfix]
|
||||||
|
[HarmonyPatch(typeof(NetworkRootDataUpdate), nameof(NetworkRootDataUpdate.Submit))]
|
||||||
|
private static void Postfix_NetworkRootDataUpdater_Submit()
|
||||||
|
{
|
||||||
|
ModNetwork.SendRelativeSyncUpdate(); // Send the relative sync update after the network root data update
|
||||||
|
}
|
||||||
|
}
|
32
RelativeSync/Properties/AssemblyInfo.cs
Normal file
32
RelativeSync/Properties/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
using NAK.RelativeSync.Properties;
|
||||||
|
using MelonLoader;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
[assembly: AssemblyVersion(AssemblyInfoParams.Version)]
|
||||||
|
[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)]
|
||||||
|
[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)]
|
||||||
|
[assembly: AssemblyTitle(nameof(NAK.RelativeSync))]
|
||||||
|
[assembly: AssemblyCompany(AssemblyInfoParams.Author)]
|
||||||
|
[assembly: AssemblyProduct(nameof(NAK.RelativeSync))]
|
||||||
|
|
||||||
|
[assembly: MelonInfo(
|
||||||
|
typeof(NAK.RelativeSync.RelativeSyncMod),
|
||||||
|
nameof(NAK.RelativeSync),
|
||||||
|
AssemblyInfoParams.Version,
|
||||||
|
AssemblyInfoParams.Author,
|
||||||
|
downloadLink: "https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/RelativeSync"
|
||||||
|
)]
|
||||||
|
|
||||||
|
[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")]
|
||||||
|
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
|
||||||
|
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
|
||||||
|
[assembly: MelonColor(255, 125, 126, 129)]
|
||||||
|
[assembly: MelonAuthorColor(255, 158, 21, 32)]
|
||||||
|
[assembly: HarmonyDontPatchAll]
|
||||||
|
|
||||||
|
namespace NAK.RelativeSync.Properties;
|
||||||
|
internal static class AssemblyInfoParams
|
||||||
|
{
|
||||||
|
public const string Version = "1.0.0";
|
||||||
|
public const string Author = "NotAKidoS";
|
||||||
|
}
|
14
RelativeSync/README.md
Normal file
14
RelativeSync/README.md
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# RelativeSync
|
||||||
|
|
||||||
|
Relative sync for Movement Parent & Chairs. Requires both users to have the mod installed. Synced over Mod Network.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Here is the block of text where I tell you this mod is not affiliated with or endorsed by ABI.
|
||||||
|
https://documentation.abinteractive.net/official/legal/tos/#7-modding-our-games
|
||||||
|
|
||||||
|
> This mod is an independent creation not affiliated with, supported by, or approved by Alpha Blend Interactive.
|
||||||
|
|
||||||
|
> Use of this mod is done so at the user's own risk and the creator cannot be held responsible for any issues arising from its use.
|
||||||
|
|
||||||
|
> To the best of my knowledge, I have adhered to the Modding Guidelines established by Alpha Blend Interactive.
|
6
RelativeSync/RelativeSync.csproj
Normal file
6
RelativeSync/RelativeSync.csproj
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<RootNamespace>Sprays</RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
156
RelativeSync/RelativeSync/Components/RelativeSyncController.cs
Normal file
156
RelativeSync/RelativeSync/Components/RelativeSyncController.cs
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
using ABI_RC.Core.Player;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NAK.RelativeSync.Components;
|
||||||
|
|
||||||
|
[DefaultExecutionOrder(int.MaxValue)] // make sure this runs after NetIKController
|
||||||
|
public class RelativeSyncController : MonoBehaviour
|
||||||
|
{
|
||||||
|
private static float MaxMagnitude = 750000000000f;
|
||||||
|
|
||||||
|
private string _userId;
|
||||||
|
private PuppetMaster puppetMaster { get; set; }
|
||||||
|
private NetIKController netIkController { get; set; }
|
||||||
|
|
||||||
|
// private bool _syncMarkerChangedSinceLastSync;
|
||||||
|
private RelativeSyncMarker _relativeSyncMarker;
|
||||||
|
|
||||||
|
private RelativeSyncData _relativeSyncData;
|
||||||
|
private RelativeSyncData _lastSyncData;
|
||||||
|
|
||||||
|
#region Unity Events
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
puppetMaster = GetComponent<PuppetMaster>();
|
||||||
|
netIkController = GetComponent<NetIKController>();
|
||||||
|
|
||||||
|
_userId = puppetMaster._playerDescriptor.ownerId;
|
||||||
|
RelativeSyncManager.RelativeSyncControllers.Add(_userId, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
RelativeSyncManager.RelativeSyncControllers.Remove(_userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LateUpdate()
|
||||||
|
{
|
||||||
|
if (puppetMaster._isHidden)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_relativeSyncMarker == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Animator animator = puppetMaster._animator;
|
||||||
|
if (animator == null)
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: this is fucked and idk why, is technically slightly differing sync rates,
|
||||||
|
// but even reimplementing dynamic tps here didnt fix the jitter
|
||||||
|
float lerp = netIkController.GetLerpSpeed();
|
||||||
|
|
||||||
|
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
|
||||||
|
avatarTransform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
|
||||||
|
|
||||||
|
// fix hip syncing because it is not relative to root, it is synced in world space -_-
|
||||||
|
if (hipTrans != null)
|
||||||
|
{
|
||||||
|
hipTrans.position = transform.position + transform.rotation * relativeHipPos;
|
||||||
|
hipTrans.rotation = transform.rotation * relativeHipRot;
|
||||||
|
}
|
||||||
|
|
||||||
|
_lastSyncData.LocalRootPosition = targetLocalPosition;
|
||||||
|
_lastSyncData.LocalRootRotation = targetLocalRotation.eulerAngles;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Unity Events
|
||||||
|
|
||||||
|
#region Public Methods
|
||||||
|
|
||||||
|
public void SetRelativeSyncMarker(RelativeSyncMarker target)
|
||||||
|
{
|
||||||
|
if (_relativeSyncMarker == target)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_relativeSyncMarker = target;
|
||||||
|
|
||||||
|
// calculate relative position and rotation so lerp can smooth it out (hack)
|
||||||
|
if (_relativeSyncMarker != null)
|
||||||
|
{
|
||||||
|
Transform avatarTransform = puppetMaster._animator.transform;
|
||||||
|
Transform markerTransform = _relativeSyncMarker.transform;
|
||||||
|
Vector3 localPosition = markerTransform.InverseTransformPoint(avatarTransform.position);
|
||||||
|
Quaternion localRotation = Quaternion.Inverse(markerTransform.rotation) * avatarTransform.rotation;
|
||||||
|
|
||||||
|
// 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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetRelativePositions(Vector3 position, Vector3 rotation)
|
||||||
|
{
|
||||||
|
_relativeSyncData.LocalRootPosition = position;
|
||||||
|
_relativeSyncData.LocalRootRotation = rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Public Methods
|
||||||
|
|
||||||
|
public struct RelativeSyncData
|
||||||
|
{
|
||||||
|
public Vector3 LocalRootPosition;
|
||||||
|
public Vector3 LocalRootRotation;
|
||||||
|
}
|
||||||
|
}
|
50
RelativeSync/RelativeSync/Components/RelativeSyncMarker.cs
Normal file
50
RelativeSync/RelativeSync/Components/RelativeSyncMarker.cs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
using ABI.CCK.Components;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NAK.RelativeSync.Components;
|
||||||
|
|
||||||
|
public class RelativeSyncMarker : MonoBehaviour
|
||||||
|
{
|
||||||
|
public int pathHash { get; private set; }
|
||||||
|
|
||||||
|
public bool ApplyRelativePosition = true;
|
||||||
|
public bool ApplyRelativeRotation = true;
|
||||||
|
public bool OnlyApplyRelativeHeading;
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
string path = GetGameObjectPath(transform);
|
||||||
|
pathHash = path.GetHashCode();
|
||||||
|
RelativeSyncManager.RelativeSyncTransforms.Add(pathHash, this);
|
||||||
|
|
||||||
|
ConfigureForPotentialMovementParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
RelativeSyncManager.RelativeSyncTransforms.Remove(pathHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ConfigureForPotentialMovementParent()
|
||||||
|
{
|
||||||
|
if (!gameObject.TryGetComponent(out CVRMovementParent movementParent))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// TODO: a refactor may be needed to handle the orientation mode being animated
|
||||||
|
|
||||||
|
// respect orientation mode & gravity zone
|
||||||
|
ApplyRelativeRotation = movementParent.orientationMode == CVRMovementParent.OrientationMode.RotateWithParent;
|
||||||
|
OnlyApplyRelativeHeading = movementParent.GetComponent<GravityZone>() == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetGameObjectPath(Transform transform)
|
||||||
|
{
|
||||||
|
string path = transform.name;
|
||||||
|
while (transform.parent != null)
|
||||||
|
{
|
||||||
|
transform = transform.parent;
|
||||||
|
path = transform.name + "/" + path;
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
82
RelativeSync/RelativeSync/Components/RelativeSyncMonitor.cs
Normal file
82
RelativeSync/RelativeSync/Components/RelativeSyncMonitor.cs
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
using ABI_RC.Core.Player;
|
||||||
|
using ABI_RC.Systems.Movement;
|
||||||
|
using NAK.RelativeSync.Networking;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NAK.RelativeSync.Components;
|
||||||
|
|
||||||
|
[DefaultExecutionOrder(int.MaxValue)]
|
||||||
|
public class RelativeSyncMonitor : MonoBehaviour
|
||||||
|
{
|
||||||
|
private BetterBetterCharacterController _characterController { get; set; }
|
||||||
|
|
||||||
|
private RelativeSyncMarker _relativeSyncMarker;
|
||||||
|
private RelativeSyncMarker _lastRelativeSyncMarker;
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
_characterController = GetComponent<BetterBetterCharacterController>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LateUpdate()
|
||||||
|
{
|
||||||
|
if (_characterController == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
CheckForRelativeSyncMarker();
|
||||||
|
|
||||||
|
if (_relativeSyncMarker == null)
|
||||||
|
{
|
||||||
|
if (_lastRelativeSyncMarker == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// send empty position and rotation to stop syncing
|
||||||
|
SendEmptyPositionAndRotation();
|
||||||
|
_lastRelativeSyncMarker = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_lastRelativeSyncMarker = _relativeSyncMarker;
|
||||||
|
|
||||||
|
SendCurrentPositionAndRotation();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CheckForRelativeSyncMarker()
|
||||||
|
{
|
||||||
|
if (_characterController._isSitting && _characterController._lastCvrSeat)
|
||||||
|
{
|
||||||
|
RelativeSyncMarker newMarker = _characterController._lastCvrSeat.GetComponent<RelativeSyncMarker>();
|
||||||
|
_relativeSyncMarker = newMarker;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_characterController._previousMovementParent != null)
|
||||||
|
{
|
||||||
|
RelativeSyncMarker newMarker = _characterController._previousMovementParent.GetComponent<RelativeSyncMarker>();
|
||||||
|
_relativeSyncMarker = newMarker;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
34
RelativeSync/RelativeSync/RelativeSyncManager.cs
Normal file
34
RelativeSync/RelativeSync/RelativeSyncManager.cs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
using ABI_RC.Core.Base;
|
||||||
|
using ABI_RC.Core.Player;
|
||||||
|
using NAK.RelativeSync.Components;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NAK.RelativeSync;
|
||||||
|
|
||||||
|
public static class RelativeSyncManager
|
||||||
|
{
|
||||||
|
public const int NoTarget = -1;
|
||||||
|
|
||||||
|
public static readonly Dictionary<int, RelativeSyncMarker> RelativeSyncTransforms = new();
|
||||||
|
public static readonly Dictionary<string, RelativeSyncController> RelativeSyncControllers = new();
|
||||||
|
|
||||||
|
public static void ApplyRelativeSync(string userId, int target, Vector3 position, Vector3 rotation)
|
||||||
|
{
|
||||||
|
if (!RelativeSyncControllers.TryGetValue(userId, out RelativeSyncController controller))
|
||||||
|
if (CVRPlayerManager.Instance.GetPlayerPuppetMaster(userId, out PuppetMaster pm))
|
||||||
|
controller = pm.AddComponentIfMissing<RelativeSyncController>();
|
||||||
|
|
||||||
|
if (controller == null)
|
||||||
|
{
|
||||||
|
RelativeSyncMod.Logger.Error($"Failed to apply relative sync for user {userId}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find target transform
|
||||||
|
RelativeSyncMarker syncMarker = null;
|
||||||
|
if (target != NoTarget) RelativeSyncTransforms.TryGetValue(target, out syncMarker);
|
||||||
|
|
||||||
|
controller.SetRelativePositions(position, rotation);
|
||||||
|
controller.SetRelativeSyncMarker(syncMarker);
|
||||||
|
}
|
||||||
|
}
|
23
RelativeSync/format.json
Normal file
23
RelativeSync/format.json
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"_id": -1,
|
||||||
|
"name": "RelativeSync",
|
||||||
|
"modversion": "1.0.0",
|
||||||
|
"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.",
|
||||||
|
"searchtags": [
|
||||||
|
"relative",
|
||||||
|
"sync",
|
||||||
|
"movement",
|
||||||
|
"chair"
|
||||||
|
],
|
||||||
|
"requirements": [
|
||||||
|
"None"
|
||||||
|
],
|
||||||
|
"downloadlink": "https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/download/r26/RelativeSync.dll",
|
||||||
|
"sourcelink": "https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/RelativeSync/",
|
||||||
|
"changelog": "- Initial Release",
|
||||||
|
"embedcolor": "#507e64"
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue