Decompiled source of PeakIntiface v0.4.2
BepInEx\plugins\PeakIntiface\Buttplug.dll
Decompiled 2 weeks agousing System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net.WebSockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Text; using System.Threading; using System.Threading.Tasks; using Buttplug.Core; using Buttplug.Core.Messages; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("Buttplug.Test")] [assembly: InternalsVisibleTo("Buttplug.Client.Test")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Nonpolynomial Labs, LLC")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright Nonpolynomial Labs, LLC")] [assembly: AssemblyDescription("Buttplug Sex Toy Control Library. Contains Core (messages, errors, etc), Client, and Websocket Connector components")] [assembly: AssemblyFileVersion("5.0.1.0")] [assembly: AssemblyInformationalVersion("5.0.1+08b292884fa1a73f5cdd64cfa4aa956a2b22d14b")] [assembly: AssemblyProduct("Buttplug")] [assembly: AssemblyTitle("Buttplug")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/buttplugio/buttplug-csharp")] [assembly: AssemblyVersion("5.0.1.0")] namespace Buttplug.Core { public static class ButtplugConsts { public const uint SystemMsgId = 0u; public const uint DefaultMsgId = 1u; public const uint MaxId = uint.MaxValue; public const uint ProtocolVersionMajor = 4u; public const uint ProtocolVersionMinor = 0u; public const uint CurrentSpecVersion = 4u; } public class ButtplugDeviceException : ButtplugException { public ButtplugDeviceException(string message, uint id = 0u, Exception inner = null) : base(message, Error.ErrorClass.ERROR_DEVICE, id, inner) { } } public class ButtplugException : Exception { public Error ButtplugErrorMessage { get; } public static ButtplugException FromError(Error msg) { return msg.ErrorCode switch { Error.ErrorClass.ERROR_DEVICE => new ButtplugDeviceException(msg.ErrorMessage, msg.Id), Error.ErrorClass.ERROR_INIT => new ButtplugHandshakeException(msg.ErrorMessage, msg.Id), Error.ErrorClass.ERROR_MSG => new ButtplugMessageException(msg.ErrorMessage, msg.Id), Error.ErrorClass.ERROR_PING => new ButtplugPingException(msg.ErrorMessage, msg.Id), Error.ErrorClass.ERROR_UNKNOWN => new ButtplugException(msg.ErrorMessage, msg.Id), _ => new ButtplugException(msg.ErrorMessage, msg.Id), }; } public ButtplugException(string message, uint id = 0u, Exception inner = null) : this(message, Error.ErrorClass.ERROR_UNKNOWN, id, inner) { } public ButtplugException(string message, Error.ErrorClass err = Error.ErrorClass.ERROR_UNKNOWN, uint id = 0u, Exception inner = null) : base(message, inner) { ButtplugErrorMessage = new Error(message, err, id); } } public class ButtplugExceptionEventArgs : EventArgs { public ButtplugException Exception { get; } public ButtplugExceptionEventArgs(ButtplugException ex) { Exception = ex; } } public class ButtplugHandshakeException : ButtplugException { public ButtplugHandshakeException(string message, uint id = 0u, Exception inner = null) : base(message, Error.ErrorClass.ERROR_INIT, id, inner) { } } public class ButtplugJsonMessageParser { private readonly Dictionary<string, Type> _messageTypes; private readonly JsonSerializer _serializer; public ButtplugJsonMessageParser() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown _serializer = new JsonSerializer { MissingMemberHandling = (MissingMemberHandling)0 }; _messageTypes = new Dictionary<string, Type>(); foreach (Type allMessageType in ButtplugUtils.GetAllMessageTypes()) { _messageTypes.Add(allMessageType.Name, allMessageType); } if (!_messageTypes.Any()) { throw new ButtplugMessageException("No message types available."); } } public IEnumerable<ButtplugMessage> Deserialize(string jsonMsg) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_0031: Expected O, but got Unknown //IL_005b: Expected O, but got Unknown //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) JsonTextReader val = new JsonTextReader((TextReader)new StringReader(jsonMsg)) { CloseInput = false, SupportMultipleContent = true }; List<ButtplugMessage> list = new List<ButtplugMessage>(); while (true) { try { if (!((JsonReader)val).Read()) { return list; } } catch (JsonReaderException ex) { JsonReaderException ex2 = ex; throw new ButtplugMessageException("Not valid JSON: " + jsonMsg + " - " + ((Exception)(object)ex2).Message); } JArray val2; try { val2 = JArray.Load((JsonReader)(object)val); } catch (JsonReaderException ex3) { JsonReaderException ex4 = ex3; throw new ButtplugMessageException("Not valid JSON: " + jsonMsg + " - " + ((Exception)(object)ex4).Message); } foreach (JObject item in ((JToken)val2).Children<JObject>()) { string name = item.Properties().First().Name; if (!_messageTypes.ContainsKey(name)) { throw new ButtplugMessageException(name + " is not a valid message class"); } list.Add(DeserializeAs(item, _messageTypes[name])); } } } private ButtplugMessage DeserializeAs(JObject obj, Type msgType) { //IL_00c0: Expected O, but got Unknown if (!msgType.IsSubclassOf(typeof(ButtplugMessage))) { throw new ButtplugMessageException("Type " + msgType.Name + " is not a subclass of ButtplugMessage"); } if (msgType.Namespace != "Buttplug.Core.Messages") { throw new ButtplugMessageException("Type " + msgType.Name + " (" + msgType.Namespace + ") is not in the namespace of Buttplug.Core.Messages"); } string name = ButtplugMessage.GetName(msgType); try { return (ButtplugMessage)((JToken)Extensions.Value<JObject>((IEnumerable<JToken>)obj[name])).ToObject(msgType, _serializer); } catch (InvalidCastException ex) { throw new ButtplugMessageException($"Could not create message for JSON {obj}: {ex.Message}"); } catch (JsonSerializationException ex2) { JsonSerializationException ex3 = ex2; throw new ButtplugMessageException($"Could not create message for JSON {obj}: {((Exception)(object)ex3).Message}"); } } public string Serialize(ButtplugMessage msg) { //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) if (msg.GetType().Namespace != "Buttplug.Core.Messages") { throw new ButtplugMessageException("Type " + msg.GetType().Name + " (" + msg.GetType().Namespace + ") is not in the namespace of Buttplug.Core.Messages"); } JObject val = ButtplugMessageToJObject(msg); if (val == null) { throw new ButtplugMessageException("Message cannot be converted to JSON.", msg.Id); } JArray val2 = new JArray(); val2.Add((JToken)(object)val); return ((JToken)val2).ToString((Formatting)0, Array.Empty<JsonConverter>()); } public string Serialize(IEnumerable<ButtplugMessage> msgs) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown JArray val = new JArray(); foreach (ButtplugMessage msg in msgs) { JObject val2 = ButtplugMessageToJObject(msg); if (val2 != null) { val.Add((JToken)(object)val2); } } if (!((IEnumerable<JToken>)val).Any()) { throw new ButtplugMessageException("No messages serialized."); } return ((JToken)val).ToString((Formatting)0, Array.Empty<JsonConverter>()); } private JObject ButtplugMessageToJObject(ButtplugMessage msg) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown return new JObject((object)new JProperty(msg.Name, (object)JObject.FromObject((object)msg))); } } public class ButtplugMessageException : ButtplugException { public ButtplugMessageException(string message, uint id = 0u, Exception inner = null) : base(message, Error.ErrorClass.ERROR_MSG, id, inner) { } } public class ButtplugPingException : ButtplugException { public ButtplugPingException(string message, uint id = 0u, Exception inner = null) : base(message, Error.ErrorClass.ERROR_PING, id, inner) { } } public static class ButtplugUtils { public static IEnumerable<Type> GetAllMessageTypes() { IEnumerable<Type> enumerable; try { enumerable = Assembly.GetAssembly(typeof(ButtplugMessage))?.GetTypes(); } catch (ReflectionTypeLoadException ex) { enumerable = ex.Types; } return (enumerable ?? throw new InvalidOperationException()).Where((Type type) => type != null && type.IsClass && type.IsSubclassOf(typeof(ButtplugMessage)) && type != typeof(ButtplugDeviceMessage)); } [DebuggerStepThrough] public static void ArgumentNotNull(object argument, string argumentName) { if (argument == null) { throw new ArgumentNullException(argumentName); } } public static Type GetMessageType(string messageName) { return Type.GetType("Buttplug.Core.Messages." + messageName); } } } namespace Buttplug.Core.Messages { public class ButtplugDeviceMessage : ButtplugMessage { [JsonProperty(/*Could not decode attribute arguments.*/)] public uint DeviceIndex { get; set; } public ButtplugDeviceMessage(uint id = 1u, uint deviceIndex = uint.MaxValue) : base(id) { DeviceIndex = deviceIndex; } } public abstract class ButtplugMessage { private static readonly Dictionary<Type, ButtplugMessageMetadata> _metadataCache = new Dictionary<Type, ButtplugMessageMetadata>(); [JsonProperty(/*Could not decode attribute arguments.*/)] public uint Id { get; set; } [JsonIgnore] public string Name => GetName(GetType()); protected ButtplugMessage(uint id) { Id = id; } private static T GetMessageAttribute<T>(Type msgType, Func<ButtplugMessageMetadata, T> func) { ButtplugUtils.ArgumentNotNull(msgType, "msgType"); ButtplugUtils.ArgumentNotNull(func, "func"); if (!msgType.IsSubclassOf(typeof(ButtplugMessage))) { throw new ArgumentException("Argument " + msgType.Name + " must be a subclass of ButtplugMessage"); } if (_metadataCache.ContainsKey(msgType)) { return func(_metadataCache[msgType]); } Attribute[] customAttributes = Attribute.GetCustomAttributes(msgType); for (int i = 0; i < customAttributes.Length; i++) { if (customAttributes[i] is ButtplugMessageMetadata buttplugMessageMetadata) { _metadataCache[msgType] = buttplugMessageMetadata; return func(buttplugMessageMetadata); } } throw new ArgumentException($"Type {msgType} does not have ButtplugMessageMetadata Attributes"); } public static string GetName(Type msgType) { return GetMessageAttribute(msgType, (ButtplugMessageMetadata md) => md.Name); } } public interface IButtplugMessageOutgoingOnly { } [AttributeUsage(AttributeTargets.Class)] public class ButtplugMessageMetadata : Attribute { public string Name { get; } public ButtplugMessageMetadata(string name) { Name = name; } } public interface IButtplugDeviceInfoMessage { string DeviceName { get; } uint DeviceIndex { get; } string DeviceDisplayName { get; } uint DeviceMessageTimingGap { get; } Dictionary<string, DeviceFeature> DeviceFeatures { get; } } public class MessageReceivedEventArgs : EventArgs { public ButtplugMessage Message { get; } public MessageReceivedEventArgs(ButtplugMessage message) { Message = message; } } [JsonConverter(typeof(StringEnumConverter))] public enum OutputType { [EnumMember(Value = "Unknown")] Unknown, [EnumMember(Value = "Vibrate")] Vibrate, [EnumMember(Value = "Oscillate")] Oscillate, [EnumMember(Value = "Rotate")] Rotate, [EnumMember(Value = "Position")] Position, [EnumMember(Value = "HwPositionWithDuration")] HwPositionWithDuration, [EnumMember(Value = "Led")] Led, [EnumMember(Value = "Temperature")] Temperature, [EnumMember(Value = "Constrict")] Constrict, [EnumMember(Value = "Spray")] Spray } [JsonConverter(typeof(StringEnumConverter))] public enum InputType { [EnumMember(Value = "Unknown")] Unknown, [EnumMember(Value = "Battery")] Battery, [EnumMember(Value = "RSSI")] RSSI, [EnumMember(Value = "Button")] Button, [EnumMember(Value = "Pressure")] Pressure, [EnumMember(Value = "Depth")] Depth, [EnumMember(Value = "Position")] Position } [JsonConverter(typeof(StringEnumConverter))] public enum InputCommandType { [EnumMember(Value = "Read")] Read, [EnumMember(Value = "Subscribe")] Subscribe, [EnumMember(Value = "Unsubscribe")] Unsubscribe } [ButtplugMessageMetadata("Ok")] public class Ok : ButtplugMessage, IButtplugMessageOutgoingOnly { public Ok(uint id) : base(id) { } } [ButtplugMessageMetadata("Error")] public class Error : ButtplugMessage, IButtplugMessageOutgoingOnly { public enum ErrorClass { ERROR_UNKNOWN, ERROR_INIT, ERROR_PING, ERROR_MSG, ERROR_DEVICE } [JsonProperty(/*Could not decode attribute arguments.*/)] public ErrorClass ErrorCode; [JsonProperty(/*Could not decode attribute arguments.*/)] public string ErrorMessage; public Error(string errorMessage, ErrorClass errorCode, uint id) : base(id) { ErrorMessage = errorMessage; ErrorCode = errorCode; } } public class DeviceFeatureOutput { [JsonProperty(/*Could not decode attribute arguments.*/)] public int[] Value; [JsonProperty(/*Could not decode attribute arguments.*/)] public int[] Duration; } public class DeviceFeatureInput { [JsonProperty(/*Could not decode attribute arguments.*/)] public int[][] Value; [JsonProperty(/*Could not decode attribute arguments.*/)] public InputCommandType[] Command; } public class DeviceFeature { [JsonProperty(/*Could not decode attribute arguments.*/)] public string FeatureDescription; [JsonProperty(/*Could not decode attribute arguments.*/)] public uint FeatureIndex; [JsonProperty(/*Could not decode attribute arguments.*/)] public Dictionary<string, DeviceFeatureOutput> Output; [JsonProperty(/*Could not decode attribute arguments.*/)] public Dictionary<string, DeviceFeatureInput> Input; public bool HasOutput(OutputType type) { if (Output != null) { return Output.ContainsKey(type.ToString()); } return false; } public bool HasInput(InputType type) { if (Input != null) { return Input.ContainsKey(type.ToString()); } return false; } public DeviceFeatureOutput GetOutput(OutputType type) { if (Output == null || !Output.ContainsKey(type.ToString())) { return null; } return Output[type.ToString()]; } public DeviceFeatureInput GetInput(InputType type) { if (Input == null || !Input.ContainsKey(type.ToString())) { return null; } return Input[type.ToString()]; } } public class DeviceInfo : IButtplugDeviceInfoMessage { [JsonProperty(/*Could not decode attribute arguments.*/)] public string DeviceName; [JsonProperty(/*Could not decode attribute arguments.*/)] public uint DeviceIndex; [JsonProperty(/*Could not decode attribute arguments.*/)] public string DeviceDisplayName; [JsonProperty(/*Could not decode attribute arguments.*/)] public uint DeviceMessageTimingGap; [JsonProperty(/*Could not decode attribute arguments.*/)] public Dictionary<string, DeviceFeature> DeviceFeatures; string IButtplugDeviceInfoMessage.DeviceName => DeviceName; uint IButtplugDeviceInfoMessage.DeviceIndex => DeviceIndex; string IButtplugDeviceInfoMessage.DeviceDisplayName => DeviceDisplayName; uint IButtplugDeviceInfoMessage.DeviceMessageTimingGap => DeviceMessageTimingGap; Dictionary<string, DeviceFeature> IButtplugDeviceInfoMessage.DeviceFeatures => DeviceFeatures; public DeviceFeature GetFeature(uint index) { string key = index.ToString(); if (DeviceFeatures != null && DeviceFeatures.ContainsKey(key)) { return DeviceFeatures[key]; } return null; } public IEnumerable<DeviceFeature> GetAllFeatures() { if (DeviceFeatures == null) { return Enumerable.Empty<DeviceFeature>(); } return DeviceFeatures.Values; } public IEnumerable<DeviceFeature> GetFeaturesWithOutput(OutputType type) { return from f in GetAllFeatures() where f.HasOutput(type) select f; } public IEnumerable<DeviceFeature> GetFeaturesWithInput(InputType type) { return from f in GetAllFeatures() where f.HasInput(type) select f; } } public class DeviceListConverter : JsonConverter<DeviceList> { public override DeviceList ReadJson(JsonReader reader, Type objectType, DeviceList existingValue, bool hasExistingValue, JsonSerializer serializer) { JObject val = JObject.Load(reader); DeviceList deviceList = new DeviceList(); if (val["Id"] != null) { deviceList.Id = Extensions.Value<uint>((IEnumerable<JToken>)val["Id"]); } deviceList.Devices = new Dictionary<uint, DeviceInfo>(); if (val["Devices"] != null) { JToken obj = val["Devices"]; JObject val2 = (JObject)(object)((obj is JObject) ? obj : null); if (val2 != null) { foreach (JProperty item in val2.Properties()) { DeviceInfo value = item.Value.ToObject<DeviceInfo>(serializer); deviceList.Devices[uint.Parse(item.Name)] = value; } } } return deviceList; } public override void WriteJson(JsonWriter writer, DeviceList value, JsonSerializer serializer) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown JObject val = new JObject(); val["Id"] = JToken.op_Implicit(value.Id); JObject val2 = new JObject(); if (value.Devices != null) { foreach (KeyValuePair<uint, DeviceInfo> device in value.Devices) { ((JToken)val2)[(object)device.Key] = (JToken)(object)JObject.FromObject((object)device.Value, serializer); } } val["Devices"] = (JToken)(object)val2; ((JToken)val).WriteTo(writer, Array.Empty<JsonConverter>()); } } [ButtplugMessageMetadata("DeviceList")] [JsonConverter(typeof(DeviceListConverter))] public class DeviceList : ButtplugMessage, IButtplugMessageOutgoingOnly { [JsonProperty(/*Could not decode attribute arguments.*/)] public Dictionary<uint, DeviceInfo> Devices; public DeviceList(Dictionary<uint, DeviceInfo> devices, uint id) : base(id) { Devices = devices ?? new Dictionary<uint, DeviceInfo>(); } internal DeviceList() : base(0u) { Devices = new Dictionary<uint, DeviceInfo>(); } public IEnumerable<DeviceInfo> GetAllDevices() { IEnumerable<DeviceInfo> enumerable = Devices?.Values; return enumerable ?? Enumerable.Empty<DeviceInfo>(); } public DeviceInfo GetDevice(uint index) { if (Devices != null && Devices.ContainsKey(index)) { return Devices[index]; } return null; } } [ButtplugMessageMetadata("RequestDeviceList")] public class RequestDeviceList : ButtplugMessage { public RequestDeviceList(uint id = 1u) : base(id) { } } [ButtplugMessageMetadata("StartScanning")] public class StartScanning : ButtplugMessage { public StartScanning(uint id = 1u) : base(id) { } } [ButtplugMessageMetadata("StopScanning")] public class StopScanning : ButtplugMessage { public StopScanning(uint id = 1u) : base(id) { } } [ButtplugMessageMetadata("ScanningFinished")] public class ScanningFinished : ButtplugMessage, IButtplugMessageOutgoingOnly { public ScanningFinished() : base(0u) { } } [ButtplugMessageMetadata("RequestServerInfo")] public class RequestServerInfo : ButtplugMessage { [JsonProperty(/*Could not decode attribute arguments.*/)] public string ClientName; [JsonProperty(/*Could not decode attribute arguments.*/)] public uint ProtocolVersionMajor; [JsonProperty(/*Could not decode attribute arguments.*/)] public uint ProtocolVersionMinor; public RequestServerInfo(string clientName, uint id = 1u, uint protocolVersionMajor = 4u, uint protocolVersionMinor = 0u) : base(id) { ClientName = clientName; ProtocolVersionMajor = protocolVersionMajor; ProtocolVersionMinor = protocolVersionMinor; } } [ButtplugMessageMetadata("ServerInfo")] public class ServerInfo : ButtplugMessage, IButtplugMessageOutgoingOnly { [JsonProperty(/*Could not decode attribute arguments.*/)] public uint ProtocolVersionMajor; [JsonProperty(/*Could not decode attribute arguments.*/)] public uint ProtocolVersionMinor; [JsonProperty(/*Could not decode attribute arguments.*/)] public uint MaxPingTime; [JsonProperty(/*Could not decode attribute arguments.*/)] public string ServerName; [JsonIgnore] public uint MessageVersion => ProtocolVersionMajor; public ServerInfo(string serverName, uint protocolVersionMajor, uint protocolVersionMinor, uint maxPingTime, uint id = 1u) : base(id) { ServerName = serverName; ProtocolVersionMajor = protocolVersionMajor; ProtocolVersionMinor = protocolVersionMinor; MaxPingTime = maxPingTime; } } [ButtplugMessageMetadata("Ping")] public class Ping : ButtplugMessage { public Ping(uint id = 1u) : base(id) { } } [ButtplugMessageMetadata("StopCmd")] public class StopCmd : ButtplugMessage { [JsonProperty(/*Could not decode attribute arguments.*/)] public uint? DeviceIndex; [JsonProperty(/*Could not decode attribute arguments.*/)] public uint? FeatureIndex; [JsonProperty(/*Could not decode attribute arguments.*/)] public bool? Inputs; [JsonProperty(/*Could not decode attribute arguments.*/)] public bool? Outputs; public StopCmd(uint id = 1u, uint? deviceIndex = null, uint? featureIndex = null, bool? inputs = null, bool? outputs = null) : base(id) { DeviceIndex = deviceIndex; FeatureIndex = featureIndex; Inputs = inputs; Outputs = outputs; } } [ButtplugMessageMetadata("StopDeviceCmd")] public class StopDeviceCmd : ButtplugDeviceMessage { [JsonProperty(/*Could not decode attribute arguments.*/)] public bool? Inputs; [JsonProperty(/*Could not decode attribute arguments.*/)] public bool? Outputs; public StopDeviceCmd(uint deviceIndex = uint.MaxValue, uint id = 1u, bool? inputs = null, bool? outputs = null) : base(id, deviceIndex) { Inputs = inputs; Outputs = outputs; } } [ButtplugMessageMetadata("StopAllDevices")] public class StopAllDevices : ButtplugMessage { [JsonProperty(/*Could not decode attribute arguments.*/)] public bool? Inputs; [JsonProperty(/*Could not decode attribute arguments.*/)] public bool? Outputs; public StopAllDevices(uint id = 1u, bool? inputs = null, bool? outputs = null) : base(id) { Inputs = inputs; Outputs = outputs; } } public class OutputCommandValue { [JsonProperty(/*Could not decode attribute arguments.*/)] public int Value; public OutputCommandValue() { } public OutputCommandValue(int value) { Value = value; } } public class OutputCommandValueWithDuration { [JsonProperty(/*Could not decode attribute arguments.*/)] public int Value; [JsonProperty(/*Could not decode attribute arguments.*/)] public uint Duration; public OutputCommandValueWithDuration() { } public OutputCommandValueWithDuration(int value, uint duration) { Value = value; Duration = duration; } } public class OutputCmdConverter : JsonConverter<OutputCmd> { public override OutputCmd ReadJson(JsonReader reader, Type objectType, OutputCmd existingValue, bool hasExistingValue, JsonSerializer serializer) { JObject val = JObject.Load(reader); OutputCmd outputCmd = new OutputCmd(); if (val["Id"] != null) { outputCmd.Id = Extensions.Value<uint>((IEnumerable<JToken>)val["Id"]); } if (val["DeviceIndex"] != null) { outputCmd.DeviceIndex = Extensions.Value<uint>((IEnumerable<JToken>)val["DeviceIndex"]); } if (val["FeatureIndex"] != null) { outputCmd.FeatureIndex = Extensions.Value<uint>((IEnumerable<JToken>)val["FeatureIndex"]); } outputCmd.Command = new Dictionary<string, object>(); if (val["Command"] != null) { JToken obj = val["Command"]; JObject val2 = (JObject)(object)((obj is JObject) ? obj : null); if (val2 != null) { foreach (JProperty item in val2.Properties()) { if (item.Name == "HwPositionWithDuration") { outputCmd.Command[item.Name] = item.Value.ToObject<OutputCommandValueWithDuration>(serializer); } else { outputCmd.Command[item.Name] = item.Value.ToObject<OutputCommandValue>(serializer); } } } } return outputCmd; } public override void WriteJson(JsonWriter writer, OutputCmd value, JsonSerializer serializer) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown JObject val = new JObject(); val["Id"] = JToken.op_Implicit(value.Id); val["DeviceIndex"] = JToken.op_Implicit(value.DeviceIndex); val["FeatureIndex"] = JToken.op_Implicit(value.FeatureIndex); JObject val2 = new JObject(); if (value.Command != null) { foreach (KeyValuePair<string, object> item in value.Command) { val2[item.Key] = (JToken)(object)JObject.FromObject(item.Value, serializer); } } val["Command"] = (JToken)(object)val2; ((JToken)val).WriteTo(writer, Array.Empty<JsonConverter>()); } } [ButtplugMessageMetadata("OutputCmd")] [JsonConverter(typeof(OutputCmdConverter))] public class OutputCmd : ButtplugDeviceMessage { [JsonProperty(/*Could not decode attribute arguments.*/)] public uint FeatureIndex; [JsonProperty(/*Could not decode attribute arguments.*/)] public Dictionary<string, object> Command; public OutputCmd() { Command = new Dictionary<string, object>(); } public OutputCmd(uint deviceIndex, uint featureIndex, uint id = 1u) : base(id, deviceIndex) { FeatureIndex = featureIndex; Command = new Dictionary<string, object>(); } public static OutputCmd Create(uint deviceIndex, uint featureIndex, OutputType outputType, int value, uint id = 1u) { return new OutputCmd(deviceIndex, featureIndex, id) { Command = { [outputType.ToString()] = new OutputCommandValue(value) } }; } public static OutputCmd CreatePositionWithDuration(uint deviceIndex, uint featureIndex, int position, uint duration, uint id = 1u) { return new OutputCmd(deviceIndex, featureIndex, id) { Command = { [OutputType.HwPositionWithDuration.ToString()] = new OutputCommandValueWithDuration(position, duration) } }; } public OutputCmd WithValue(OutputType outputType, int value) { Command[outputType.ToString()] = new OutputCommandValue(value); return this; } public OutputCmd WithPositionAndDuration(int position, uint duration) { Command[OutputType.HwPositionWithDuration.ToString()] = new OutputCommandValueWithDuration(position, duration); return this; } } [ButtplugMessageMetadata("InputCmd")] public class InputCmd : ButtplugDeviceMessage { [JsonProperty(/*Could not decode attribute arguments.*/)] public uint FeatureIndex; [JsonProperty(/*Could not decode attribute arguments.*/)] public InputType Type; [JsonProperty(/*Could not decode attribute arguments.*/)] public InputCommandType Command; public InputCmd() { } public InputCmd(uint deviceIndex, uint featureIndex, InputType type, InputCommandType command, uint id = 1u) : base(id, deviceIndex) { FeatureIndex = featureIndex; Type = type; Command = command; } public static InputCmd Read(uint deviceIndex, uint featureIndex, InputType type, uint id = 1u) { return new InputCmd(deviceIndex, featureIndex, type, InputCommandType.Read, id); } public static InputCmd Subscribe(uint deviceIndex, uint featureIndex, InputType type, uint id = 1u) { return new InputCmd(deviceIndex, featureIndex, type, InputCommandType.Subscribe, id); } public static InputCmd Unsubscribe(uint deviceIndex, uint featureIndex, InputType type, uint id = 1u) { return new InputCmd(deviceIndex, featureIndex, type, InputCommandType.Unsubscribe, id); } } public class InputReadingValue { [JsonProperty(/*Could not decode attribute arguments.*/)] public double Value; public InputReadingValue() { } public InputReadingValue(double value) { Value = value; } } public class InputReadingConverter : JsonConverter<InputReading> { public override InputReading ReadJson(JsonReader reader, Type objectType, InputReading existingValue, bool hasExistingValue, JsonSerializer serializer) { JObject val = JObject.Load(reader); InputReading inputReading = new InputReading(); if (val["Id"] != null) { inputReading.Id = Extensions.Value<uint>((IEnumerable<JToken>)val["Id"]); } if (val["DeviceIndex"] != null) { inputReading.DeviceIndex = Extensions.Value<uint>((IEnumerable<JToken>)val["DeviceIndex"]); } if (val["FeatureIndex"] != null) { inputReading.FeatureIndex = Extensions.Value<uint>((IEnumerable<JToken>)val["FeatureIndex"]); } inputReading.Reading = new Dictionary<string, InputReadingValue>(); if (val["Reading"] != null) { JToken obj = val["Reading"]; JObject val2 = (JObject)(object)((obj is JObject) ? obj : null); if (val2 != null) { foreach (JProperty item in val2.Properties()) { inputReading.Reading[item.Name] = item.Value.ToObject<InputReadingValue>(serializer); } } } return inputReading; } public override void WriteJson(JsonWriter writer, InputReading value, JsonSerializer serializer) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown JObject val = new JObject(); val["Id"] = JToken.op_Implicit(value.Id); val["DeviceIndex"] = JToken.op_Implicit(value.DeviceIndex); val["FeatureIndex"] = JToken.op_Implicit(value.FeatureIndex); JObject val2 = new JObject(); if (value.Reading != null) { foreach (KeyValuePair<string, InputReadingValue> item in value.Reading) { val2[item.Key] = (JToken)(object)JObject.FromObject((object)item.Value, serializer); } } val["Reading"] = (JToken)(object)val2; ((JToken)val).WriteTo(writer, Array.Empty<JsonConverter>()); } } [ButtplugMessageMetadata("InputReading")] [JsonConverter(typeof(InputReadingConverter))] public class InputReading : ButtplugDeviceMessage, IButtplugMessageOutgoingOnly { [JsonProperty(/*Could not decode attribute arguments.*/)] public uint FeatureIndex; [JsonProperty(/*Could not decode attribute arguments.*/)] public Dictionary<string, InputReadingValue> Reading; public double? BatteryLevel => GetValue(InputType.Battery); public double? RSSILevel => GetValue(InputType.RSSI); public InputReading() : base(0u) { Reading = new Dictionary<string, InputReadingValue>(); } public InputReading(uint deviceIndex, uint featureIndex, uint id = 0u) : base(id, deviceIndex) { FeatureIndex = featureIndex; Reading = new Dictionary<string, InputReadingValue>(); } public double? GetValue(InputType type) { string key = type.ToString(); if (Reading != null && Reading.ContainsKey(key)) { return Reading[key].Value; } return null; } } } namespace Buttplug.Client { public class ButtplugClient : IDisposable, IAsyncDisposable { protected Timer _pingTimer; internal ButtplugClientMessageHandler _handler; private readonly ConcurrentDictionary<uint, ButtplugClientDevice> _devices = new ConcurrentDictionary<uint, ButtplugClientDevice>(); private IButtplugClientConnector _connector; private int _serverDisconnectRaised; public string Name { get; } public ButtplugClientDevice[] Devices => _devices.Values.ToArray(); public bool Connected => _connector?.Connected ?? false; public event EventHandler<DeviceAddedEventArgs> DeviceAdded; public event EventHandler<DeviceRemovedEventArgs> DeviceRemoved; public event EventHandler<ButtplugExceptionEventArgs> ErrorReceived; public event EventHandler ScanningFinished; public event EventHandler PingTimeout; public event EventHandler ServerDisconnect; public event EventHandler<InputReadingEventArgs> InputReadingReceived; public ButtplugClient(string clientName) { Name = clientName; } public async Task ConnectAsync(IButtplugClientConnector connector, CancellationToken token = default(CancellationToken)) { if (Connected) { throw new ButtplugHandshakeException("Client already connected to a server."); } ButtplugUtils.ArgumentNotNull(connector, "connector"); _connector = connector; _serverDisconnectRaised = 0; _connector.Disconnected += ConnectorDisconnectedHandler; _connector.InvalidMessageReceived += ConnectorErrorHandler; _connector.MessageReceived += MessageReceivedHandler; _devices.Clear(); _handler = new ButtplugClientMessageHandler(connector); await _connector.ConnectAsync(token).ConfigureAwait(continueOnCapturedContext: false); ButtplugMessage res = await _handler.SendMessageAsync(new RequestServerInfo(Name), token).ConfigureAwait(continueOnCapturedContext: false); if (!(res is ServerInfo si)) { if (res is Error e) { await DisconnectAsync().ConfigureAwait(continueOnCapturedContext: false); throw ButtplugException.FromError(e); } await DisconnectAsync().ConfigureAwait(continueOnCapturedContext: false); throw new ButtplugHandshakeException("Unrecognized message " + res.Name + " during handshake", res.Id); } if (si.MaxPingTime != 0) { _pingTimer?.Dispose(); _pingTimer = new Timer(OnPingTimer, null, 0, Convert.ToInt32(Math.Round((double)si.MaxPingTime / 2.0, 0))); } if (si.ProtocolVersionMajor < 4) { await DisconnectAsync().ConfigureAwait(continueOnCapturedContext: false); throw new ButtplugHandshakeException($"Buttplug Server's protocol version ({si.ProtocolVersionMajor}.{si.ProtocolVersionMinor}) is less than the client's ({4u}.{0u}). A newer server is required.", res.Id); } try { await RequestDeviceListAsync(token).ConfigureAwait(continueOnCapturedContext: false); } catch { await DisconnectAsync().ConfigureAwait(continueOnCapturedContext: false); throw; } } public async Task DisconnectAsync() { if (Connected) { _connector.MessageReceived -= MessageReceivedHandler; _connector.InvalidMessageReceived -= ConnectorErrorHandler; await _connector.DisconnectAsync().ConfigureAwait(continueOnCapturedContext: false); _connector.Disconnected -= ConnectorDisconnectedHandler; SignalServerDisconnect(this, EventArgs.Empty); } } private async Task RequestDeviceListAsync(CancellationToken token = default(CancellationToken)) { ButtplugMessage buttplugMessage = await _handler.SendMessageAsync(new RequestDeviceList(), token).ConfigureAwait(continueOnCapturedContext: false); if (buttplugMessage is DeviceList list) { ProcessDeviceList(list); return; } if (buttplugMessage is Error msg) { throw ButtplugException.FromError(msg); } throw new ButtplugMessageException("Received unknown response to DeviceList query"); } private void ProcessDeviceList(DeviceList list) { HashSet<uint> incomingDeviceIndices = new HashSet<uint>(); foreach (DeviceInfo allDevice in list.GetAllDevices()) { incomingDeviceIndices.Add(allDevice.DeviceIndex); if (!_devices.ContainsKey(allDevice.DeviceIndex)) { ButtplugClientDevice buttplugClientDevice = new ButtplugClientDevice(_handler, allDevice); _devices[allDevice.DeviceIndex] = buttplugClientDevice; this.DeviceAdded?.Invoke(this, new DeviceAddedEventArgs(buttplugClientDevice)); } } foreach (uint item in _devices.Keys.Where((uint idx) => !incomingDeviceIndices.Contains(idx)).ToList()) { if (_devices.TryRemove(item, out var value)) { this.DeviceRemoved?.Invoke(this, new DeviceRemovedEventArgs(value)); } } } public async Task StartScanningAsync(CancellationToken token = default(CancellationToken)) { EnsureConnected(); await _handler.SendMessageExpectOk(new StartScanning(), token).ConfigureAwait(continueOnCapturedContext: false); } public async Task StopScanningAsync(CancellationToken token = default(CancellationToken)) { EnsureConnected(); await _handler.SendMessageExpectOk(new StopScanning(), token).ConfigureAwait(continueOnCapturedContext: false); } public async Task StopAllDevicesAsync(CancellationToken token = default(CancellationToken)) { EnsureConnected(); await _handler.SendMessageExpectOk(new StopCmd(), token).ConfigureAwait(continueOnCapturedContext: false); } private void EnsureConnected() { if (!Connected) { throw new ButtplugClientConnectorException("Client not connected."); } } private void ConnectorErrorHandler(object sender, ButtplugExceptionEventArgs exception) { this.ErrorReceived?.Invoke(this, exception); } private void ConnectorDisconnectedHandler(object sender, EventArgs eventArgs) { SignalServerDisconnect(sender, eventArgs); } private void SignalServerDisconnect(object sender, EventArgs eventArgs) { if (Interlocked.Exchange(ref _serverDisconnectRaised, 1) == 0) { this.ServerDisconnect?.Invoke(sender, eventArgs); } } private async void MessageReceivedHandler(object sender, MessageReceivedEventArgs args) { ButtplugMessage message = args.Message; if (!(message is DeviceList list)) { if (!(message is ScanningFinished)) { if (!(message is InputReading reading)) { if (message is Error error) { this.ErrorReceived?.Invoke(this, new ButtplugExceptionEventArgs(ButtplugException.FromError(error))); if (error.ErrorCode == Error.ErrorClass.ERROR_PING) { this.PingTimeout?.Invoke(this, EventArgs.Empty); await DisconnectAsync().ConfigureAwait(continueOnCapturedContext: false); } } else { this.ErrorReceived?.Invoke(this, new ButtplugExceptionEventArgs(new ButtplugMessageException($"Got unhandled message: {message}", message.Id))); } } else { this.InputReadingReceived?.Invoke(this, new InputReadingEventArgs(reading)); } } else { try { await RequestDeviceListAsync().ConfigureAwait(continueOnCapturedContext: false); } catch (Exception ex) { this.ErrorReceived?.Invoke(this, new ButtplugExceptionEventArgs(new ButtplugMessageException("Failed to get device list after scanning: " + ex.Message))); } this.ScanningFinished?.Invoke(this, EventArgs.Empty); } } else { ProcessDeviceList(list); } } private async void OnPingTimer(object state) { try { await _handler.SendMessageExpectOk(new Ping()).ConfigureAwait(continueOnCapturedContext: false); } catch (Exception inner) { this.ErrorReceived?.Invoke(this, new ButtplugExceptionEventArgs(new ButtplugPingException("Exception thrown during ping update", 0u, inner))); await DisconnectAsync().ConfigureAwait(continueOnCapturedContext: false); } } protected virtual void Dispose(bool disposing) { _pingTimer?.Dispose(); DisconnectAsync().GetAwaiter().GetResult(); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } public async ValueTask DisposeAsync() { _pingTimer?.Dispose(); await DisconnectAsync().ConfigureAwait(continueOnCapturedContext: false); GC.SuppressFinalize(this); } } public class DeviceAddedEventArgs : EventArgs { public ButtplugClientDevice Device { get; } public DeviceAddedEventArgs(ButtplugClientDevice device) { Device = device; } } public class DeviceRemovedEventArgs : EventArgs { public ButtplugClientDevice Device { get; } public DeviceRemovedEventArgs(ButtplugClientDevice device) { Device = device; } } public class InputReadingEventArgs : EventArgs { public InputReading Reading { get; } public uint DeviceIndex => Reading.DeviceIndex; public uint FeatureIndex => Reading.FeatureIndex; public InputReadingEventArgs(InputReading reading) { Reading = reading; } } public class ButtplugClientConnectorException : ButtplugException { public ButtplugClientConnectorException(string message, Exception inner = null) : base(message, Error.ErrorClass.ERROR_UNKNOWN, 0u, inner) { } } public class ButtplugClientDevice { private readonly Dictionary<uint, ButtplugClientDeviceFeature> _features; private readonly ButtplugClientMessageHandler _handler; public uint Index { get; } public string Name { get; } public string DisplayName { get; } public uint MessageTimingGap { get; } public IReadOnlyDictionary<uint, ButtplugClientDeviceFeature> Features => _features; internal ButtplugClientDevice(ButtplugClientMessageHandler handler, IButtplugDeviceInfoMessage devInfo) : this(handler, devInfo.DeviceIndex, devInfo.DeviceName, devInfo.DeviceFeatures, devInfo.DeviceDisplayName, devInfo.DeviceMessageTimingGap) { ButtplugUtils.ArgumentNotNull(devInfo, "devInfo"); } internal ButtplugClientDevice(ButtplugClientMessageHandler handler, uint index, string name, Dictionary<string, DeviceFeature> deviceFeatures, string displayName, uint messageTimingGap) { ButtplugUtils.ArgumentNotNull(handler, "handler"); _handler = handler; Index = index; Name = name; DisplayName = displayName; MessageTimingGap = messageTimingGap; _features = new Dictionary<uint, ButtplugClientDeviceFeature>(); if (deviceFeatures == null) { return; } foreach (KeyValuePair<string, DeviceFeature> deviceFeature in deviceFeatures) { if (uint.TryParse(deviceFeature.Key, out var result)) { _features[result] = new ButtplugClientDeviceFeature(this, deviceFeature.Value, _handler); } } } public IEnumerable<ButtplugClientDeviceFeature> GetFeaturesWithOutput(OutputType outputType) { return _features.Values.Where((ButtplugClientDeviceFeature f) => f.HasOutput(outputType)); } public IEnumerable<ButtplugClientDeviceFeature> GetFeaturesWithInput(InputType inputType) { return _features.Values.Where((ButtplugClientDeviceFeature f) => f.HasInput(inputType)); } public ButtplugClientDeviceFeature GetFeature(uint featureIndex) { if (!_features.TryGetValue(featureIndex, out var value)) { return null; } return value; } public bool HasOutput(OutputType outputType) { return _features.Values.Any((ButtplugClientDeviceFeature f) => f.HasOutput(outputType)); } public bool HasInput(InputType inputType) { return _features.Values.Any((ButtplugClientDeviceFeature f) => f.HasInput(inputType)); } public async Task RunOutputAsync(DeviceOutputCommand command, CancellationToken token = default(CancellationToken)) { List<ButtplugClientDeviceFeature> source = GetFeaturesWithOutput(command.OutputType).ToList(); if (!source.Any()) { throw new ButtplugDeviceException($"Device {Name} has no features with output type {command.OutputType}"); } await Task.WhenAll(source.Select((ButtplugClientDeviceFeature f) => f.RunOutputAsync(command, token))).ConfigureAwait(continueOnCapturedContext: false); } public async Task RunOutputAsync(uint featureIndex, DeviceOutputCommand command, CancellationToken token = default(CancellationToken)) { await (GetFeature(featureIndex) ?? throw new ButtplugDeviceException($"Device {Name} does not have feature index {featureIndex}")).RunOutputAsync(command, token).ConfigureAwait(continueOnCapturedContext: false); } public async Task<InputReading> RunInputAsync(DeviceInputCommand command, CancellationToken token = default(CancellationToken)) { List<ButtplugClientDeviceFeature> source = GetFeaturesWithInput(command.InputType).ToList(); if (!source.Any()) { throw new ButtplugDeviceException($"Device {Name} has no features with input type {command.InputType}"); } return await source.First().RunInputAsync(command, token).ConfigureAwait(continueOnCapturedContext: false); } public async Task<InputReading> RunInputAsync(uint featureIndex, DeviceInputCommand command, CancellationToken token = default(CancellationToken)) { return await (GetFeature(featureIndex) ?? throw new ButtplugDeviceException($"Device {Name} does not have feature index {featureIndex}")).RunInputAsync(command, token).ConfigureAwait(continueOnCapturedContext: false); } public async Task<double> BatteryAsync(TimeSpan? timeout = null, CancellationToken token = default(CancellationToken)) { CancellationTokenSource linkedCts = null; try { CancellationToken token2 = token; if (timeout.HasValue) { linkedCts = CancellationTokenSource.CreateLinkedTokenSource(new CancellationToken[1] { token }); linkedCts.CancelAfter(timeout.Value); token2 = linkedCts.Token; } double? num = (await RunInputAsync(DeviceInput.Battery.Read(), token2).ConfigureAwait(continueOnCapturedContext: false))?.GetValue(InputType.Battery); if (!num.HasValue) { throw new ButtplugMessageException("Battery reading from device '" + Name + "' did not contain a battery value."); } return num.Value / 100.0; } finally { linkedCts?.Dispose(); } } public async Task StopAsync(CancellationToken token = default(CancellationToken)) { await _handler.SendMessageExpectOk(new StopCmd(1u, Index), token).ConfigureAwait(continueOnCapturedContext: false); } } public class PercentOrSteps { private readonly double? _percent; private readonly int? _steps; public double? Percent => _percent; public int? Steps => _steps; private PercentOrSteps(double? percent, int? steps) { _percent = percent; _steps = steps; } public static PercentOrSteps FromSteps(int steps) { return new PercentOrSteps(null, steps); } public static PercentOrSteps FromPercent(double percent, double minPercent = 0.0, double maxPercent = 1.0) { if (percent < minPercent || percent > maxPercent) { throw new ButtplugDeviceException($"Percent value {percent} is not in the range {minPercent} <= x <= {maxPercent}"); } return new PercentOrSteps(percent, null); } public int ToStepValue(int maxSteps) { if (_steps.HasValue) { return _steps.Value; } return (int)Math.Ceiling(_percent.Value * (double)maxSteps); } } public class DeviceOutputCommand { public OutputType OutputType { get; } public PercentOrSteps Value { get; } public uint? Duration { get; } public DeviceOutputCommand(OutputType outputType, PercentOrSteps value, uint? duration = null) { OutputType = outputType; Value = value; Duration = duration; } } public class DeviceOutputValueBuilder { private readonly OutputType _outputType; internal DeviceOutputValueBuilder(OutputType outputType) { _outputType = outputType; } public DeviceOutputCommand Steps(int steps) { return new DeviceOutputCommand(_outputType, PercentOrSteps.FromSteps(steps)); } public DeviceOutputCommand Percent(double percent) { double minPercent = ((_outputType == OutputType.Rotate) ? (-1.0) : 0.0); return new DeviceOutputCommand(_outputType, PercentOrSteps.FromPercent(percent, minPercent)); } } public class DeviceOutputPositionWithDurationBuilder { internal DeviceOutputPositionWithDurationBuilder() { } public DeviceOutputCommand Steps(int steps, uint durationMs) { return new DeviceOutputCommand(OutputType.HwPositionWithDuration, PercentOrSteps.FromSteps(steps), durationMs); } public DeviceOutputCommand Percent(double percent, uint durationMs) { return new DeviceOutputCommand(OutputType.HwPositionWithDuration, PercentOrSteps.FromPercent(percent), durationMs); } } public static class DeviceOutput { public static DeviceOutputValueBuilder Vibrate => new DeviceOutputValueBuilder(OutputType.Vibrate); public static DeviceOutputValueBuilder Rotate => new DeviceOutputValueBuilder(OutputType.Rotate); public static DeviceOutputValueBuilder Oscillate => new DeviceOutputValueBuilder(OutputType.Oscillate); public static DeviceOutputValueBuilder Constrict => new DeviceOutputValueBuilder(OutputType.Constrict); public static DeviceOutputValueBuilder Temperature => new DeviceOutputValueBuilder(OutputType.Temperature); public static DeviceOutputValueBuilder Led => new DeviceOutputValueBuilder(OutputType.Led); public static DeviceOutputValueBuilder Spray => new DeviceOutputValueBuilder(OutputType.Spray); public static DeviceOutputValueBuilder Position => new DeviceOutputValueBuilder(OutputType.Position); public static DeviceOutputPositionWithDurationBuilder PositionWithDuration => new DeviceOutputPositionWithDurationBuilder(); } public class DeviceInputCommand { public InputType InputType { get; } public InputCommandType CommandType { get; } public DeviceInputCommand(InputType inputType, InputCommandType commandType) { InputType = inputType; CommandType = commandType; } } public class DeviceInputBuilder { private readonly InputType _inputType; internal DeviceInputBuilder(InputType inputType) { _inputType = inputType; } public DeviceInputCommand Read() { return new DeviceInputCommand(_inputType, InputCommandType.Read); } public DeviceInputCommand Subscribe() { return new DeviceInputCommand(_inputType, InputCommandType.Subscribe); } public DeviceInputCommand Unsubscribe() { return new DeviceInputCommand(_inputType, InputCommandType.Unsubscribe); } } public static class DeviceInput { public static DeviceInputBuilder Battery => new DeviceInputBuilder(InputType.Battery); public static DeviceInputBuilder RSSI => new DeviceInputBuilder(InputType.RSSI); public static DeviceInputBuilder Button => new DeviceInputBuilder(InputType.Button); public static DeviceInputBuilder Pressure => new DeviceInputBuilder(InputType.Pressure); public static DeviceInputBuilder Depth => new DeviceInputBuilder(InputType.Depth); public static DeviceInputBuilder Position => new DeviceInputBuilder(InputType.Position); } public class ButtplugClientDeviceFeature { private readonly ButtplugClientMessageHandler _handler; public ButtplugClientDevice Device { get; } public uint FeatureIndex { get; } public string FeatureDescription { get; } public DeviceFeature FeatureDefinition { get; } internal ButtplugClientDeviceFeature(ButtplugClientDevice device, DeviceFeature feature, ButtplugClientMessageHandler handler) { Device = device; FeatureIndex = feature.FeatureIndex; FeatureDescription = feature.FeatureDescription; FeatureDefinition = feature; _handler = handler; } public bool HasOutput(OutputType outputType) { return FeatureDefinition.HasOutput(outputType); } public bool HasInput(InputType inputType) { return FeatureDefinition.HasInput(inputType); } public bool TryGetOutputRange(OutputType outputType, out int min, out int max) { DeviceFeatureOutput output = FeatureDefinition.GetOutput(outputType); if (output?.Value != null && output.Value.Length >= 2) { min = output.Value[0]; max = output.Value[1]; return true; } min = 0; max = 0; return false; } public async Task RunOutputAsync(DeviceOutputCommand command, CancellationToken token = default(CancellationToken)) { if (!HasOutput(command.OutputType)) { IEnumerable<string> enumerable = FeatureDefinition.Output?.Keys; IEnumerable<string> enumerable2 = enumerable ?? Enumerable.Empty<string>(); string text = (enumerable2.Any() ? string.Join(", ", enumerable2) : "none"); throw new ButtplugDeviceException($"Feature {FeatureIndex} ({FeatureDescription}) on device '{Device.Name}' does not support output type '{command.OutputType}'. " + "Supported outputs: " + text + "."); } DeviceFeatureOutput output = FeatureDefinition.GetOutput(command.OutputType); int maxSteps = 100; if (output?.Value != null && output.Value.Length >= 2) { maxSteps = output.Value[1]; } int num = command.Value.ToStepValue(maxSteps); OutputCmd msg; if (command.OutputType == OutputType.HwPositionWithDuration) { if (!command.Duration.HasValue) { throw new ButtplugDeviceException($"HwPositionWithDuration command for feature {FeatureIndex} ({FeatureDescription}) on device '{Device.Name}' requires a duration value. " + "Use DeviceOutput.PositionWithDuration.Percent(position, durationMs) or DeviceOutput.PositionWithDuration.Steps(steps, durationMs)."); } msg = OutputCmd.CreatePositionWithDuration(Device.Index, FeatureIndex, num, command.Duration.Value); } else { msg = OutputCmd.Create(Device.Index, FeatureIndex, command.OutputType, num); } await _handler.SendMessageExpectOk(msg, token).ConfigureAwait(continueOnCapturedContext: false); } public async Task<InputReading> RunInputAsync(DeviceInputCommand command, CancellationToken token = default(CancellationToken)) { if (!HasInput(command.InputType)) { IEnumerable<string> enumerable = FeatureDefinition.Input?.Keys; IEnumerable<string> enumerable2 = enumerable ?? Enumerable.Empty<string>(); string text = (enumerable2.Any() ? string.Join(", ", enumerable2) : "none"); throw new ButtplugDeviceException($"Feature {FeatureIndex} ({FeatureDescription}) on device '{Device.Name}' does not support input type '{command.InputType}'. " + "Supported inputs: " + text + "."); } InputCmd msg = new InputCmd(Device.Index, FeatureIndex, command.InputType, command.CommandType); if (command.CommandType == InputCommandType.Read) { ButtplugMessage buttplugMessage = await _handler.SendMessageAsync(msg, token).ConfigureAwait(continueOnCapturedContext: false); if (!(buttplugMessage is InputReading result)) { if (buttplugMessage is Error msg2) { throw ButtplugException.FromError(msg2); } throw new ButtplugMessageException("Message type " + buttplugMessage.Name + " not handled by RunInputAsync", buttplugMessage.Id); } return result; } await _handler.SendMessageExpectOk(msg, token).ConfigureAwait(continueOnCapturedContext: false); return null; } } public static class ButtplugClientExtensions { public static Task ConnectAsync(this ButtplugClient client, string uri, CancellationToken token = default(CancellationToken)) { return client.ConnectAsync(new Uri(uri), token); } public static Task ConnectAsync(this ButtplugClient client, Uri uri, CancellationToken token = default(CancellationToken)) { return client.ConnectAsync(new ButtplugWebsocketConnector(uri), token); } public static ButtplugClientDevice GetDevice(this ButtplugClient client, uint index) { return client.Devices.FirstOrDefault((ButtplugClientDevice d) => d.Index == index); } public static IEnumerable<ButtplugClientDevice> GetDevicesWithOutput(this ButtplugClient client, OutputType outputType) { return client.Devices.Where((ButtplugClientDevice d) => d.HasOutput(outputType)); } public static IEnumerable<ButtplugClientDevice> GetDevicesWithInput(this ButtplugClient client, InputType inputType) { return client.Devices.Where((ButtplugClientDevice d) => d.HasInput(inputType)); } } internal class ButtplugClientMessageHandler { private IButtplugClientConnector _connector; internal ButtplugClientMessageHandler(IButtplugClientConnector connector) { _connector = connector; } public async Task<ButtplugMessage> SendMessageAsync(ButtplugMessage msg, CancellationToken token = default(CancellationToken)) { if (!_connector.Connected) { throw new ButtplugClientConnectorException("Client not connected."); } return await _connector.SendAsync(msg, token).ConfigureAwait(continueOnCapturedContext: false); } public async Task SendMessageExpectOk(ButtplugMessage msg, CancellationToken token = default(CancellationToken)) { ButtplugMessage buttplugMessage = await SendMessageAsync(msg, token).ConfigureAwait(continueOnCapturedContext: false); if (!(buttplugMessage is Ok)) { if (buttplugMessage is Error msg2) { throw ButtplugException.FromError(msg2); } throw new ButtplugMessageException("Message type " + msg.Name + " not handled by SendMessageExpectOk", msg.Id); } } } public class ButtplugConnectorJSONParser { private readonly ButtplugJsonMessageParser _parser = new ButtplugJsonMessageParser(); public string Serialize(ButtplugMessage msg) { return _parser.Serialize(msg); } public string Serialize(ButtplugMessage[] msgs) { return _parser.Serialize(msgs); } public IEnumerable<ButtplugMessage> Deserialize(string msg) { return _parser.Deserialize(msg); } } public class ButtplugConnectorMessageSorter : IDisposable { private class WaitingMessage : IDisposable { public TaskCompletionSource<ButtplugMessage> Promise { get; } public CancellationTokenRegistration Cancellation { get; set; } public WaitingMessage(TaskCompletionSource<ButtplugMessage> promise) { Promise = promise; } public void Dispose() { Cancellation.Dispose(); } } private int _counter; private readonly ConcurrentDictionary<uint, WaitingMessage> _waitingMsgs = new ConcurrentDictionary<uint, WaitingMessage>(); public uint NextMsgId => Convert.ToUInt32(Interlocked.Increment(ref _counter)); public Task<ButtplugMessage> PrepareMessage(ButtplugMessage msg, CancellationToken token = default(CancellationToken)) { msg.Id = NextMsgId; WaitingMessage waitingMessage = new WaitingMessage(new TaskCompletionSource<ButtplugMessage>()); _waitingMsgs.TryAdd(msg.Id, waitingMessage); if (token.CanBeCanceled) { waitingMessage.Cancellation = token.Register(delegate { if (_waitingMsgs.TryRemove(msg.Id, out var value)) { value.Promise.TrySetCanceled(); } }); } return waitingMessage.Promise.Task; } public void CheckMessage(ButtplugMessage msg) { if (msg.Id == 0) { throw new ButtplugMessageException("Cannot sort message with System ID", msg.Id); } if (!_waitingMsgs.TryRemove(msg.Id, out var value)) { throw new ButtplugMessageException("Message with non-matching ID received.", msg.Id); } value.Dispose(); if (msg is Error msg2) { value.Promise.SetException(ButtplugException.FromError(msg2)); } else { value.Promise.SetResult(msg); } } protected virtual void Dispose(bool disposing) { foreach (KeyValuePair<uint, WaitingMessage> waitingMsg in _waitingMsgs) { if (_waitingMsgs.TryRemove(waitingMsg.Key, out var value)) { value.Dispose(); value.Promise.TrySetException(new Exception("Sorter has been destroyed with live tasks still in queue.")); } } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } public class ReturnMessage { public readonly string Message; public readonly Task<ButtplugMessage> Promise; public ReturnMessage(string message, Task<ButtplugMessage> promise) { Message = message; Promise = promise; } } public class ButtplugRemoteJSONConnector : IDisposable { private readonly ButtplugConnectorJSONParser _jsonSerializer = new ButtplugConnectorJSONParser(); private readonly ButtplugConnectorMessageSorter _msgSorter = new ButtplugConnectorMessageSorter(); public event EventHandler<MessageReceivedEventArgs> MessageReceived; public event EventHandler<ButtplugExceptionEventArgs> InvalidMessageReceived; public ReturnMessage PrepareMessage(ButtplugMessage msg, CancellationToken token = default(CancellationToken)) { Task<ButtplugMessage> promise = _msgSorter.PrepareMessage(msg, token); return new ReturnMessage(_jsonSerializer.Serialize(msg), promise); } protected void ReceiveMessages(string jSONMsg) { IEnumerable<ButtplugMessage> enumerable; try { enumerable = _jsonSerializer.Deserialize(jSONMsg); } catch (ButtplugMessageException ex) { this.InvalidMessageReceived?.Invoke(this, new ButtplugExceptionEventArgs(ex)); return; } foreach (ButtplugMessage item in enumerable) { if (item.Id == 0) { this.MessageReceived?.Invoke(this, new MessageReceivedEventArgs(item)); continue; } try { _msgSorter.CheckMessage(item); } catch (ButtplugMessageException ex2) { this.InvalidMessageReceived?.Invoke(this, new ButtplugExceptionEventArgs(ex2)); } } } protected virtual void Dispose(bool disposing) { _msgSorter.Dispose(); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } public class ButtplugWebsocketConnector : ButtplugRemoteJSONConnector, IButtplugClientConnector { private ClientWebSocket _wsClient; private readonly SynchronizationContext _owningDispatcher = SynchronizationContext.Current ?? new SynchronizationContext(); private readonly Uri _uri; private Task _readTask; private CancellationTokenSource _readLoopCancellationToken; public bool Connected { get { ClientWebSocket wsClient = _wsClient; if (wsClient == null) { return false; } return wsClient.State == WebSocketState.Open; } } public event EventHandler Disconnected; public ButtplugWebsocketConnector(Uri uri) { _uri = uri; } public async Task ConnectAsync(CancellationToken token = default(CancellationToken)) { if (_wsClient != null) { throw new ButtplugHandshakeException("Websocket connector is already connected."); } try { _wsClient = new ClientWebSocket(); await _wsClient.ConnectAsync(_uri, token).ConfigureAwait(continueOnCapturedContext: false); } catch (Exception inner) { throw new ButtplugClientConnectorException("Websocket Connection Exception! See Inner Exception", inner); } _readLoopCancellationToken?.Dispose(); _readLoopCancellationToken = new CancellationTokenSource(); _readTask = Task.Run(async delegate { await RunClientLoop(_readLoopCancellationToken.Token).ConfigureAwait(continueOnCapturedContext: false); }); } public async Task DisconnectAsync(CancellationToken token = default(CancellationToken)) { _readLoopCancellationToken?.Cancel(); if (_wsClient != null && (_wsClient.State == WebSocketState.Connecting || _wsClient.State == WebSocketState.Open)) { await _wsClient.CloseAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None).ConfigureAwait(continueOnCapturedContext: false); } _wsClient?.Dispose(); _wsClient = null; if (_readTask != null) { await _readTask.ConfigureAwait(continueOnCapturedContext: false); } _readLoopCancellationToken?.Dispose(); _readLoopCancellationToken = null; } public async Task<ButtplugMessage> SendAsync(ButtplugMessage msg, CancellationToken cancellationToken) { if (_wsClient == null) { throw new ButtplugException("Cannot send messages while disconnected", Error.ErrorClass.ERROR_MSG); } ReturnMessage returnMsg = PrepareMessage(msg, cancellationToken); await _wsClient.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes(returnMsg.Message)), WebSocketMessageType.Text, endOfMessage: true, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); return await returnMsg.Promise.ConfigureAwait(continueOnCapturedContext: false); } private async Task RunClientLoop(CancellationToken token) { try { new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: false); byte[] buff = new byte[2048]; while (Connected && !token.IsCancellationRequested) { WebSocketReceiveResult webSocketReceiveResult = await _wsClient.ReceiveAsync(new ArraySegment<byte>(buff), token).ConfigureAwait(continueOnCapturedContext: false); if (webSocketReceiveResult.MessageType == WebSocketMessageType.Text) { string jSONMsg = Encoding.Default.GetString(buff, 0, webSocketReceiveResult.Count); ReceiveMessages(jSONMsg); } } } catch (Exception) { } finally { if (_wsClient != null) { _wsClient.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None).Dispose(); _wsClient = null; } _owningDispatcher.Send(delegate { Dispose(); }, null); _owningDispatcher.Send(delegate { this.Disconnected?.Invoke(this, EventArgs.Empty); }, null); } } } public interface IButtplugClientConnector { bool Connected { get; } event EventHandler<MessageReceivedEventArgs> MessageReceived; event EventHandler<ButtplugExceptionEventArgs> InvalidMessageReceived; event EventHandler Disconnected; Task ConnectAsync(CancellationToken token = default(CancellationToken)); Task DisconnectAsync(CancellationToken token = default(CancellationToken)); Task<ButtplugMessage> SendAsync(ButtplugMessage msg, CancellationToken token = default(CancellationToken)); } }
BepInEx\plugins\PeakIntiface\PeakIntiface.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Buttplug.Client; using Buttplug.Core.Messages; using PeakIntiface.Buttplug; using PeakIntiface.Toy; using PeakIntiface.Triggers; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("PeakIntiface")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+0b15eeb7ef8e55e8e05ba857bf82664946ae5b27")] [assembly: AssemblyProduct("PeakIntiface")] [assembly: AssemblyTitle("PeakIntiface")] [assembly: AssemblyVersion("1.0.0.0")] namespace PeakIntiface { internal class ConfigManager { private readonly ConfigFile config; public static ConfigEntry<bool> Enabled; public static ConfigEntry<string> ServerIP; public static ConfigEntry<int> ServerPort; public static ConfigEntry<float> MaximumIntensity; public static ConfigEntry<float> MaximumRotate; public static ConfigEntry<float> MaximumConstrict; public static ConfigEntry<bool> EmergencyStopEnabled; public static ConfigEntry<KeyCode> EmergencyStopKey; public static ConfigEntry<float> ConstantVibration; public static ConfigEntry<bool> UseCarried; public static ConfigEntry<bool> UseSpectate; public static ConfigEntry<bool> InjuryTriggerEnabled; public static ConfigEntry<float> InjuryTriggerMaximumIntensity; public static ConfigEntry<float> InjuryTriggerMinimumIntensity; public static ConfigEntry<float> InjuryTriggerDuration; public static ConfigEntry<bool> ColdTriggerEnabled; public static ConfigEntry<float> ColdTriggerMaximumIntensity; public static ConfigEntry<float> ColdTriggerMinimumIntensity; public static ConfigEntry<float> ColdTriggerDuration; public static ConfigEntry<bool> PoisonTriggerEnabled; public static ConfigEntry<float> PoisonTriggerMaximumIntensity; public static ConfigEntry<float> PoisonTriggerMinimumIntensity; public static ConfigEntry<float> PoisonTriggerDuration; public static ConfigEntry<bool> CurseTriggerEnabled; public static ConfigEntry<float> CurseTriggerMaximumIntensity; public static ConfigEntry<float> CurseTriggerMinimumIntensity; public static ConfigEntry<float> CurseTriggerDuration; public static ConfigEntry<bool> DrowsyTriggerEnabled; public static ConfigEntry<float> DrowsyTriggerMaximumIntensity; public static ConfigEntry<float> DrowsyTriggerMinimumIntensity; public static ConfigEntry<float> DrowsyTriggerDuration; public static ConfigEntry<bool> HotTriggerEnabled; public static ConfigEntry<float> HotTriggerMaximumIntensity; public static ConfigEntry<float> HotTriggerMinimumIntensity; public static ConfigEntry<float> HotTriggerDuration; public static ConfigEntry<bool> ThornsTriggerEnabled; public static ConfigEntry<float> ThornsTriggerMaximumIntensity; public static ConfigEntry<float> ThornsTriggerMinimumIntensity; public static ConfigEntry<float> ThornsTriggerDuration; public static ConfigEntry<bool> SporesTriggerEnabled; public static ConfigEntry<float> SporesTriggerMaximumIntensity; public static ConfigEntry<float> SporesTriggerMinimumIntensity; public static ConfigEntry<float> SporesTriggerDuration; public static ConfigEntry<bool> WebTriggerEnabled; public static ConfigEntry<float> WebTriggerMaximumIntensity; public static ConfigEntry<float> WebTriggerMinimumIntensity; public static ConfigEntry<float> WebTriggerDuration; public static ConfigEntry<bool> ArrowTriggerEnabled; public static ConfigEntry<float> ArrowTriggerMaximumIntensity; public static ConfigEntry<float> ArrowTriggerMinimumIntensity; public static ConfigEntry<float> ArrowTriggerDuration; public static ConfigEntry<bool> PetrifyTriggerEnabled; public static ConfigEntry<float> PetrifyTriggerMaximumIntensity; public static ConfigEntry<float> PetrifyTriggerMinimumIntensity; public static ConfigEntry<float> PetrifyTriggerDuration; public static ConfigEntry<bool> DeadEnabled; public static ConfigEntry<float> DeadIntensity; public static ConfigEntry<float> DeadTime; public static ConfigEntry<bool> PassedOutEnabled; public static ConfigEntry<float> PassedOutIntensity; public static ConfigEntry<float> PassedOutTime; public static ConfigEntry<bool> RagdollEnabled; public static ConfigEntry<float> RagdollIntensity; public static ConfigEntry<float> RagdollTime; public ConfigManager(ConfigFile config) { this.config = config; LoadConfig(); } private void LoadConfig() { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected O, but got Unknown //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Expected O, but got Unknown //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Expected O, but got Unknown //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Expected O, but got Unknown //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Expected O, but got Unknown //IL_01fa: Expected O, but got Unknown //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Expected O, but got Unknown //IL_022a: Expected O, but got Unknown //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_0287: Expected O, but got Unknown //IL_02ba: Unknown result type (might be due to invalid IL or missing references) //IL_02c4: Expected O, but got Unknown //IL_02f7: Unknown result type (might be due to invalid IL or missing references) //IL_0301: Expected O, but got Unknown //IL_0354: Unknown result type (might be due to invalid IL or missing references) //IL_035e: Expected O, but got Unknown //IL_0391: Unknown result type (might be due to invalid IL or missing references) //IL_039b: Expected O, but got Unknown //IL_03ce: Unknown result type (might be due to invalid IL or missing references) //IL_03d8: Expected O, but got Unknown //IL_042b: Unknown result type (might be due to invalid IL or missing references) //IL_0435: Expected O, but got Unknown //IL_0468: Unknown result type (might be due to invalid IL or missing references) //IL_0472: Expected O, but got Unknown //IL_04a5: Unknown result type (might be due to invalid IL or missing references) //IL_04af: Expected O, but got Unknown //IL_0502: Unknown result type (might be due to invalid IL or missing references) //IL_050c: Expected O, but got Unknown //IL_053f: Unknown result type (might be due to invalid IL or missing references) //IL_0549: Expected O, but got Unknown //IL_057c: Unknown result type (might be due to invalid IL or missing references) //IL_0586: Expected O, but got Unknown //IL_05d9: Unknown result type (might be due to invalid IL or missing references) //IL_05e3: Expected O, but got Unknown //IL_0616: Unknown result type (might be due to invalid IL or missing references) //IL_0620: Expected O, but got Unknown //IL_0653: Unknown result type (might be due to invalid IL or missing references) //IL_065d: Expected O, but got Unknown //IL_06b0: Unknown result type (might be due to invalid IL or missing references) //IL_06ba: Expected O, but got Unknown //IL_06ed: Unknown result type (might be due to invalid IL or missing references) //IL_06f7: Expected O, but got Unknown //IL_072a: Unknown result type (might be due to invalid IL or missing references) //IL_0734: Expected O, but got Unknown //IL_0787: Unknown result type (might be due to invalid IL or missing references) //IL_0791: Expected O, but got Unknown //IL_07c4: Unknown result type (might be due to invalid IL or missing references) //IL_07ce: Expected O, but got Unknown //IL_0801: Unknown result type (might be due to invalid IL or missing references) //IL_080b: Expected O, but got Unknown //IL_085e: Unknown result type (might be due to invalid IL or missing references) //IL_0868: Expected O, but got Unknown //IL_089b: Unknown result type (might be due to invalid IL or missing references) //IL_08a5: Expected O, but got Unknown //IL_08d8: Unknown result type (might be due to invalid IL or missing references) //IL_08e2: Expected O, but got Unknown //IL_0935: Unknown result type (might be due to invalid IL or missing references) //IL_093f: Expected O, but got Unknown //IL_0972: Unknown result type (might be due to invalid IL or missing references) //IL_097c: Expected O, but got Unknown //IL_09af: Unknown result type (might be due to invalid IL or missing references) //IL_09b9: Expected O, but got Unknown //IL_0a0c: Unknown result type (might be due to invalid IL or missing references) //IL_0a16: Expected O, but got Unknown //IL_0a49: Unknown result type (might be due to invalid IL or missing references) //IL_0a53: Expected O, but got Unknown //IL_0a86: Unknown result type (might be due to invalid IL or missing references) //IL_0a90: Expected O, but got Unknown //IL_0ae3: Unknown result type (might be due to invalid IL or missing references) //IL_0aed: Expected O, but got Unknown //IL_0b20: Unknown result type (might be due to invalid IL or missing references) //IL_0b2a: Expected O, but got Unknown //IL_0b5d: Unknown result type (might be due to invalid IL or missing references) //IL_0b67: Expected O, but got Unknown //IL_0b7c: Unknown result type (might be due to invalid IL or missing references) //IL_0b8d: Unknown result type (might be due to invalid IL or missing references) //IL_0b97: Expected O, but got Unknown //IL_0b97: Expected O, but got Unknown //IL_0bac: Unknown result type (might be due to invalid IL or missing references) //IL_0bcf: Unknown result type (might be due to invalid IL or missing references) //IL_0bd9: Expected O, but got Unknown //IL_0bd9: Expected O, but got Unknown //IL_0bee: Unknown result type (might be due to invalid IL or missing references) //IL_0c11: Unknown result type (might be due to invalid IL or missing references) //IL_0c1b: Expected O, but got Unknown //IL_0c1b: Expected O, but got Unknown //IL_0c30: Unknown result type (might be due to invalid IL or missing references) //IL_0c41: Unknown result type (might be due to invalid IL or missing references) //IL_0c4b: Expected O, but got Unknown //IL_0c4b: Expected O, but got Unknown //IL_0c60: Unknown result type (might be due to invalid IL or missing references) //IL_0c83: Unknown result type (might be due to invalid IL or missing references) //IL_0c8d: Expected O, but got Unknown //IL_0c8d: Expected O, but got Unknown //IL_0ca2: Unknown result type (might be due to invalid IL or missing references) //IL_0cc5: Unknown result type (might be due to invalid IL or missing references) //IL_0ccf: Expected O, but got Unknown //IL_0ccf: Expected O, but got Unknown //IL_0ce4: Unknown result type (might be due to invalid IL or missing references) //IL_0cf5: Unknown result type (might be due to invalid IL or missing references) //IL_0cff: Expected O, but got Unknown //IL_0cff: Expected O, but got Unknown //IL_0d14: Unknown result type (might be due to invalid IL or missing references) //IL_0d37: Unknown result type (might be due to invalid IL or missing references) //IL_0d41: Expected O, but got Unknown //IL_0d41: Expected O, but got Unknown //IL_0d56: Unknown result type (might be due to invalid IL or missing references) //IL_0d79: Unknown result type (might be due to invalid IL or missing references) //IL_0d83: Expected O, but got Unknown //IL_0d83: Expected O, but got Unknown Enabled = config.Bind<bool>("General", "Enabled", true, "Enable or Disable toy control"); Enabled.SettingChanged += EnabledSettingChanged; MaximumIntensity = config.Bind<float>("General", "Maximum Intensity", 0.7f, new ConfigDescription("Maximumtoy intensity from 0.0 to 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); MaximumRotate = config.Bind<float>("General", "Maximum Rotate", 0.3f, new ConfigDescription("Maximum toy rotate intensity from 0.0 to 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); MaximumConstrict = config.Bind<float>("General", "Maximum Constrict", 0.3f, new ConfigDescription("Maximum toy constrict intensity from 0.0 to 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); ServerIP = config.Bind<string>("General", "Server IP", "127.0.0.1", "IP address of the Intiface Server"); ServerPort = config.Bind<int>("General", "Server Port", 12345, "Port of the Intiface Server"); EmergencyStopEnabled = config.Bind<bool>("General", "Emergency Stop Enabled", true, "Enable or Disable Emergency Stop"); EmergencyStopKey = config.Bind<KeyCode>("General", "Emergency Stop Key", (KeyCode)291, "Key to trigger Emergency Stop"); ConstantVibration = config.Bind<float>("General", "Constant Vibration", 0f, new ConfigDescription("Constant vibration intensity from 0.0 to 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); ConstantVibration.SettingChanged += ConstantVibrationSettingChanged; UseCarried = config.Bind<bool>(new ConfigDefinition("Player State", "Use Carried"), true, new ConfigDescription("Use the player who is carring you for triggers", (AcceptableValueBase)null, Array.Empty<object>())); UseSpectate = config.Bind<bool>(new ConfigDefinition("Player State", "Use Spectate"), true, new ConfigDescription("Use the player who you are Spectating for triggers", (AcceptableValueBase)null, Array.Empty<object>())); InjuryTriggerEnabled = config.Bind<bool>("Triggers", "Injury Trigger Enabled", true, "Enable or Disable Injury Trigger"); InjuryTriggerMaximumIntensity = config.Bind<float>("Triggers", "Injury Trigger Maximum Intensity", 0.5f, new ConfigDescription("Maximum intensity for Injury Trigger from 0.0 to 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); InjuryTriggerMinimumIntensity = config.Bind<float>("Triggers", "Injury Trigger Minimum Intensity", 0.1f, new ConfigDescription("Minimum intensity for Injury Trigger from 0.0 to 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); InjuryTriggerDuration = config.Bind<float>("Triggers", "Injury Trigger Duration", 5000f, new ConfigDescription("Duration for Injury Trigger in milliseconds", (AcceptableValueBase)(object)new AcceptableValueRange<float>(100f, 10000f), Array.Empty<object>())); ColdTriggerEnabled = config.Bind<bool>("Triggers", "Cold Trigger Enabled", true, "Enable or Disable Cold Trigger"); ColdTriggerMaximumIntensity = config.Bind<float>("Triggers", "Cold Trigger Maximum Intensity", 0.5f, new ConfigDescription("Maximum intensity for Cold Trigger from 0.0 to 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); ColdTriggerMinimumIntensity = config.Bind<float>("Triggers", "Cold Trigger Minimum Intensity", 0.1f, new ConfigDescription("Minimum intensity for Cold Trigger from 0.0 to 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); ColdTriggerDuration = config.Bind<float>("Triggers", "Cold Trigger Duration", 1000f, new ConfigDescription("Duration for Cold Trigger in milliseconds", (AcceptableValueBase)(object)new AcceptableValueRange<float>(100f, 10000f), Array.Empty<object>())); PoisonTriggerEnabled = config.Bind<bool>("Triggers", "Poison Trigger Enabled", true, "Enable or Disable Poison Trigger"); PoisonTriggerMaximumIntensity = config.Bind<float>("Triggers", "Poison Trigger Maximum Intensity", 0.5f, new ConfigDescription("Maximum intensity for Poison Trigger from 0.0 to 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); PoisonTriggerMinimumIntensity = config.Bind<float>("Triggers", "Poison Trigger Minimum Intensity", 0.1f, new ConfigDescription("Minimum intensity for Poison Trigger from 0.0 to 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); PoisonTriggerDuration = config.Bind<float>("Triggers", "Poison Trigger Duration", 3000f, new ConfigDescription("Duration for Poison Trigger in milliseconds", (AcceptableValueBase)(object)new AcceptableValueRange<float>(100f, 10000f), Array.Empty<object>())); CurseTriggerEnabled = config.Bind<bool>("Triggers", "Curse Trigger Enabled", true, "Enable or Disable Curse Trigger"); CurseTriggerMaximumIntensity = config.Bind<float>("Triggers", "Curse Trigger Maximum Intensity", 0.5f, new ConfigDescription("Maximum intensity for Curse Trigger from 0.0 to 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); CurseTriggerMinimumIntensity = config.Bind<float>("Triggers", "Curse Trigger Minimum Intensity", 0.1f, new ConfigDescription("Minimum intensity for Curse Trigger from 0.0 to 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); CurseTriggerDuration = config.Bind<float>("Triggers", "Curse Trigger Duration", 5000f, new ConfigDescription("Duration for Curse Trigger in milliseconds", (AcceptableValueBase)(object)new AcceptableValueRange<float>(100f, 10000f), Array.Empty<object>())); DrowsyTriggerEnabled = config.Bind<bool>("Triggers", "Drowsy Trigger Enabled", true, "Enable or Disable Drowsy Trigger"); DrowsyTriggerMaximumIntensity = config.Bind<float>("Triggers", "Drowsy Trigger Maximum Intensity", 0.5f, new ConfigDescription("Maximum intensity for Drowsy Trigger from 0.0 to 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); DrowsyTriggerMinimumIntensity = config.Bind<float>("Triggers", "Drowsy Trigger Minimum Intensity", 0.1f, new ConfigDescription("Minimum intensity for Drowsy Trigger from 0.0 to 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); DrowsyTriggerDuration = config.Bind<float>("Triggers", "Drowsy Trigger Duration", 1000f, new ConfigDescription("Duration for Drowsy Trigger in milliseconds", (AcceptableValueBase)(object)new AcceptableValueRange<float>(100f, 10000f), Array.Empty<object>())); HotTriggerEnabled = config.Bind<bool>("Triggers", "Hot Trigger Enabled", true, "Enable or Disable Hot Trigger"); HotTriggerMaximumIntensity = config.Bind<float>("Triggers", "Hot Trigger Maximum Intensity", 0.5f, new ConfigDescription("Maximum intensity for Hot Trigger from 0.0 to 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); HotTriggerMinimumIntensity = config.Bind<float>("Triggers", "Hot Trigger Minimum Intensity", 0.1f, new ConfigDescription("Minimum intensity for Hot Trigger from 0.0 to 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); HotTriggerDuration = config.Bind<float>("Triggers", "Hot Trigger Duration", 1000f, new ConfigDescription("Duration for Hot Trigger in milliseconds", (AcceptableValueBase)(object)new AcceptableValueRange<float>(100f, 10000f), Array.Empty<object>())); ThornsTriggerEnabled = config.Bind<bool>("Triggers", "Thorns Trigger Enabled", true, "Enable or Disable Thorns Trigger"); ThornsTriggerMaximumIntensity = config.Bind<float>("Triggers", "Thorns Trigger Maximum Intensity", 0.5f, new ConfigDescription("Maximum intensity for Thorns Trigger from 0.0 to 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); ThornsTriggerMinimumIntensity = config.Bind<float>("Triggers", "Thorns Trigger Minimum Intensity", 0.1f, new ConfigDescription("Minimum intensity for Thorns Trigger from 0.0 to 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); ThornsTriggerDuration = config.Bind<float>("Triggers", "Thorns Trigger Duration", 3000f, new ConfigDescription("Duration for Thorns Trigger in milliseconds", (AcceptableValueBase)(object)new AcceptableValueRange<float>(100f, 10000f), Array.Empty<object>())); SporesTriggerEnabled = config.Bind<bool>("Triggers", "Spores Trigger Enabled", true, "Enable or Disable Spores Trigger"); SporesTriggerMaximumIntensity = config.Bind<float>("Triggers", "Spores Trigger Maximum Intensity", 0.5f, new ConfigDescription("Maximum intensity for Spores Trigger from 0.0 to 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); SporesTriggerMinimumIntensity = config.Bind<float>("Triggers", "Spores Trigger Minimum Intensity", 0.1f, new ConfigDescription("Minimum intensity for Spores Trigger from 0.0 to 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); SporesTriggerDuration = config.Bind<float>("Triggers", "Spores Trigger Duration", 5000f, new ConfigDescription("Duration for Spores Trigger in milliseconds", (AcceptableValueBase)(object)new AcceptableValueRange<float>(100f, 10000f), Array.Empty<object>())); WebTriggerEnabled = config.Bind<bool>("Triggers", "Web Trigger Enabled", true, "Enable or Disable Web Trigger"); WebTriggerMaximumIntensity = config.Bind<float>("Triggers", "Web Trigger Maximum Intensity", 0.5f, new ConfigDescription("Maximum intensity for Web Trigger from 0.0 to 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); WebTriggerMinimumIntensity = config.Bind<float>("Triggers", "Web Trigger Minimum Intensity", 0.1f, new ConfigDescription("Minimum intensity for Web Trigger from 0.0 to 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); WebTriggerDuration = config.Bind<float>("Triggers", "Web Trigger Duration", 2000f, new ConfigDescription("Duration for Web Trigger in milliseconds", (AcceptableValueBase)(object)new AcceptableValueRange<float>(100f, 10000f), Array.Empty<object>())); ArrowTriggerEnabled = config.Bind<bool>("Triggers", "Arrow Trigger Enabled", true, "Enable or Disable Arrow Trigger"); ArrowTriggerMaximumIntensity = config.Bind<float>("Triggers", "Arrow Trigger Maximum Intensity", 0.5f, new ConfigDescription("Maximum intensity for Arrow Trigger from 0.0 to 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); ArrowTriggerMinimumIntensity = config.Bind<float>("Triggers", "Arrow Trigger Minimum Intensity", 0.1f, new ConfigDescription("Minimum intensity for Arrow Trigger from 0.0 to 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); ArrowTriggerDuration = config.Bind<float>("Triggers", "Arrow Trigger Duration", 5000f, new ConfigDescription("Duration for Arrow Trigger in milliseconds", (AcceptableValueBase)(object)new AcceptableValueRange<float>(100f, 10000f), Array.Empty<object>())); PetrifyTriggerEnabled = config.Bind<bool>("Triggers", "Petrify Trigger Enabled", true, "Enable or Disable Petrify Trigger"); PetrifyTriggerMaximumIntensity = config.Bind<float>("Triggers", "Petrify Trigger Maximum Intensity", 0.5f, new ConfigDescription("Maximum intensity for Petrify Trigger from 0.0 to 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); PetrifyTriggerMinimumIntensity = config.Bind<float>("Triggers", "Petrify Trigger Minimum Intensity", 0.1f, new ConfigDescription("Minimum intensity for Petrify Trigger from 0.0 to 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); PetrifyTriggerDuration = config.Bind<float>("Triggers", "Petrify Trigger Duration", 5000f, new ConfigDescription("Duration for Petrify Trigger in milliseconds", (AcceptableValueBase)(object)new AcceptableValueRange<float>(100f, 10000f), Array.Empty<object>())); DeadEnabled = config.Bind<bool>(new ConfigDefinition("Player State", "Dead Enabled"), true, new ConfigDescription("Enabled on death to trigger toy.", (AcceptableValueBase)null, Array.Empty<object>())); DeadIntensity = config.Bind<float>(new ConfigDefinition("Player State", "Dead Intensity"), 1f, new ConfigDescription("How strong the toy should go when you die.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); DeadTime = config.Bind<float>(new ConfigDefinition("Player State", "Dead Time"), 5000f, new ConfigDescription("How strong the toy should go when you die.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(100f, 10000f), Array.Empty<object>())); PassedOutEnabled = config.Bind<bool>(new ConfigDefinition("Player State", "Passed Out Enabled"), true, new ConfigDescription("Enabled on passing out to trigger toy.", (AcceptableValueBase)null, Array.Empty<object>())); PassedOutIntensity = config.Bind<float>(new ConfigDefinition("Player State", "Passed Out Intensity"), 1f, new ConfigDescription("How strong the toy should go when you pass out.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); PassedOutTime = config.Bind<float>(new ConfigDefinition("Player State", "Passed Out Time"), 5000f, new ConfigDescription("How long the toy should go when you pass out.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(100f, 10000f), Array.Empty<object>())); RagdollEnabled = config.Bind<bool>(new ConfigDefinition("Player State", "Ragdoll Enabled"), true, new ConfigDescription("Enabled on ragdoll to trigger toy.", (AcceptableValueBase)null, Array.Empty<object>())); RagdollIntensity = config.Bind<float>(new ConfigDefinition("Player State", "Ragdoll Intensity"), 1f, new ConfigDescription("How strong the toy should go when you ragdoll.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); RagdollTime = config.Bind<float>(new ConfigDefinition("Player State", "Ragdoll Time"), 5000f, new ConfigDescription("How long the toy should go when you ragdoll.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(100f, 10000f), Array.Empty<object>())); } private void ConstantVibrationSettingChanged(object sender, EventArgs e) { if (ConstantVibration.Value < 0.01f) { Plugin.ToyController?.StopVibrationAsync(); } } private void EnabledSettingChanged(object sender, EventArgs e) { if (Plugin.ButtplugManager != null) { if (Enabled.Value) { Plugin.ButtplugManager.StartReconnecting(); } else { Plugin.ButtplugManager.DisconnectAsync(); } } } } [BepInPlugin("momo.peakintiface", "PEAK Intiface", "0.4.2")] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "momo.peakintiface"; public const string PluginName = "PEAK Intiface"; public const string PluginVersion = "0.4.2"; public static ButtplugManager ButtplugManager; public static ToyController ToyController; public static StaminaTrigger StaminaTrigger; public static StatustEffectTrigger StatustEffectTrigger; public static CharacterStateTrigger CharacterStateTrigger; private void Awake() { ((BaseUnityPlugin)this).Logger.LogInfo((object)"PEAK Intiface v0.4.2 loading..."); new ConfigManager(((BaseUnityPlugin)this).Config); ButtplugManager = new ButtplugManager(((BaseUnityPlugin)this).Logger); if (ConfigManager.Enabled.Value) { ButtplugManager.StartReconnecting(); } ToyController = new ToyController(ButtplugManager, ((BaseUnityPlugin)this).Logger); StaminaTrigger = new StaminaTrigger(((BaseUnityPlugin)this).Logger); StatustEffectTrigger = new StatustEffectTrigger(((BaseUnityPlugin)this).Logger); CharacterStateTrigger = new CharacterStateTrigger(((BaseUnityPlugin)this).Logger); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Intiface Address: {ConfigManager.ServerIP.Value}:{ConfigManager.ServerPort.Value}"); ((BaseUnityPlugin)this).Logger.LogInfo((object)"PEAK Intiface Loaded!"); } private void Update() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (ConfigManager.EmergencyStopEnabled.Value && Input.GetKeyDown(ConfigManager.EmergencyStopKey.Value)) { ToyController?.StopVibrationAsync(); ConfigManager.Enabled.Value = false; } else if (ConfigManager.Enabled.Value && ToyController != null && ButtplugManager.IsConnected) { StaminaTrigger?.Update(); StatustEffectTrigger?.Update(); CharacterStateTrigger?.Update(); } } private void OnApplicationQuit() { if (ButtplugManager == null) { return; } ((BaseUnityPlugin)this).Logger.LogInfo((object)"PEAK is closing. Shutting down Intiface..."); try { Task task = ButtplugManager.DisconnectAsync(); if (!task.Wait(2000)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Intiface shutdown timed out. Allowing PEAK to close."); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Error during Intiface shutdown: " + ex.Message)); } } } } namespace PeakIntiface.Triggers { public class CharacterStateTrigger { private readonly ManualLogSource logger; private bool alreadyDead = false; private bool alreadyPassedOut = false; private bool alreadyRagdoll = false; private readonly Dictionary<string, CancellationTokenSource> timedVibration = new Dictionary<string, CancellationTokenSource>(); public CharacterStateTrigger(ManualLogSource logger) { this.logger = logger; } public void Update() { Character localCharacter = Character.localCharacter; if (!((Object)(object)localCharacter == (Object)null) && !((Object)(object)localCharacter.data == (Object)null)) { bool dead = localCharacter.data.dead; bool flag = localCharacter.data.passedOut || localCharacter.data.fullyPassedOut; bool flag2 = localCharacter.data.fallSeconds > 0f; if (dead && !alreadyDead && ConfigManager.DeadEnabled.Value) { OnDeath(); } if (flag && !alreadyPassedOut && ConfigManager.PassedOutEnabled.Value) { OnPassOut(); } if (flag2 && !alreadyRagdoll && ConfigManager.RagdollEnabled.Value) { OnRagdoll(); } alreadyDead = dead; alreadyPassedOut = flag; alreadyRagdoll = flag2; } } private void OnDeath() { StartTimedVibration("Death", ConfigManager.DeadIntensity.Value, ConfigManager.DeadTime.Value); } private void OnPassOut() { StartTimedVibration("PassOut", ConfigManager.PassedOutIntensity.Value, ConfigManager.PassedOutTime.Value); } private void OnRagdoll() { StartTimedVibration("Ragdoll", ConfigManager.RagdollIntensity.Value, ConfigManager.RagdollTime.Value); } private async Task StartTimedVibration(string VibrationSource, double intensity, float durationMilliseconds) { if (timedVibration.TryGetValue(VibrationSource, out var oldTimer)) { oldTimer.Cancel(); oldTimer.Dispose(); } CancellationTokenSource timer = new CancellationTokenSource(); timedVibration[VibrationSource] = timer; string source = "CharacterState-" + VibrationSource; TriggerUtils.SetVibration(source, intensity); try { await Task.Delay((int)durationMilliseconds, timer.Token); } catch (Exception) { return; } TriggerUtils.SetVibration(source, 0.0); timer.Dispose(); timedVibration.Remove(VibrationSource); } } public class StaminaTrigger { private readonly ManualLogSource logger; public StaminaTrigger(ManualLogSource logger) { this.logger = logger; } public void Update() { if (Plugin.ToyController != null) { Character characterForStats = TriggerUtils.GetCharacterForStats(); if ((Object)(object)characterForStats == (Object)null) { TriggerUtils.SetVibration("movement", 0.0); return; } float currentStamina = characterForStats.data.currentStamina; float maxStamina = characterForStats.GetMaxStamina(); float num = ((maxStamina > 0f) ? (currentStamina / maxStamina) : 0f); TriggerUtils.SetVibration("movement", 1f - num); } } } public class StatustEffectTrigger { private readonly ManualLogSource logger; private Character subscribedCharacter = null; private readonly Dictionary<STATUSTYPE, CancellationTokenSource> afflictionIntensities = new Dictionary<STATUSTYPE, CancellationTokenSource>(); public StatustEffectTrigger(ManualLogSource logger) { this.logger = logger; } internal void Update() { Character characterForStats = TriggerUtils.GetCharacterForStats(); if (!((Object)(object)characterForStats == (Object)null) && !((Object)(object)subscribedCharacter == (Object)(object)characterForStats)) { if ((Object)(object)subscribedCharacter != (Object)null) { CharacterAfflictions afflictions = subscribedCharacter.refs.afflictions; afflictions.OnAddedIncrementalStatus = (Action<STATUSTYPE, float>)Delegate.Remove(afflictions.OnAddedIncrementalStatus, new Action<STATUSTYPE, float>(OnAfflictionAdded)); } subscribedCharacter = characterForStats; CharacterAfflictions afflictions2 = subscribedCharacter.refs.afflictions; afflictions2.OnAddedIncrementalStatus = (Action<STATUSTYPE, float>)Delegate.Combine(afflictions2.OnAddedIncrementalStatus, new Action<STATUSTYPE, float>(OnAfflictionAdded)); logger.LogInfo((object)"Subscribed to status effects."); } } private void OnAfflictionAdded(STATUSTYPE sTATUSTYPE, float amount) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected I4, but got Unknown //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_0293: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: Unknown result type (might be due to invalid IL or missing references) //IL_0337: Unknown result type (might be due to invalid IL or missing references) //IL_0389: Unknown result type (might be due to invalid IL or missing references) //IL_03d2: Unknown result type (might be due to invalid IL or missing references) float num = subscribedCharacter.refs.afflictions.GetCurrentStatus(sTATUSTYPE); if (num <= 0f) { num = amount; } switch ((int)sTATUSTYPE) { case 0: if (ConfigManager.InjuryTriggerEnabled.Value) { double intensity6 = TriggerUtils.CalculateIntensity(num, ConfigManager.InjuryTriggerMaximumIntensity.Value, ConfigManager.InjuryTriggerMinimumIntensity.Value, 2f); HandleAfflictionIntensity(sTATUSTYPE, intensity6, ConfigManager.InjuryTriggerDuration.Value); } break; case 2: if (ConfigManager.ColdTriggerEnabled.Value) { double intensity10 = TriggerUtils.CalculateIntensity(num, ConfigManager.ColdTriggerMaximumIntensity.Value, ConfigManager.ColdTriggerMinimumIntensity.Value, 2f); HandleAfflictionIntensity(sTATUSTYPE, intensity10, ConfigManager.ColdTriggerDuration.Value); } break; case 3: if (ConfigManager.PoisonTriggerEnabled.Value) { double intensity2 = TriggerUtils.CalculateIntensity(num, ConfigManager.PoisonTriggerMaximumIntensity.Value, ConfigManager.PoisonTriggerMinimumIntensity.Value, 2f); HandleAfflictionIntensity(sTATUSTYPE, intensity2, ConfigManager.PoisonTriggerDuration.Value); } break; case 5: if (ConfigManager.CurseTriggerEnabled.Value) { double intensity8 = TriggerUtils.CalculateIntensity(num, ConfigManager.CurseTriggerMaximumIntensity.Value, ConfigManager.CurseTriggerMinimumIntensity.Value, 2f); HandleAfflictionIntensity(sTATUSTYPE, intensity8, ConfigManager.CurseTriggerDuration.Value); } break; case 6: if (ConfigManager.DrowsyTriggerEnabled.Value) { double intensity4 = TriggerUtils.CalculateIntensity(num, ConfigManager.DrowsyTriggerMaximumIntensity.Value, ConfigManager.DrowsyTriggerMinimumIntensity.Value, 2f); HandleAfflictionIntensity(sTATUSTYPE, intensity4, ConfigManager.DrowsyTriggerDuration.Value); } break; case 8: if (ConfigManager.HotTriggerEnabled.Value) { double intensity11 = TriggerUtils.CalculateIntensity(num, ConfigManager.HotTriggerMaximumIntensity.Value, ConfigManager.HotTriggerMinimumIntensity.Value, 2f); HandleAfflictionIntensity(sTATUSTYPE, intensity11, ConfigManager.HotTriggerDuration.Value); } break; case 9: if (!ConfigManager.ThornsTriggerEnabled.Value) { double intensity9 = TriggerUtils.CalculateIntensity(num, ConfigManager.ThornsTriggerMaximumIntensity.Value, ConfigManager.ThornsTriggerMinimumIntensity.Value, 2f); HandleAfflictionIntensity(sTATUSTYPE, intensity9, ConfigManager.ThornsTriggerDuration.Value); } break; case 10: if (ConfigManager.SporesTriggerEnabled.Value) { double intensity7 = TriggerUtils.CalculateIntensity(num, ConfigManager.SporesTriggerMaximumIntensity.Value, ConfigManager.SporesTriggerMinimumIntensity.Value, 2f); HandleAfflictionIntensity(sTATUSTYPE, intensity7, ConfigManager.SporesTriggerDuration.Value); } break; case 11: if (ConfigManager.WebTriggerEnabled.Value) { double intensity5 = TriggerUtils.CalculateIntensity(num, ConfigManager.WebTriggerMaximumIntensity.Value, ConfigManager.WebTriggerMinimumIntensity.Value, 2f); HandleAfflictionIntensity(sTATUSTYPE, intensity5, ConfigManager.WebTriggerDuration.Value); } break; case 12: if (ConfigManager.ArrowTriggerEnabled.Value) { double intensity3 = TriggerUtils.CalculateIntensity(num, ConfigManager.ArrowTriggerMaximumIntensity.Value, ConfigManager.ArrowTriggerMinimumIntensity.Value, 2f); HandleAfflictionIntensity(sTATUSTYPE, intensity3, ConfigManager.ArrowTriggerDuration.Value); } break; case 13: if (!ConfigManager.PetrifyTriggerEnabled.Value) { double intensity = TriggerUtils.CalculateIntensity(num, ConfigManager.PetrifyTriggerMaximumIntensity.Value, ConfigManager.PetrifyTriggerMinimumIntensity.Value, 2f); HandleAfflictionIntensity(sTATUSTYPE, intensity, ConfigManager.PetrifyTriggerDuration.Value); } break; case 1: case 4: case 7: break; } } private async Task HandleAfflictionIntensity(STATUSTYPE sTATUSTYPE, double intensity, float durationMilliseconds) { //IL_0019: 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) if (afflictionIntensities.TryGetValue(sTATUSTYPE, out var oldTimer)) { oldTimer.Cancel(); oldTimer.Dispose(); } CancellationTokenSource timer = new CancellationTokenSource(); afflictionIntensities[sTATUSTYPE] = timer; string source = $"StatustEffectTrigger-{sTATUSTYPE}"; TriggerUtils.SetVibration(source, intensity); try { await Task.Delay((int)durationMilliseconds, timer.Token); } catch (Exception) { return; } TriggerUtils.SetVibration(source, 0.0); timer.Dispose(); afflictionIntensities.Remove(sTATUSTYPE); } } internal static class TriggerUtils { public static double CalculateIntensity(float intensity, float maximum, float minimum, float multiplier = 1f) { return minimum + intensity * multiplier * (maximum - minimum); } public static void SetVibration(string source, double intensity) { Plugin.ToyController.SetSourceIntensity(source, intensity); } public static Character GetCharacterForStats() { Character val = Character.localCharacter; if ((Object)(object)val == (Object)null) { return null; } if ((Object)(object)val.data == (Object)null) { return null; } if (ConfigManager.UseCarried.Value && val.data.isCarried && (Object)(object)val.data.carrier != (Object)null) { val = val.data.carrier; } else if (ConfigManager.UseSpectate.Value && val.data.dead) { val = Character.observedCharacter; } return val; } } } namespace PeakIntiface.Toy { public class ToyController { private readonly ManualLogSource logger; private readonly ButtplugManager buttplugManager; private readonly Dictionary<string, double> sourceIntensities = new Dictionary<string, double>(); public ToyController(ButtplugManager buttplugManager, ManualLogSource logger) { logger.LogInfo((object)"ToyController initialized."); this.logger = logger; this.buttplugManager = buttplugManager; } public void SetSourceIntensity(string source, double intensity) { sourceIntensities[source] = intensity; UpdateVibrationAsync(); } private async void UpdateVibrationAsync() { if (!buttplugManager.IsConnected) { return; } double highestIntensity = ConfigManager.ConstantVibration.Value; string highestSource = "ConstantVibration"; foreach (KeyValuePair<string, double> sourceIntensity in sourceIntensities) { if (sourceIntensity.Value > highestIntensity) { highestIntensity = sourceIntensity.Value; highestSource = sourceIntensity.Key; } } logger.LogInfo((object)$"Highest intensity source: {highestSource} with intensity: {highestIntensity}"); if (highestIntensity < 0.009999999776482582) { await StopVibrationAsync(); return; } ButtplugClientDevice[] devices = buttplugManager.Client.Devices; foreach (ButtplugClientDevice device in devices) { if (device.HasOutput((OutputType)1)) { try { double clampedVibrateIntensity = Math.Max(0.0, Math.Min(highestIntensity, ConfigManager.MaximumIntensity.Value)); await device.RunOutputAsync(DeviceOutput.Vibrate.Percent(clampedVibrateIntensity), default(CancellationToken)); } catch (Exception ex) { Exception ex2 = ex; logger.LogWarning((object)("Could not control " + device.Name + ": " + ex2.Message)); } } if (device.HasOutput((OutputType)3) && highestSource != "ConstantVibration") { try { double clampedRotateIntensity = Math.Max(0.0, Math.Min(highestIntensity, ConfigManager.MaximumRotate.Value)); double rotationIntensity = clampedRotateIntensity * (double)((!(Random.value < 0.5f)) ? 1 : (-1)); await device.RunOutputAsync(DeviceOutput.Rotate.Percent(rotationIntensity), default(CancellationToken)); } catch (Exception ex) { Exception ex3 = ex; logger.LogWarning((object)("Could not control " + device.Name + ": " + ex3.Message)); } } if (device.HasOutput((OutputType)8)) { try { double clampedConstrictIntensity = Math.Max(0.0, Math.Min(highestIntensity, ConfigManager.MaximumConstrict.Value)); await device.RunOutputAsync(DeviceOutput.Constrict.Percent(clampedConstrictIntensity), default(CancellationToken)); } catch (Exception ex) { Exception ex4 = ex; logger.LogWarning((object)("Could not control " + device.Name + ": " + ex4.Message)); } } } } public async Task StopVibrationAsync() { if (!buttplugManager.IsConnected) { return; } sourceIntensities.Clear(); try { await buttplugManager.Client.StopAllDevicesAsync(default(CancellationToken)); } catch (Exception ex) { logger.LogWarning((object)("Could not stop devices: " + ex.Message)); } } } } namespace PeakIntiface.Buttplug { public class ButtplugManager { private readonly ManualLogSource logger; private ButtplugClient client; private bool reconnecting = false; public bool IsConnected => client != null && client.Connected; public ButtplugClient Client => client; public ButtplugManager(ManualLogSource logger) { this.logger = logger; } public async Task StartReconnecting() { if (reconnecting) { return; } reconnecting = true; while (reconnecting) { if (!IsConnected) { logger.LogInfo((object)"Retrying reconnecting"); await ConnectAsync(ConfigManager.ServerIP.Value, ConfigManager.ServerPort.Value); } await Task.Delay(5000); } } private async Task ConnectAsync(string ip, int port) { if (IsConnected) { return; } string address = $"ws://{ip}:{port}"; logger.LogInfo((object)("Connecting to " + address + "...")); try { client = new ButtplugClient("PEAK Intiface"); client.DeviceAdded += OnDeviceAdded; client.DeviceRemoved += OnDeviceRemoved; ButtplugWebsocketConnector connector = new ButtplugWebsocketConnector(new Uri(address)); await client.ConnectAsync((IButtplugClientConnector)(object)connector, default(CancellationToken)); logger.LogInfo((object)"Connected to Intiface!"); await client.StartScanningAsync(default(CancellationToken)); } catch { logger.LogError((object)"Could not connect to Intiface"); client = null; } } public async Task DisconnectAsync() { reconnecting = false; if (!IsConnected) { return; } try { try { await client.StopScanningAsync(default(CancellationToken)).ConfigureAwait(continueOnCapturedContext: false); } catch { } try { await client.StopAllDevicesAsync(default(CancellationToken)).ConfigureAwait(continueOnCapturedContext: false); } catch { } await client.DisconnectAsync().ConfigureAwait(continueOnCapturedContext: false); logger.LogInfo((object)"Disconnected from Intiface."); } catch (Exception ex) { Exception ex2 = ex; logger.LogWarning((object)$"Error disconnecting from Intiface: {ex2}"); } finally { client = null; } } private void OnDeviceAdded(object sender, DeviceAddedEventArgs args) { logger.LogInfo((object)("Device connected: " + args.Device.Name)); } private void OnDeviceRemoved(object sender, DeviceRemovedEventArgs args) { logger.LogInfo((object)("Device disconnected: " + args.Device.Name)); } } }