using System;
using System.Diagnostics;
using System.Net.Sockets;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Threading;
using System.Threading.Tasks;
using BoneLib.BoneMenu;
using MelonLoader;
using Microsoft.CodeAnalysis;
using PSVR2Toolkit.CAPI;
using UnityEngine;
using hapticTriggers;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: MelonInfo(typeof(Core), "hapticTriggers", "1.0.0", "MajedCT", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("hapticTriggers")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("hapticTriggers")]
[assembly: AssemblyTitle("hapticTriggers")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.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 PSVR2Toolkit.CAPI
{
public class IpcClient
{
private const ushort IPC_SERVER_PORT = 3364;
private const ushort k_unIpcVersion = 1;
private static IpcClient m_pInstance;
private bool m_running = false;
private TcpClient? m_client;
private NetworkStream? m_stream;
private Thread? m_receiveThread;
private readonly object m_gazeStateLock = new object();
private TaskCompletionSource<CommandDataServerGazeDataResult>? m_gazeTask;
private CancellationTokenSource m_forceShutdownToken;
private int m_gazePumpPeriodMs = 8;
private CommandDataServerGazeDataResult? m_lastGazeState = null;
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?.Dispose();
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 = 1;
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<CommandHeader>())
{
Console.WriteLine("[IPC_CLIENT] Received invalid command header size.");
}
else
{
HandleIpcCommand(array, num2);
}
}
else
{
Thread.Sleep(1);
}
}
}
catch (OperationCanceledException)
{
}
catch (Exception ex2)
{
if (m_running)
{
Console.WriteLine("[IPC_CLIENT] Error in receive loop: " + ex2.Message);
}
}
}
private void HandleIpcCommand(byte[] pBuffer, int bytesReceived)
{
CommandHeader commandHeader = ByteArrayToStructure<CommandHeader>(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<CommandDataServerHandshakeResult>())
{
CommandDataServerHandshakeResult commandDataServerHandshakeResult = ByteArrayToStructure<CommandDataServerHandshakeResult>(pBuffer, Marshal.SizeOf<CommandHeader>());
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 (commandHeader.dataLen == Marshal.SizeOf<CommandDataServerGazeDataResult>())
{
CommandDataServerGazeDataResult value = ByteArrayToStructure<CommandDataServerGazeDataResult>(pBuffer, Marshal.SizeOf<CommandHeader>());
m_lastGazeState = value;
}
break;
case ECommandType.ClientRequestHandshake:
case ECommandType.ClientRequestGazeData:
break;
}
}
private void SendIpcCommand<T>(ECommandType type, T data = default(T)) where T : struct
{
if (m_running)
{
int num = ((!data.Equals(default(T))) ? Marshal.SizeOf<T>() : 0);
int num2 = Marshal.SizeOf<CommandHeader>() + num;
byte[] array = new byte[num2];
CommandHeader commandHeader = default(CommandHeader);
commandHeader.type = type;
commandHeader.dataLen = num;
CommandHeader structure = commandHeader;
IntPtr intPtr = Marshal.AllocHGlobal(Marshal.SizeOf<CommandHeader>());
Marshal.StructureToPtr(structure, intPtr, fDeleteOld: false);
Marshal.Copy(intPtr, array, 0, Marshal.SizeOf<CommandHeader>());
Marshal.FreeHGlobal(intPtr);
if (num > 0)
{
IntPtr intPtr2 = Marshal.AllocHGlobal(num);
Marshal.StructureToPtr(data, intPtr2, fDeleteOld: false);
Marshal.Copy(intPtr2, array, Marshal.SizeOf<CommandHeader>(), num);
Marshal.FreeHGlobal(intPtr2);
}
m_stream.Write(array, 0, array.Length);
}
}
private void SendIpcCommand(ECommandType type)
{
if (m_running)
{
int num = Marshal.SizeOf<CommandHeader>();
byte[] array = new byte[num];
CommandHeader commandHeader = default(CommandHeader);
commandHeader.type = type;
commandHeader.dataLen = 0;
CommandHeader structure = commandHeader;
IntPtr intPtr = Marshal.AllocHGlobal(Marshal.SizeOf<CommandHeader>());
Marshal.StructureToPtr(structure, intPtr, fDeleteOld: false);
Marshal.Copy(intPtr, array, 0, Marshal.SizeOf<CommandHeader>());
Marshal.FreeHGlobal(intPtr);
m_stream.Write(array, 0, array.Length);
}
}
private T ByteArrayToStructure<T>(byte[] bytes, int offset) where T : struct
{
int num = Marshal.SizeOf<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 CommandDataServerGazeDataResult RequestEyeTrackingData()
{
if (!m_running)
{
return default(CommandDataServerGazeDataResult);
}
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;
}
public struct CommandDataServerGazeDataResult
{
public GazeEyeResult leftEye;
public GazeEyeResult 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 hapticTriggers
{
public class Core : MelonMod
{
private IpcClient ipcClient;
private bool Toggle;
public override void OnInitializeMelon()
{
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
Page val = Page.Root.CreatePage("crappy haptic triggers", Color.magenta, 0, true);
val.CreateBool("this shit doesnt even work", Color.yellow, false, (Action<bool>)delegate(bool value)
{
Toggle = value;
});
((MelonBase)this).LoggerInstance.Msg("Initializing PSVR2 triggers!");
ipcClient = IpcClient.Instance();
if (ipcClient.Start())
{
MelonLogger.Msg("connected to PSVR2Toolkit, i guess the mod works lmao");
}
else
{
MelonLogger.Msg("Failed to connect to PSVR2Toolkit, is PSVR2Toolkit running? \n(ur probably a stinky non-psvr2 user)");
}
}
public override void OnUpdate()
{
bool toggle = Toggle;
if (toggle = true)
{
EVRControllerType controllerType = EVRControllerType.Left;
EVRControllerType controllerType2 = EVRControllerType.Right;
ipcClient.TriggerEffectWeapon(controllerType, 2, 4, 3);
ipcClient.TriggerEffectFeedback(controllerType, 128, 100);
ipcClient.TriggerEffectWeapon(controllerType2, 2, 4, 3);
ipcClient.TriggerEffectFeedback(controllerType2, 128, 100);
}
else
{
EVRControllerType controllerType3 = EVRControllerType.Left;
EVRControllerType controllerType4 = EVRControllerType.Right;
ipcClient.TriggerEffectWeapon(controllerType3, 0, 0, 0);
ipcClient.TriggerEffectFeedback(controllerType3, 0, 0);
ipcClient.TriggerEffectWeapon(controllerType4, 0, 0, 0);
ipcClient.TriggerEffectFeedback(controllerType4, 0, 0);
}
}
public override void OnApplicationQuit()
{
if (ipcClient != null)
{
EVRControllerType controllerType = EVRControllerType.Left;
EVRControllerType controllerType2 = EVRControllerType.Right;
ipcClient.TriggerEffectWeapon(controllerType, 0, 0, 0);
ipcClient.TriggerEffectFeedback(controllerType, 0, 0);
ipcClient.TriggerEffectWeapon(controllerType2, 0, 0, 0);
ipcClient.TriggerEffectFeedback(controllerType2, 0, 0);
ipcClient.Stop();
MelonLogger.Msg("PSVR2 IPC Connection closed");
}
}
}
}