using MelonLoader; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using UnityEngine; using StressLevelZero; using StressLevelZero.Rig; using StressLevelZero.Player; using StressLevelZero.VRMK; using BoneSync.Networking; using BoneSync.Networking.Messages; using System.Reflection; using System.IO; using BoneSync.Data; using StressLevelZero.Interaction; using UnhollowerBaseLib; using BoneSync.Patching; using BoneSync.Sync.Components; using Oculus.Platform.Models; namespace BoneSync.Player { internal class PlayerRig { private const float RIG_SYNC_FPS = Syncable.SYNC_FPS; public static AssetBundle rigBundle; private static float _lastLocalSyncTime = 0; private static Dictionary _playerRigs = new Dictionary(); private ulong _ownerId; private GameObject playerRig; private SLZ_Body body; private CharacterAnimationManager characterAnimationManager; private Animator animator; private Transform headTransform; private Transform leftHandTransform; private Transform rightHandTransform; public static void LoadBundle() { rigBundle = EmebeddedAssetBundle.LoadFromAssembly("BoneSync.playerrep.eres"); if (rigBundle == null) throw new NullReferenceException("playerRepBundle is null! Did you forget to compile the player bundle into the dll?"); MelonLogger.Msg("Loaded playerRepBundle success"); } public static void Tick() { foreach (PlayerRig playerRig in _playerRigs.Values) { playerRig.UpdateIK(); } } public static void LocalSyncTick() { if (Time.realtimeSinceStartup - _lastLocalSyncTime > 1 / RIG_SYNC_FPS) { SendLocalPlayerSync(); _lastLocalSyncTime = Time.realtimeSinceStartup; } } private void SetFingerCurl(Handedness handedness, SimpleFingerCurl fingerCurl) { characterAnimationManager.ApplyFingerCurl(handedness, 1f - fingerCurl.thumb, 1f - fingerCurl.index, 1f - fingerCurl.middle, 1f - fingerCurl.ring, 1f - fingerCurl.pinky); } public void UpdatePlayerSync(PlayerSyncInfo playerSyncInfo) { EnsurePlayerRig(); //MelonLogger.Msg("Updating player sync for " + _ownerId); playerRig.transform.position = playerSyncInfo.rootPos; headTransform.ApplySimpleTransform(playerSyncInfo.headPos); leftHandTransform.ApplySimpleTransform(playerSyncInfo.leftHandPos); rightHandTransform.ApplySimpleTransform(playerSyncInfo.rightHandPos); SetFingerCurl(Handedness.LEFT, playerSyncInfo.leftHandFingerCurl); SetFingerCurl(Handedness.RIGHT, playerSyncInfo.rightHandFingerCurl); UpdatePose(Handedness.LEFT, playerSyncInfo.poseIndexLeft); UpdatePose(Handedness.RIGHT, playerSyncInfo.poseIndexRight); UpdatePoseRadius(Handedness.LEFT, playerSyncInfo.poseRadiusLeft); UpdatePoseRadius(Handedness.RIGHT, playerSyncInfo.poseRadiusRight); } public void UpdatePose(Handedness hand, int index) { Il2CppStringArray handPoses = PlayerScripts.playerHandPoses; if (handPoses.Count < index + 1) return; UpdatePose(hand, handPoses[index]); } public void UpdatePose(Handedness hand, string pose) => characterAnimationManager?.SetHandPose(hand, pose); public void UpdatePoseRadius(Handedness hand, float radius) => characterAnimationManager?.SetCylinderRadius(hand, radius); private static GameObject localPlayerRig; private static Transform localRigRoot; private static Transform localRigHeadTransform; private static Transform localRigLeftHandTransform; private static Transform localRigRightHandTransform; public static void SetLocalRigReferences() { if (localPlayerRig != null) return; localPlayerRig = GameObject.Find("[RigManager (Default Brett)]/[SkeletonRig (GameWorld Brett)]"); localRigRoot = localPlayerRig.transform; localRigHeadTransform = localRigRoot.Find("Head"); localRigLeftHandTransform = localRigRoot.Find("Hand (left)"); localRigRightHandTransform = localRigRoot.Find("Hand (right)"); } public static PlayerSyncInfo? GetLocalSyncInfo() { SetLocalRigReferences(); if (localPlayerRig == null) { MelonLogger.Msg("Local player rig not found"); return null; } if (localRigHeadTransform == null || localRigLeftHandTransform == null || localRigRightHandTransform == null) { MelonLogger.Msg("Local player rig components not found"); return null; } PlayerSyncInfo playerSyncInfo = new PlayerSyncInfo() { rootPos = localRigRoot.position, headPos = new SimpleSyncTransform(localRigHeadTransform), leftHandPos = new SimpleSyncTransform(localRigLeftHandTransform), rightHandPos = new SimpleSyncTransform(localRigRightHandTransform), //leftHandFingerCurl = new SimpleFingerCurl(PlayerScripts.playerLeftHand.fingerCurl), //rightHandFingerCurl = new SimpleFingerCurl(PlayerScripts.playerRightHand.fingerCurl) poseIndexLeft = SkeletonHandPatches.poseIndexLeft, poseIndexRight = SkeletonHandPatches.poseIndexRight, poseRadiusLeft = SkeletonHandPatches.radiusLeft, poseRadiusRight = SkeletonHandPatches.radiusRight }; if (PlayerScripts.playerLeftHand) playerSyncInfo.leftHandFingerCurl = new SimpleFingerCurl(PlayerScripts.playerLeftHand.fingerCurl); if (PlayerScripts.playerRightHand) playerSyncInfo.rightHandFingerCurl = new SimpleFingerCurl(PlayerScripts.playerRightHand.fingerCurl); return playerSyncInfo; } private static void SendLocalPlayerSync() { if (!BoneSync.IsConnected) return; //MelonLogger.Msg("Sending local player sync"); PlayerSyncInfo? playerSyncInfo = GetLocalSyncInfo(); if (!playerSyncInfo.HasValue) return; PlayerSyncMessage playerSyncMessage = new PlayerSyncMessage(playerSyncInfo.Value); playerSyncMessage.Broadcast(); } public static PlayerRig GetPlayerRig(ulong ownerId) { if (_playerRigs.ContainsKey(ownerId)) { //MelonLogger.Msg("PlayerRig already exists for " + ownerId); return _playerRigs[ownerId]; } if (rigBundle == null) { MelonLogger.Msg("playerRepBundle is null! Did you forget to load the bundle?"); return null; } //PlayerScripts.GetPlayerScripts(); PlayerRig rig = new PlayerRig(ownerId); return rig; } public static void DestroyRig(ulong ownerId) { if (_playerRigs.ContainsKey(ownerId)) { _playerRigs[ownerId].Destroy(); } } private void EnsurePlayerRig() { if (playerRig != null) return; playerRig = GameObject.Instantiate(rigBundle.LoadAsset("PlayerRep")); playerRig.name = "PlayerRep"; body = playerRig.GetComponentInChildren(); characterAnimationManager = playerRig.GetComponentInChildren(); animator = playerRig.GetComponentInChildren(); headTransform = playerRig.transform.Find("Head"); leftHandTransform = playerRig.transform.Find("Hand (left)"); rightHandTransform = playerRig.transform.Find("Hand (right)"); body.OnStart(); } public void UpdateIK() { // Catch errors so other players arent broken try { if (body == null || characterAnimationManager == null || animator == null || playerRig == null) return; animator.Update(Time.fixedDeltaTime); characterAnimationManager.OnLateUpdate(); Vector3 repInputVel = Vector3.zero; body.FullBodyUpdate(repInputVel, Vector3.zero); body.ArtToBlender.UpdateBlender(); } catch (Exception e) { MelonLogger.Warning("Failed to update player rig " + _ownerId); MelonLogger.Warning(e.ToString()); } } private PlayerRig(ulong id) { _playerRigs.Add(id, this); _ownerId = id; } public static void OnPlayerSync(PlayerSyncMessage playerSyncMessage) { //MelonLogger.Msg("Player Sync Received " + playerSyncMessage.senderId); PlayerRig playerRig = PlayerRig.GetPlayerRig(playerSyncMessage.senderId); if (playerRig == null) return; playerRig.UpdatePlayerSync(playerSyncMessage.playerSyncInfo); } public void Destroy() { _playerRigs.Remove(_ownerId); GameObject.Destroy(playerRig); } } }