using System; using System.CodeDom.Compiler; using System.Configuration; using System.Diagnostics; using System.Net.Sockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using FistVR; using HarmonyLib; using Microsoft.CodeAnalysis; using PSVR2Toolkit.CAPI; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyCompany("Niko666")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Implemented some Adaptive Trigger effects for chad PSVR2 controller enjoyers. REQUIRES PSVR2 Toolkit TO USE!")] [assembly: AssemblyFileVersion("1.1.2.0")] [assembly: AssemblyInformationalVersion("1.1.2")] [assembly: AssemblyProduct("Niko666.Adaptive_Trigger_For_PSVR2")] [assembly: AssemblyTitle("Adaptive_Trigger_For_PSVR2")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.2.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BepInEx { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class BepInAutoPluginAttribute : Attribute { public BepInAutoPluginAttribute(string id = null, string name = null, string version = null) { } } } namespace BepInEx.Preloader.Core.Patching { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class PatcherAutoPluginAttribute : Attribute { public PatcherAutoPluginAttribute(string id = null, string name = null, string version = null) { } } } namespace Niko666 { [BepInProcess("h3vr.exe")] [BepInPlugin("Niko666.Adaptive_Trigger_For_PSVR2", "Adaptive_Trigger_For_PSVR2", "1.1.2")] public class AdaptiveTrigger : BaseUnityPlugin { public static ConfigEntry ControllerToUse; public static ConfigEntry ClickyEffectStrength; public static ConfigEntry RecoilFeedbackStrength; public static ConfigEntry UseVibrationFeedbackForRecoil; public static ConfigEntry VibrationFrequency; public static ConfigEntry OverrideTriggerEffectPos; public static ConfigEntry OverrideStartPos; public static ConfigEntry OverrideEndPos; public static int _leftShotsSoFar; public static int _rightShotsSoFar; public static bool _LeftHandTriggerEffectApplied; public static bool _LeftHandRecoilEffectApplied; public static bool _LeftHandTriggerEffectCleared; public static bool _RightHandTriggerEffectApplied; public static bool _RightHandRecoilEffectApplied; public static bool _RightHandTriggerEffectCleared; public const string Id = "Niko666.Adaptive_Trigger_For_PSVR2"; public static AdaptiveTrigger Instance { get; private set; } internal static ManualLogSource Logger { get; private set; } public static string Name => "Adaptive_Trigger_For_PSVR2"; public static string Version => "1.1.2"; public void Awake() { Instance = this; ControllerToUse = ((BaseUnityPlugin)this).Config.Bind("General", "ControllerToUse", EVRControllerType.Both, "Enable Adaptive Trigger effect on selected controllers only. (Both, Left, Right)"); ClickyEffectStrength = ((BaseUnityPlugin)this).Config.Bind("General", "ClickyEffectStrength", (byte)4, "Effect strength of clicky trigger effect. (1-8)"); RecoilFeedbackStrength = ((BaseUnityPlugin)this).Config.Bind("General", "RecoilFeedbackStrength", (byte)8, "Effect strength of firearm recoil effect. (0-8)"); UseVibrationFeedbackForRecoil = ((BaseUnityPlugin)this).Config.Bind("General", "UseVibrationFeedbackForRecoil", false, "Use vibration-based feedback for recoil effect. By default the mod use force-based feedback to emulate the recoil \"kick\" effect, but it doesn't work well with extremely high RPM weapons when doing full-auto shooting. Turning this option on will make the trigger vibrates instead of kicking, which is more suitable for full-auto shooting but worse the feeling when single-shot. "); VibrationFrequency = ((BaseUnityPlugin)this).Config.Bind("General", "VibrationFrequency", (byte)50, "Vibration frequency for recoil effect when 'UseVibrationFeedbackForRecoil' is enabled. (1-255)"); OverrideTriggerEffectPos = ((BaseUnityPlugin)this).Config.Bind("General", "OverrideTriggerEffectPos", false, "Override trigger effect position with user set values instead of reading from firearm's trigger thresholds. Not recommend but could be useful if you want to."); OverrideStartPos = ((BaseUnityPlugin)this).Config.Bind("General", "DefaultStartPos", (byte)2, "Override start position of the trigger effect. (0-9)"); OverrideEndPos = ((BaseUnityPlugin)this).Config.Bind("General", "DefaultEndPos", (byte)7, "Override end position of the trigger effect. (0-9)"); Logger = ((BaseUnityPlugin)this).Logger; if (!IpcClient.Instance().IsRunning) { if (IpcClient.Instance().Start()) { Logger.LogMessage((object)"PSVR2 Toolkit IPC Connected."); Harmony.CreateAndPatchAll(typeof(AdaptiveTriggerPatch), (string)null); Logger.LogMessage((object)("Fuck this world! Sent from Niko666.Adaptive_Trigger_For_PSVR2 " + Version)); } else { Logger.LogMessage((object)"Failed to connect PSVR2 Toolkit IPC. Did you install PSVR2 Toolkit properly?"); } } } public void OnDestroy() { IpcClient.Instance().TriggerEffectFeedback(EVRControllerType.Both, 9, 0); Thread.Sleep(20); IpcClient.Instance().TriggerEffectDisable(EVRControllerType.Both); Thread.Sleep(20); IpcClient.Instance().Stop(); Thread.Sleep(20); Logger.LogMessage((object)"PSVR2 Toolkit IPC disconnected. It is now safe to turn off your computer."); } public static void ShotFired(FVRFireArm fireArm) { if ((Object)(object)((FVRInteractiveObject)fireArm).m_hand != (Object)null) { if (((FVRInteractiveObject)fireArm).m_hand.IsThisTheRightHand) { _rightShotsSoFar++; } else { _leftShotsSoFar++; } } } public static void ApplyTriggerEffect(byte startPos, byte endPos, EVRControllerType Hand, float buzztime) { switch (Hand) { case EVRControllerType.Left: _LeftHandTriggerEffectCleared = false; if (_leftShotsSoFar != 0) { if (!_LeftHandRecoilEffectApplied) { if (UseVibrationFeedbackForRecoil.Value) { if (ControllerToUse.Value == EVRControllerType.Left || ControllerToUse.Value == EVRControllerType.Both) { IpcClient.Instance().TriggerEffectVibration(EVRControllerType.Left, 0, RecoilFeedbackStrength.Value, VibrationFrequency.Value); } } else if (ControllerToUse.Value == EVRControllerType.Left || ControllerToUse.Value == EVRControllerType.Both) { IpcClient.Instance().TriggerEffectFeedback(EVRControllerType.Left, 0, RecoilFeedbackStrength.Value); } _LeftHandRecoilEffectApplied = true; } if (buzztime > 0.01f) { if (ControllerToUse.Value == EVRControllerType.Left || ControllerToUse.Value == EVRControllerType.Both) { IpcClient.Instance().TriggerEffectSlopeFeedback(EVRControllerType.Left, (byte)Mathf.Clamp(startPos - 1, 0, 9), (byte)Mathf.Clamp(endPos - 1, 0, 9), 1, ClickyEffectStrength.Value); } _LeftHandRecoilEffectApplied = false; _leftShotsSoFar = 0; } } else if (!_LeftHandTriggerEffectApplied) { if (ControllerToUse.Value == EVRControllerType.Left || ControllerToUse.Value == EVRControllerType.Both) { IpcClient.Instance().TriggerEffectSlopeFeedback(EVRControllerType.Left, (byte)Mathf.Clamp(startPos - 1, 0, 9), (byte)Mathf.Clamp(endPos - 1, 0, 9), 1, ClickyEffectStrength.Value); } _LeftHandTriggerEffectApplied = true; } break; case EVRControllerType.Right: _RightHandTriggerEffectCleared = false; if (_rightShotsSoFar != 0) { if (!_RightHandRecoilEffectApplied) { if (UseVibrationFeedbackForRecoil.Value) { if (ControllerToUse.Value == EVRControllerType.Right || ControllerToUse.Value == EVRControllerType.Both) { IpcClient.Instance().TriggerEffectVibration(EVRControllerType.Right, 0, RecoilFeedbackStrength.Value, VibrationFrequency.Value); } } else if (ControllerToUse.Value == EVRControllerType.Right || ControllerToUse.Value == EVRControllerType.Both) { IpcClient.Instance().TriggerEffectFeedback(EVRControllerType.Right, 0, RecoilFeedbackStrength.Value); } _RightHandRecoilEffectApplied = true; } if (buzztime > 0.01f) { if (ControllerToUse.Value == EVRControllerType.Right || ControllerToUse.Value == EVRControllerType.Both) { IpcClient.Instance().TriggerEffectSlopeFeedback(EVRControllerType.Right, (byte)Mathf.Clamp(startPos - 1, 0, 9), (byte)Mathf.Clamp(endPos - 1, 0, 9), 1, ClickyEffectStrength.Value); } _RightHandRecoilEffectApplied = false; _rightShotsSoFar = 0; } } else if (!_RightHandTriggerEffectApplied) { if (ControllerToUse.Value == EVRControllerType.Right || ControllerToUse.Value == EVRControllerType.Both) { IpcClient.Instance().TriggerEffectSlopeFeedback(EVRControllerType.Right, (byte)Mathf.Clamp(startPos - 1, 0, 9), (byte)Mathf.Clamp(endPos - 1, 0, 9), 1, ClickyEffectStrength.Value); } _RightHandTriggerEffectApplied = true; } break; case EVRControllerType.Both: break; } } public static void ClearTriggerEffect(EVRControllerType Hand) { switch (Hand) { case EVRControllerType.Left: if (!_LeftHandTriggerEffectCleared) { IpcClient.Instance().TriggerEffectFeedback(EVRControllerType.Left, 9, 0); _LeftHandTriggerEffectApplied = false; _LeftHandRecoilEffectApplied = false; _leftShotsSoFar = 0; _LeftHandTriggerEffectCleared = true; } break; case EVRControllerType.Right: if (!_RightHandTriggerEffectCleared) { IpcClient.Instance().TriggerEffectDisable(EVRControllerType.Right); _RightHandTriggerEffectApplied = false; _RightHandRecoilEffectApplied = false; _rightShotsSoFar = 0; _RightHandTriggerEffectCleared = true; } break; case EVRControllerType.Both: if (!_LeftHandTriggerEffectCleared || !_RightHandTriggerEffectCleared) { IpcClient.Instance().TriggerEffectFeedback(EVRControllerType.Both, 9, 0); _LeftHandTriggerEffectApplied = false; _LeftHandRecoilEffectApplied = false; _leftShotsSoFar = 0; _LeftHandTriggerEffectCleared = true; _RightHandTriggerEffectApplied = false; _RightHandRecoilEffectApplied = false; _rightShotsSoFar = 0; _RightHandTriggerEffectCleared = true; } break; } } } internal class AdaptiveTriggerPatch : MonoBehaviour { [CompilerGenerated] private static class <>O { public static ShotFired <0>__ShotFired; } [HarmonyPatch(typeof(SteamVR_LoadLevel), "Begin")] [HarmonyPrefix] public static bool BeginPatch() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown if ((Object)(object)GM.CurrentSceneSettings != (Object)null) { FVRSceneSettings currentSceneSettings = GM.CurrentSceneSettings; object obj = <>O.<0>__ShotFired; if (obj == null) { ShotFired val = AdaptiveTrigger.ShotFired; <>O.<0>__ShotFired = val; obj = (object)val; } currentSceneSettings.ShotFiredEvent -= (ShotFired)obj; } ((BaseUnityPlugin)AdaptiveTrigger.Instance).Config.Reload(); AdaptiveTrigger._LeftHandTriggerEffectCleared = false; AdaptiveTrigger._RightHandTriggerEffectCleared = false; AdaptiveTrigger.ClearTriggerEffect(EVRControllerType.Both); return true; } [HarmonyPatch(typeof(FVRFireArm), "Awake")] [HarmonyPostfix] public static void ShotDetect() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown FVRSceneSettings currentSceneSettings = GM.CurrentSceneSettings; object obj = <>O.<0>__ShotFired; if (obj == null) { ShotFired val = AdaptiveTrigger.ShotFired; <>O.<0>__ShotFired = val; obj = (object)val; } currentSceneSettings.ShotFiredEvent += (ShotFired)obj; } [HarmonyPatch(typeof(FVRViveHand), "Update")] [HarmonyPostfix] public static void ClearEffectOnDrop(FVRViveHand __instance) { if ((Object)(object)__instance.CurrentInteractable == (Object)null) { AdaptiveTrigger.ClearTriggerEffect(__instance.IsThisTheRightHand ? EVRControllerType.Right : EVRControllerType.Left); } } [HarmonyPatch(typeof(FVRFireArm), "FVRUpdate")] [HarmonyPostfix] public static void GlobalTriggerEffect(FVRFireArm __instance) { if (!((Object)(object)((FVRInteractiveObject)__instance).m_hand != (Object)null)) { return; } if ((Object)(object)((FVRPhysicalObject)__instance).savedGrip != (Object)null) { AdaptiveTrigger.ClearTriggerEffect(((FVRInteractiveObject)__instance).m_hand.IsThisTheRightHand ? EVRControllerType.Right : EVRControllerType.Left); return; } AdaptiveTrigger._LeftHandTriggerEffectCleared = false; AdaptiveTrigger._RightHandTriggerEffectCleared = false; byte startPos; byte endPos; if (AdaptiveTrigger.OverrideTriggerEffectPos.Value) { startPos = (byte)Mathf.Clamp(AdaptiveTrigger.OverrideStartPos.Value + 1, 0, 10); endPos = (byte)Mathf.Clamp(AdaptiveTrigger.OverrideEndPos.Value + 1, 0, 10); } else { ClosedBoltWeapon val = (ClosedBoltWeapon)(object)((__instance is ClosedBoltWeapon) ? __instance : null); if (val == null) { OpenBoltReceiver val2 = (OpenBoltReceiver)(object)((__instance is OpenBoltReceiver) ? __instance : null); if (val2 == null) { Handgun val3 = (Handgun)(object)((__instance is Handgun) ? __instance : null); if (val3 == null) { TubeFedShotgun val4 = (TubeFedShotgun)(object)((__instance is TubeFedShotgun) ? __instance : null); if (val4 == null) { BoltActionRifle val5 = (BoltActionRifle)(object)((__instance is BoltActionRifle) ? __instance : null); if (val5 == null) { if (!(__instance is BreakActionWeapon)) { if (!(__instance is Revolver)) { SingleActionRevolver val6 = (SingleActionRevolver)(object)((__instance is SingleActionRevolver) ? __instance : null); if (val6 == null) { RevolvingShotgun val7 = (RevolvingShotgun)(object)((__instance is RevolvingShotgun) ? __instance : null); if (val7 == null) { LAPD2019 val8 = (LAPD2019)(object)((__instance is LAPD2019) ? __instance : null); if (val8 == null) { BAP val9 = (BAP)(object)((__instance is BAP) ? __instance : null); if (val9 == null) { if (!(__instance is PotatoGun)) { GrappleGun val10 = (GrappleGun)(object)((__instance is GrappleGun) ? __instance : null); if (val10 == null) { Airgun val11 = (Airgun)(object)((__instance is Airgun) ? __instance : null); if (val11 == null) { CarlGustaf val12 = (CarlGustaf)(object)((__instance is CarlGustaf) ? __instance : null); if (val12 == null) { RailTater val13 = (RailTater)(object)((__instance is RailTater) ? __instance : null); if (val13 == null) { FlameThrower val14 = (FlameThrower)(object)((__instance is FlameThrower) ? __instance : null); if (val14 == null) { sblp val15 = (sblp)(object)((__instance is sblp) ? __instance : null); if (val15 != null) { startPos = (byte)Mathf.Clamp((int)(val15.TriggerResetThreshold * 10f - 1f), 0, 9); endPos = (byte)Mathf.Clamp((int)(val15.TriggerFiringThreshold * 10f - 1f), 0, 9); } else { startPos = 2; endPos = 7; } } else { startPos = 3; endPos = (byte)Mathf.Clamp((int)(val14.TriggerFiringThreshold * 10f - 1f), 0, 9); } } else { startPos = (byte)Mathf.Clamp((int)(val13.TriggerResetThreshold * 10f - 1f), 0, 9); endPos = (byte)Mathf.Clamp((int)(val13.TriggerFiringThreshold * 10f - 1f), 0, 9); } } else { startPos = (byte)Mathf.Clamp((int)(val12.TriggerResetThreshold * 10f - 1f), 0, 9); endPos = (byte)Mathf.Clamp((int)(val12.TriggerFiringThreshold * 10f - 1f), 0, 9); } } else { startPos = (byte)Mathf.Clamp((int)(val11.TriggerResetThreshold * 10f - 1f), 0, 9); endPos = (byte)Mathf.Clamp((int)(val11.TriggerFiringThreshold * 10f - 1f), 0, 9); } } else { startPos = (byte)Mathf.Clamp((int)(val10.TriggerResetThreshold * 10f - 1f), 0, 9); endPos = (byte)Mathf.Clamp((int)(val10.TriggerBreakThreshold * 10f - 1f), 0, 9); } } else { startPos = 4; endPos = 7; } } else { startPos = (byte)Mathf.Clamp((int)(val9.TriggerResetThreshold * 10f - 1f), 0, 9); endPos = (byte)Mathf.Clamp((int)(val9.TriggerFiringThreshold * 10f - 1f), 0, 9); } } else { startPos = (byte)Mathf.Clamp((int)(val8.TriggerResetThreshold * 10f - 1f), 0, 9); endPos = (byte)Mathf.Clamp((int)(val8.TriggerFireThreshold * 10f - 1f), 0, 9); } } else { startPos = (byte)Mathf.Clamp((int)(val7.TriggerResetThreshold * 10f - 1f), 0, 9); endPos = (byte)Mathf.Clamp((int)(val7.TriggerFiringThreshold * 10f - 1f), 0, 9); } } else { startPos = (byte)Mathf.Clamp((int)(val6.TriggerThreshold * 10f - 2f), 0, 9); endPos = (byte)Mathf.Clamp((int)(val6.TriggerThreshold * 10f - 1f), 0, 9); } } else { startPos = 2; endPos = 9; } } else { startPos = 4; endPos = 7; } } else { startPos = (byte)Mathf.Clamp((int)(val5.TriggerResetThreshold * 10f - 1f), 0, 9); endPos = (byte)Mathf.Clamp((int)(val5.TriggerFiringThreshold * 10f - 1f), 0, 9); } } else { startPos = (byte)Mathf.Clamp((int)(val4.TriggerResetThreshold * 10f - 1f), 0, 9); endPos = (byte)Mathf.Clamp((int)(val4.TriggerBreakThreshold * 10f - 1f), 0, 9); } } else { startPos = (byte)Mathf.Clamp((int)(val3.TriggerResetThreshold * 10f - 1f), 0, 9); endPos = (byte)Mathf.Clamp((int)(val3.TriggerBreakThreshold * 10f - 1f), 0, 9); } } else { startPos = (byte)Mathf.Clamp((int)(val2.TriggerResetThreshold * 10f - 1f), 0, 9); endPos = (byte)Mathf.Clamp((int)(val2.TriggerFiringThreshold * 10f - 1f), 0, 9); } } else { startPos = (byte)Mathf.Clamp((int)(val.TriggerResetThreshold * 10f - 1f), 0, 9); endPos = (byte)Mathf.Clamp((int)(val.TriggerFiringThreshold * 10f - 1f), 0, 9); } } AdaptiveTrigger.ApplyTriggerEffect(startPos, endPos, ((FVRInteractiveObject)__instance).m_hand.IsThisTheRightHand ? EVRControllerType.Right : EVRControllerType.Left, ((FVRInteractiveObject)__instance).m_hand.m_buzztime); } [HarmonyPatch(typeof(AttachableFirearmPhysicalObject), "UpdateInteraction")] [HarmonyPostfix] public static void AttachableFirearmPhysicalObjectTriggerEffect(AttachableFirearmPhysicalObject __instance) { if ((Object)(object)((FVRInteractiveObject)__instance).m_hand != (Object)null) { AdaptiveTrigger._LeftHandTriggerEffectCleared = false; AdaptiveTrigger._RightHandTriggerEffectCleared = false; byte startPos = 2; byte endPos = 7; if (AdaptiveTrigger.OverrideTriggerEffectPos.Value) { startPos = (byte)Mathf.Clamp(AdaptiveTrigger.OverrideStartPos.Value + 1, 0, 10); endPos = (byte)Mathf.Clamp(AdaptiveTrigger.OverrideEndPos.Value + 1, 0, 10); } AdaptiveTrigger.ApplyTriggerEffect(startPos, endPos, ((FVRInteractiveObject)__instance).m_hand.IsThisTheRightHand ? EVRControllerType.Right : EVRControllerType.Left, ((FVRInteractiveObject)__instance).m_hand.m_buzztime); } } [HarmonyPatch(typeof(AttachableFirearmInterface), "UpdateInteraction")] [HarmonyPostfix] public static void AttachableFirearmInterfaceTriggerEffect(AttachableFirearmInterface __instance) { if ((Object)(object)((FVRInteractiveObject)__instance).m_hand != (Object)null) { AdaptiveTrigger._LeftHandTriggerEffectCleared = false; AdaptiveTrigger._RightHandTriggerEffectCleared = false; byte startPos = 2; byte endPos = 7; if (AdaptiveTrigger.OverrideTriggerEffectPos.Value) { startPos = (byte)Mathf.Clamp(AdaptiveTrigger.OverrideStartPos.Value + 1, 0, 10); endPos = (byte)Mathf.Clamp(AdaptiveTrigger.OverrideEndPos.Value + 1, 0, 10); } AdaptiveTrigger.ApplyTriggerEffect(startPos, endPos, ((FVRInteractiveObject)__instance).m_hand.IsThisTheRightHand ? EVRControllerType.Right : EVRControllerType.Left, ((FVRInteractiveObject)__instance).m_hand.m_buzztime); } } } } namespace PSVR2Toolkit.CAPI { public class IpcClient { private const ushort IPC_SERVER_PORT = 3364; private const ushort k_unIpcVersion = 2; private static IpcClient m_pInstance; private bool m_running; private TcpClient? m_client; private NetworkStream? m_stream; private Thread? m_receiveThread; private readonly object m_gazeStateLock = new object(); private TaskCompletionSource? m_gazeTask; private CancellationTokenSource m_forceShutdownToken; private ushort m_serverIpcVersion = 2; private int m_gazePumpPeriodMs = 8; private CommandDataServerGazeDataResult2? m_lastGazeState; public bool IsRunning => m_running; public static IpcClient Instance() { if (m_pInstance == null) { m_pInstance = new IpcClient(); } return m_pInstance; } public bool Start() { if (m_running) { return false; } try { m_client = new TcpClient(); m_client.Connect("127.0.0.1", 3364); if (m_client.Connected) { m_stream = m_client.GetStream(); m_running = true; m_forceShutdownToken = new CancellationTokenSource(); m_receiveThread = new Thread((ThreadStart)delegate { ReceiveLoop(m_forceShutdownToken.Token); }); m_receiveThread.Start(); return true; } return false; } catch (SocketException ex) { Console.WriteLine($"[IPC_CLIENT] Connection failed. LastError = {ex.SocketErrorCode}"); return false; } } public void Stop() { if (m_running) { m_running = false; m_forceShutdownToken.Cancel(); lock (m_gazeStateLock) { m_gazeTask?.TrySetCanceled(); m_gazeTask = null; } try { m_stream?.Close(); m_client?.Close(); } catch { } if (m_receiveThread != null && m_receiveThread.IsAlive && !m_receiveThread.Join(2000)) { m_receiveThread.Interrupt(); } m_stream?.Dispose(); m_client?.Close(); m_forceShutdownToken.Dispose(); } } private void ReceiveLoop(CancellationToken token) { byte[] array = new byte[1024]; try { Socket client = m_client.Client; m_stream.ReadTimeout = 1; CommandDataClientRequestHandshake commandDataClientRequestHandshake = default(CommandDataClientRequestHandshake); commandDataClientRequestHandshake.ipcVersion = 2; commandDataClientRequestHandshake.processId = (uint)Process.GetCurrentProcess().Id; CommandDataClientRequestHandshake data = commandDataClientRequestHandshake; SendIpcCommand(ECommandType.ClientRequestHandshake, data); Stopwatch stopwatch = Stopwatch.StartNew(); long num = stopwatch.ElapsedMilliseconds; while (m_running && !token.IsCancellationRequested) { long elapsedMilliseconds = stopwatch.ElapsedMilliseconds; if (elapsedMilliseconds >= num) { SendIpcCommand(ECommandType.ClientRequestGazeData); num = elapsedMilliseconds + m_gazePumpPeriodMs; } if (client.Poll(1000, SelectMode.SelectRead) && client.Available > 0) { int available = client.Available; if (available > array.Length) { array = new byte[Math.Max(available, array.Length * 2)]; } int num2 = m_stream.Read(array, 0, Math.Min(array.Length, available)); if (num2 <= 0) { Console.WriteLine("[IPC_CLIENT] Disconnected from server."); break; } if (num2 < Marshal.SizeOf(typeof(CommandHeader))) { Console.WriteLine("[IPC_CLIENT] Received invalid command header size."); } else { HandleIpcCommand(array, num2); } } else { Thread.Sleep(1); } } } catch (Exception ex) { if (m_running) { Console.WriteLine("[IPC_CLIENT] Error in receive loop: " + ex.Message); } } } private GazeEyeResult2 UpgradeGazeEyeResult(GazeEyeResult eye) { GazeEyeResult2 result = default(GazeEyeResult2); result.isGazeOriginValid = eye.isGazeOriginValid; result.gazeOriginMm = eye.gazeOriginMm; result.isGazeDirValid = eye.isGazeDirValid; result.gazeDirNorm = eye.gazeDirNorm; result.isPupilDiaValid = eye.isPupilDiaValid; result.pupilDiaMm = eye.pupilDiaMm; result.isBlinkValid = eye.isBlinkValid; result.blink = eye.blink; result.isOpenEnabled = false; result.open = 0f; return result; } private CommandDataServerGazeDataResult2 UpgradeGazeDataResult(CommandDataServerGazeDataResult result) { CommandDataServerGazeDataResult2 result2 = default(CommandDataServerGazeDataResult2); result2.leftEye = UpgradeGazeEyeResult(result.leftEye); result2.rightEye = UpgradeGazeEyeResult(result.rightEye); return result2; } private void HandleIpcCommand(byte[] pBuffer, int bytesReceived) { CommandHeader commandHeader = ByteArrayToStructure(pBuffer, 0); switch (commandHeader.type) { case ECommandType.ServerPong: Console.WriteLine("[IPC_CLIENT] Received Pong from server."); break; case ECommandType.ServerHandshakeResult: if (commandHeader.dataLen == Marshal.SizeOf(typeof(CommandDataServerHandshakeResult))) { CommandDataServerHandshakeResult commandDataServerHandshakeResult = ByteArrayToStructure(pBuffer, Marshal.SizeOf(typeof(CommandHeader))); m_serverIpcVersion = commandDataServerHandshakeResult.ipcVersion; switch (commandDataServerHandshakeResult.result) { case EHandshakeResult.Success: Console.WriteLine("[IPC_CLIENT] Handshake successful!"); break; case EHandshakeResult.Failed: Console.WriteLine("[IPC_CLIENT] Handshake failed!"); break; case EHandshakeResult.Outdated: Console.WriteLine($"[IPC_CLIENT] Handshake failed with reason: Outdated client. Please upgrade to an IPC version of {commandDataServerHandshakeResult.ipcVersion}"); break; } } break; case ECommandType.ServerGazeDataResult: if (m_serverIpcVersion == 1) { if (commandHeader.dataLen == Marshal.SizeOf(typeof(CommandDataServerGazeDataResult))) { CommandDataServerGazeDataResult result = ByteArrayToStructure(pBuffer, Marshal.SizeOf(typeof(CommandHeader))); m_lastGazeState = UpgradeGazeDataResult(result); } } else if (commandHeader.dataLen == Marshal.SizeOf(typeof(CommandDataServerGazeDataResult2))) { CommandDataServerGazeDataResult2 value = ByteArrayToStructure(pBuffer, Marshal.SizeOf(typeof(CommandHeader))); m_lastGazeState = value; } break; case ECommandType.ClientRequestHandshake: case ECommandType.ClientRequestGazeData: break; } } private void SendIpcCommand(ECommandType type, T data = default(T)) where T : struct { if (m_running) { int num = ((!data.Equals(default(T))) ? Marshal.SizeOf(typeof(T)) : 0); byte[] array = new byte[Marshal.SizeOf(typeof(CommandHeader)) + num]; CommandHeader commandHeader = default(CommandHeader); commandHeader.type = type; commandHeader.dataLen = num; CommandHeader commandHeader2 = commandHeader; IntPtr intPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CommandHeader))); Marshal.StructureToPtr((object)commandHeader2, intPtr, fDeleteOld: false); Marshal.Copy(intPtr, array, 0, Marshal.SizeOf(typeof(CommandHeader))); Marshal.FreeHGlobal(intPtr); if (num > 0) { IntPtr intPtr2 = Marshal.AllocHGlobal(num); Marshal.StructureToPtr((object)data, intPtr2, fDeleteOld: false); Marshal.Copy(intPtr2, array, Marshal.SizeOf(typeof(CommandHeader)), num); Marshal.FreeHGlobal(intPtr2); } m_stream.Write(array, 0, array.Length); } } private void SendIpcCommand(ECommandType type) { if (m_running) { byte[] array = new byte[Marshal.SizeOf(typeof(CommandHeader))]; CommandHeader commandHeader = default(CommandHeader); commandHeader.type = type; commandHeader.dataLen = 0; CommandHeader commandHeader2 = commandHeader; IntPtr intPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CommandHeader))); Marshal.StructureToPtr((object)commandHeader2, intPtr, fDeleteOld: false); Marshal.Copy(intPtr, array, 0, Marshal.SizeOf(typeof(CommandHeader))); Marshal.FreeHGlobal(intPtr); m_stream.Write(array, 0, array.Length); } } private T ByteArrayToStructure(byte[] bytes, int offset) where T : struct { int num = Marshal.SizeOf(typeof(T)); if (num > bytes.Length - offset) { throw new ArgumentException("Byte array is too small to contain the structure."); } IntPtr intPtr = Marshal.AllocHGlobal(num); Marshal.Copy(bytes, offset, intPtr, num); T result = (T)Marshal.PtrToStructure(intPtr, typeof(T)); Marshal.FreeHGlobal(intPtr); return result; } public CommandDataServerGazeDataResult2 RequestEyeTrackingData() { if (!m_running) { return default(CommandDataServerGazeDataResult2); } return m_lastGazeState.GetValueOrDefault(); } public void TriggerEffectDisable(EVRControllerType controllerType) { if (m_running) { CommandDataClientTriggerEffectOff commandDataClientTriggerEffectOff = default(CommandDataClientTriggerEffectOff); commandDataClientTriggerEffectOff.controllerType = controllerType; CommandDataClientTriggerEffectOff data = commandDataClientTriggerEffectOff; SendIpcCommand(ECommandType.ClientTriggerEffectOff, data); } } public void TriggerEffectFeedback(EVRControllerType controllerType, byte position, byte strength) { if (m_running) { CommandDataClientTriggerEffectFeedback commandDataClientTriggerEffectFeedback = default(CommandDataClientTriggerEffectFeedback); commandDataClientTriggerEffectFeedback.controllerType = controllerType; commandDataClientTriggerEffectFeedback.position = position; commandDataClientTriggerEffectFeedback.strength = strength; CommandDataClientTriggerEffectFeedback data = commandDataClientTriggerEffectFeedback; SendIpcCommand(ECommandType.ClientTriggerEffectFeedback, data); } } public void TriggerEffectWeapon(EVRControllerType controllerType, byte startPosition, byte endPosition, byte strength) { if (m_running) { CommandDataClientTriggerEffectWeapon commandDataClientTriggerEffectWeapon = default(CommandDataClientTriggerEffectWeapon); commandDataClientTriggerEffectWeapon.controllerType = controllerType; commandDataClientTriggerEffectWeapon.startPosition = startPosition; commandDataClientTriggerEffectWeapon.endPosition = endPosition; commandDataClientTriggerEffectWeapon.strength = strength; CommandDataClientTriggerEffectWeapon data = commandDataClientTriggerEffectWeapon; SendIpcCommand(ECommandType.ClientTriggerEffectWeapon, data); } } public void TriggerEffectVibration(EVRControllerType controllerType, byte position, byte amplitude, byte frequency) { if (m_running) { CommandDataClientTriggerEffectVibration commandDataClientTriggerEffectVibration = default(CommandDataClientTriggerEffectVibration); commandDataClientTriggerEffectVibration.controllerType = controllerType; commandDataClientTriggerEffectVibration.position = position; commandDataClientTriggerEffectVibration.amplitude = amplitude; commandDataClientTriggerEffectVibration.frequency = frequency; CommandDataClientTriggerEffectVibration data = commandDataClientTriggerEffectVibration; SendIpcCommand(ECommandType.ClientTriggerEffectVibration, data); } } public void TriggerEffectMultiplePositionFeedback(EVRControllerType controllerType, byte[] strength) { if (m_running) { CommandDataClientTriggerEffectMultiplePositionFeedback commandDataClientTriggerEffectMultiplePositionFeedback = default(CommandDataClientTriggerEffectMultiplePositionFeedback); commandDataClientTriggerEffectMultiplePositionFeedback.controllerType = controllerType; commandDataClientTriggerEffectMultiplePositionFeedback.strength = strength; CommandDataClientTriggerEffectMultiplePositionFeedback data = commandDataClientTriggerEffectMultiplePositionFeedback; SendIpcCommand(ECommandType.ClientTriggerEffectMultiplePositionFeedback, data); } } public void TriggerEffectSlopeFeedback(EVRControllerType controllerType, byte startPosition, byte endPosition, byte startStrength, byte endStrength) { if (m_running) { CommandDataClientTriggerEffectSlopeFeedback commandDataClientTriggerEffectSlopeFeedback = default(CommandDataClientTriggerEffectSlopeFeedback); commandDataClientTriggerEffectSlopeFeedback.controllerType = controllerType; commandDataClientTriggerEffectSlopeFeedback.startPosition = startPosition; commandDataClientTriggerEffectSlopeFeedback.endPosition = endPosition; commandDataClientTriggerEffectSlopeFeedback.startStrength = startStrength; commandDataClientTriggerEffectSlopeFeedback.endStrength = endStrength; CommandDataClientTriggerEffectSlopeFeedback data = commandDataClientTriggerEffectSlopeFeedback; SendIpcCommand(ECommandType.ClientTriggerEffectSlopeFeedback, data); } } public void TriggerEffectMultiplePositionVibration(EVRControllerType controllerType, byte frequency, byte[] amplitude) { if (m_running) { CommandDataClientTriggerEffectMultiplePositionVibration commandDataClientTriggerEffectMultiplePositionVibration = default(CommandDataClientTriggerEffectMultiplePositionVibration); commandDataClientTriggerEffectMultiplePositionVibration.controllerType = controllerType; commandDataClientTriggerEffectMultiplePositionVibration.frequency = frequency; commandDataClientTriggerEffectMultiplePositionVibration.amplitude = amplitude; CommandDataClientTriggerEffectMultiplePositionVibration data = commandDataClientTriggerEffectMultiplePositionVibration; SendIpcCommand(ECommandType.ClientTriggerEffectMultiplePositionVibration, data); } } } public enum ECommandType : ushort { ClientPing, ServerPong, ClientRequestHandshake, ServerHandshakeResult, ClientRequestGazeData, ServerGazeDataResult, ClientTriggerEffectOff, ClientTriggerEffectFeedback, ClientTriggerEffectWeapon, ClientTriggerEffectVibration, ClientTriggerEffectMultiplePositionFeedback, ClientTriggerEffectSlopeFeedback, ClientTriggerEffectMultiplePositionVibration } public enum EHandshakeResult : byte { Failed, Success, Outdated } public enum EVRControllerType : byte { Left, Right, Both } public struct CommandDataClientRequestHandshake { public ushort ipcVersion; public uint processId; } public struct CommandDataServerHandshakeResult { public EHandshakeResult result; public ushort ipcVersion; } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct GazeVector3 { public float x; public float y; public float z; } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct GazeEyeResult { [MarshalAs(UnmanagedType.I1)] public bool isGazeOriginValid; public GazeVector3 gazeOriginMm; [MarshalAs(UnmanagedType.I1)] public bool isGazeDirValid; public GazeVector3 gazeDirNorm; [MarshalAs(UnmanagedType.I1)] public bool isPupilDiaValid; public float pupilDiaMm; [MarshalAs(UnmanagedType.I1)] public bool isBlinkValid; [MarshalAs(UnmanagedType.I1)] public bool blink; } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct GazeEyeResult2 { [MarshalAs(UnmanagedType.I1)] public bool isGazeOriginValid; public GazeVector3 gazeOriginMm; [MarshalAs(UnmanagedType.I1)] public bool isGazeDirValid; public GazeVector3 gazeDirNorm; [MarshalAs(UnmanagedType.I1)] public bool isPupilDiaValid; public float pupilDiaMm; [MarshalAs(UnmanagedType.I1)] public bool isBlinkValid; [MarshalAs(UnmanagedType.I1)] public bool blink; [MarshalAs(UnmanagedType.I1)] public bool isOpenEnabled; public float open; } public struct CommandDataServerGazeDataResult { public GazeEyeResult leftEye; public GazeEyeResult rightEye; } public struct CommandDataServerGazeDataResult2 { public GazeEyeResult2 leftEye; public GazeEyeResult2 rightEye; } public struct CommandHeader { public ECommandType type; public int dataLen; } public struct CommandDataClientTriggerEffectOff { public EVRControllerType controllerType; } public struct CommandDataClientTriggerEffectFeedback { public EVRControllerType controllerType; public byte position; public byte strength; } public struct CommandDataClientTriggerEffectWeapon { public EVRControllerType controllerType; public byte startPosition; public byte endPosition; public byte strength; } public struct CommandDataClientTriggerEffectVibration { public EVRControllerType controllerType; public byte position; public byte amplitude; public byte frequency; } public struct CommandDataClientTriggerEffectMultiplePositionFeedback { public EVRControllerType controllerType; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] public byte[] strength; } public struct CommandDataClientTriggerEffectSlopeFeedback { public EVRControllerType controllerType; public byte startPosition; public byte endPosition; public byte startStrength; public byte endStrength; } public struct CommandDataClientTriggerEffectMultiplePositionVibration { public EVRControllerType controllerType; public byte frequency; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] public byte[] amplitude; } } namespace plugin.Properties { [CompilerGenerated] [GeneratedCode("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.14.0.0")] internal sealed class Settings : ApplicationSettingsBase { private static Settings defaultInstance = (Settings)(object)SettingsBase.Synchronized((SettingsBase)(object)new Settings()); public static Settings Default => defaultInstance; } }