Decompiled source of RumbleRain v0.5.0

plugins/RumbleRain/Buttplug.dll

Decompiled 2 months ago
using 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("4.0.0.0")]
[assembly: AssemblyInformationalVersion("4.0.0+41a6e2363781583c9c9f475b1544b7ebee02bd52")]
[assembly: AssemblyProduct("Buttplug")]
[assembly: AssemblyTitle("Buttplug")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/buttplugio/buttplug-csharp")]
[assembly: AssemblyVersion("4.0.0.0")]
namespace Buttplug.Core
{
	public static class ButtplugConsts
	{
		public const uint SystemMsgId = 0u;

		public const uint DefaultMsgId = 1u;

		public const uint CurrentSpecVersion = 3u;
	}
	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)1
			};
			_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 val2)
				{
					JsonReaderException val3 = val2;
					throw new ButtplugMessageException("Not valid JSON: " + jsonMsg + " - " + ((Exception)(object)val3).Message);
				}
				JArray val4;
				try
				{
					val4 = JArray.Load((JsonReader)(object)val);
				}
				catch (JsonReaderException val5)
				{
					JsonReaderException val6 = val5;
					throw new ButtplugMessageException("Not valid JSON: " + jsonMsg + " - " + ((Exception)(object)val6).Message);
				}
				foreach (JObject item in ((JToken)val4).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 val)
			{
				JsonSerializationException val2 = val;
				throw new ButtplugMessageException($"Could not create message for JSON {obj}: {((Exception)(object)val2).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
	{
	}
	public interface IButtplugDeviceInfoMessage
	{
		string DeviceName { get; }

		uint DeviceIndex { get; }

		DeviceMessageAttributes DeviceMessages { get; }

		string DeviceDisplayName { get; }

		uint DeviceMessageTimingGap { get; }
	}
	[AttributeUsage(AttributeTargets.Class)]
	public class ButtplugMessageMetadata : Attribute
	{
		public string Name { get; }

		public ButtplugMessageMetadata(string name)
		{
			Name = name;
		}
	}
	[JsonConverter(typeof(StringEnumConverter))]
	public enum ActuatorType
	{
		[EnumMember(Value = "Unknown")]
		Unknown,
		[EnumMember(Value = "Vibrate")]
		Vibrate,
		[EnumMember(Value = "Rotate")]
		Rotate,
		[EnumMember(Value = "Oscillate")]
		Oscillate,
		[EnumMember(Value = "Constrict")]
		Constrict,
		[EnumMember(Value = "Inflate")]
		Inflate,
		[EnumMember(Value = "Position")]
		Position
	}
	[JsonConverter(typeof(StringEnumConverter))]
	public enum SensorType
	{
		[EnumMember(Value = "Unknown")]
		Unknown,
		[EnumMember(Value = "Battery")]
		Battery,
		[EnumMember(Value = "RSSI")]
		RSSI,
		[EnumMember(Value = "Button")]
		Button,
		[EnumMember(Value = "Pressure")]
		Pressure
	}
	public class GenericDeviceMessageAttributes
	{
		[JsonIgnore]
		internal uint _index;

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public readonly string FeatureDescriptor;

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		[JsonConverter(typeof(StringEnumConverter))]
		public readonly ActuatorType ActuatorType;

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public readonly uint StepCount;

		[JsonIgnore]
		public uint Index => _index;
	}
	public class SensorDeviceMessageAttributes
	{
		[JsonIgnore]
		internal uint _index;

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public readonly string FeatureDescriptor;

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		[JsonConverter(typeof(StringEnumConverter))]
		public readonly SensorType SensorType;

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public readonly uint[][] SensorRange;

		[JsonIgnore]
		public uint Index => _index;
	}
	public class RawDeviceMessageAttributes
	{
		public readonly string[] Endpoints;
	}
	public class NullDeviceMessageAttributes
	{
	}
	public class DeviceMessageAttributes
	{
		internal class EnumeratePair<T>
		{
			public readonly int index;

			public readonly T attr;

			public EnumeratePair(T attr, int index)
			{
				this.index = index;
				this.attr = attr;
			}
		}

		public GenericDeviceMessageAttributes[] ScalarCmd;

		public GenericDeviceMessageAttributes[] RotateCmd;

		public GenericDeviceMessageAttributes[] LinearCmd;

		public SensorDeviceMessageAttributes[] SensorReadCmd;

		public SensorDeviceMessageAttributes[] SensorSubscribeCmd;

		public readonly RawDeviceMessageAttributes[] RawReadCmd;

		public readonly RawDeviceMessageAttributes[] RawWriteCmd;

		public readonly RawDeviceMessageAttributes[] RawSubscribeCmd;

		public readonly NullDeviceMessageAttributes StopDeviceCmd;

		[OnDeserialized]
		internal void OnDeserializedMethod(StreamingContext context)
		{
			ScalarCmd?.Select((GenericDeviceMessageAttributes x, int i) => new EnumeratePair<GenericDeviceMessageAttributes>(x, i)).ToList().ForEach(delegate(EnumeratePair<GenericDeviceMessageAttributes> x)
			{
				x.attr._index = (uint)x.index;
			});
			RotateCmd?.Select((GenericDeviceMessageAttributes x, int i) => new EnumeratePair<GenericDeviceMessageAttributes>(x, i)).ToList().ForEach(delegate(EnumeratePair<GenericDeviceMessageAttributes> x)
			{
				x.attr._index = (uint)x.index;
			});
			LinearCmd?.Select((GenericDeviceMessageAttributes x, int i) => new EnumeratePair<GenericDeviceMessageAttributes>(x, i)).ToList().ForEach(delegate(EnumeratePair<GenericDeviceMessageAttributes> x)
			{
				x.attr._index = (uint)x.index;
			});
			SensorReadCmd?.Select((SensorDeviceMessageAttributes x, int i) => new EnumeratePair<SensorDeviceMessageAttributes>(x, i)).ToList().ForEach(delegate(EnumeratePair<SensorDeviceMessageAttributes> x)
			{
				x.attr._index = (uint)x.index;
			});
			SensorSubscribeCmd?.Select((SensorDeviceMessageAttributes x, int i) => new EnumeratePair<SensorDeviceMessageAttributes>(x, i)).ToList().ForEach(delegate(EnumeratePair<SensorDeviceMessageAttributes> x)
			{
				x.attr._index = (uint)x.index;
			});
		}
	}
	public class MessageReceivedEventArgs : EventArgs
	{
		public ButtplugMessage Message { get; }

		public MessageReceivedEventArgs(ButtplugMessage message)
		{
			Message = message;
		}
	}
	[ButtplugMessageMetadata("Ok")]
	public class Ok : ButtplugMessage, IButtplugMessageOutgoingOnly
	{
		public Ok(uint id)
			: base(id)
		{
		}
	}
	[ButtplugMessageMetadata("Test")]
	public class Test : ButtplugMessage
	{
		private string _testStringImpl;

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public string TestString
		{
			get
			{
				return _testStringImpl;
			}
			set
			{
				if (value == "Error")
				{
					throw new ArgumentException("Got an Error Message");
				}
				_testStringImpl = value;
			}
		}

		public Test(string str, uint id = 1u)
			: base(id)
		{
			TestString = str;
		}
	}
	[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 MessageAttributes : IEquatable<MessageAttributes>
	{
		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public uint? FeatureCount;

		public MessageAttributes()
		{
		}

		public MessageAttributes(uint featureCount)
		{
			FeatureCount = featureCount;
		}

		public bool Equals(MessageAttributes attrs)
		{
			return FeatureCount == attrs.FeatureCount;
		}
	}
	public class DeviceMessageInfo : IButtplugDeviceInfoMessage
	{
		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public readonly string DeviceName;

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public readonly uint DeviceIndex;

		public readonly string DeviceDisplayName;

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public readonly uint DeviceMessageTimingGap;

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public readonly DeviceMessageAttributes DeviceMessages;

		string IButtplugDeviceInfoMessage.DeviceName => DeviceName;

		uint IButtplugDeviceInfoMessage.DeviceIndex => DeviceIndex;

		DeviceMessageAttributes IButtplugDeviceInfoMessage.DeviceMessages => DeviceMessages;

		string IButtplugDeviceInfoMessage.DeviceDisplayName => DeviceDisplayName;

		uint IButtplugDeviceInfoMessage.DeviceMessageTimingGap => DeviceMessageTimingGap;

		public DeviceMessageInfo(uint index, string name, DeviceMessageAttributes messages)
		{
			DeviceName = name;
			DeviceIndex = index;
			DeviceMessages = messages;
		}
	}
	[ButtplugMessageMetadata("DeviceList")]
	public class DeviceList : ButtplugMessage, IButtplugMessageOutgoingOnly
	{
		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public readonly DeviceMessageInfo[] Devices = new DeviceMessageInfo[0];

		public DeviceList(DeviceMessageInfo[] deviceList, uint id)
			: base(id)
		{
			Devices = deviceList;
		}

		internal DeviceList()
			: base(0u)
		{
		}
	}
	[ButtplugMessageMetadata("DeviceAdded")]
	public class DeviceAdded : ButtplugDeviceMessage, IButtplugMessageOutgoingOnly, IButtplugDeviceInfoMessage
	{
		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public string DeviceName;

		public readonly string DeviceDisplayName;

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public readonly uint DeviceMessageTimingGap;

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public readonly DeviceMessageAttributes DeviceMessages;

		string IButtplugDeviceInfoMessage.DeviceName => DeviceName;

		uint IButtplugDeviceInfoMessage.DeviceIndex => base.DeviceIndex;

		DeviceMessageAttributes IButtplugDeviceInfoMessage.DeviceMessages => DeviceMessages;

		string IButtplugDeviceInfoMessage.DeviceDisplayName => DeviceDisplayName;

		uint IButtplugDeviceInfoMessage.DeviceMessageTimingGap => DeviceMessageTimingGap;

		public DeviceAdded(uint index, string name, DeviceMessageAttributes messages)
			: base(0u, index)
		{
			DeviceName = name;
			DeviceMessages = messages;
		}

		internal DeviceAdded()
			: base(0u)
		{
		}
	}
	[ButtplugMessageMetadata("DeviceRemoved")]
	public class DeviceRemoved : ButtplugMessage, IButtplugMessageOutgoingOnly
	{
		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public readonly uint DeviceIndex;

		public DeviceRemoved(uint index)
			: base(0u)
		{
			DeviceIndex = index;
		}
	}
	[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 MessageVersion;

		public RequestServerInfo(string clientName, uint id = 1u, uint schemversion = 3u)
			: base(id)
		{
			ClientName = clientName;
			MessageVersion = schemversion;
		}
	}
	[ButtplugMessageMetadata("ServerInfo")]
	public class ServerInfo : ButtplugMessage, IButtplugMessageOutgoingOnly
	{
		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public uint MessageVersion;

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public uint MaxPingTime;

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public string ServerName;

		public ServerInfo(string serverName, uint messageVersion, uint maxPingTime, uint id = 1u)
			: base(id)
		{
			ServerName = serverName;
			MessageVersion = messageVersion;
			MaxPingTime = maxPingTime;
		}
	}
	[ButtplugMessageMetadata("Ping")]
	public class Ping : ButtplugMessage
	{
		public Ping(uint id = 1u)
			: base(id)
		{
		}
	}
	public class GenericMessageSubcommand
	{
		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public uint Index;

		protected GenericMessageSubcommand(uint index)
		{
			Index = index;
		}
	}
	[ButtplugMessageMetadata("ScalarCmd")]
	public class ScalarCmd : ButtplugDeviceMessage
	{
		public class ScalarCommand
		{
			public readonly uint index;

			public readonly double scalar;

			public ScalarCommand(uint index, double scalar)
			{
				this.index = index;
				this.scalar = scalar;
			}
		}

		public class ScalarSubcommand : GenericMessageSubcommand
		{
			private double _scalarImpl;

			public readonly ActuatorType ActuatorType;

			[JsonProperty(/*Could not decode attribute arguments.*/)]
			public double Scalar
			{
				get
				{
					return _scalarImpl;
				}
				set
				{
					if (value < 0.0)
					{
						throw new ArgumentException("ScalarCmd value cannot be less than 0!");
					}
					if (value > 1.0)
					{
						throw new ArgumentException("ScalarCmd value cannot be greater than 1!");
					}
					_scalarImpl = value;
				}
			}

			public ScalarSubcommand(uint index, double scalar, ActuatorType actuatorType)
				: base(index)
			{
				Scalar = scalar;
				ActuatorType = actuatorType;
			}
		}

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public List<ScalarSubcommand> Scalars;

		[JsonConstructor]
		public ScalarCmd(uint deviceIndex, List<ScalarSubcommand> scalars, uint id = 1u)
			: base(id, deviceIndex)
		{
			Scalars = scalars;
		}

		public ScalarCmd(List<ScalarSubcommand> scalars)
			: this(uint.MaxValue, scalars)
		{
		}
	}
	[ButtplugMessageMetadata("RotateCmd")]
	public class RotateCmd : ButtplugDeviceMessage
	{
		public class RotateCommand
		{
			public readonly double speed;

			public readonly bool clockwise;

			public RotateCommand(double speed, bool clockwise)
			{
				this.speed = speed;
				this.clockwise = clockwise;
			}
		}

		public class RotateSubcommand : GenericMessageSubcommand
		{
			private double _speedImpl;

			[JsonProperty(/*Could not decode attribute arguments.*/)]
			public bool Clockwise;

			[JsonProperty(/*Could not decode attribute arguments.*/)]
			public double Speed
			{
				get
				{
					return _speedImpl;
				}
				set
				{
					if (value < 0.0)
					{
						throw new ArgumentException("RotateCmd Speed cannot be less than 0!");
					}
					if (value > 1.0)
					{
						throw new ArgumentException("RotateCmd Speed cannot be greater than 1!");
					}
					_speedImpl = value;
				}
			}

			public RotateSubcommand(uint index, double speed, bool clockwise)
				: base(index)
			{
				Speed = speed;
				Clockwise = clockwise;
			}
		}

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public List<RotateSubcommand> Rotations;

		public static RotateCmd Create(double speed, bool clockwise, uint cmdCount)
		{
			return Create(uint.MaxValue, 1u, Enumerable.Repeat(new RotateCommand(speed, clockwise), (int)cmdCount));
		}

		public static RotateCmd Create(IEnumerable<RotateCommand> cmds)
		{
			return Create(uint.MaxValue, 1u, cmds);
		}

		public static RotateCmd Create(uint deviceIndex, uint msgId, double speed, bool clockwise, uint cmdCount)
		{
			return Create(deviceIndex, msgId, Enumerable.Repeat(new RotateCommand(speed, clockwise), (int)cmdCount));
		}

		public static RotateCmd Create(uint deviceIndex, uint msgId, IEnumerable<RotateCommand> cmds)
		{
			List<RotateSubcommand> list = new List<RotateSubcommand>(cmds.Count());
			uint num = 0u;
			foreach (RotateCommand cmd in cmds)
			{
				list.Add(new RotateSubcommand(num, cmd.speed, cmd.clockwise));
				num++;
			}
			return new RotateCmd(deviceIndex, list, msgId);
		}

		[JsonConstructor]
		public RotateCmd(uint deviceIndex, List<RotateSubcommand> rotations, uint id = 1u)
			: base(id, deviceIndex)
		{
			Rotations = rotations;
		}

		public RotateCmd(List<RotateSubcommand> rotations)
			: this(uint.MaxValue, rotations)
		{
		}
	}
	[ButtplugMessageMetadata("LinearCmd")]
	public class LinearCmd : ButtplugDeviceMessage
	{
		public class VectorCommand
		{
			public readonly double position;

			public readonly uint duration;

			public VectorCommand(double position, uint duration)
			{
				this.position = position;
				this.duration = duration;
			}
		}

		public class VectorSubcommand : GenericMessageSubcommand
		{
			private double _positionImpl;

			[JsonProperty(/*Could not decode attribute arguments.*/)]
			public uint Duration;

			[JsonProperty(/*Could not decode attribute arguments.*/)]
			public double Position
			{
				get
				{
					return _positionImpl;
				}
				set
				{
					if (value < 0.0)
					{
						throw new ArgumentException("LinearCmd Speed cannot be less than 0!");
					}
					if (value > 1.0)
					{
						throw new ArgumentException("LinearCmd Speed cannot be greater than 1!");
					}
					_positionImpl = value;
				}
			}

			public VectorSubcommand(uint index, uint duration, double position)
				: base(index)
			{
				Duration = duration;
				Position = position;
			}
		}

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public List<VectorSubcommand> Vectors;

		public static LinearCmd Create(uint duration, double position, uint cmdCount)
		{
			return Create(uint.MaxValue, 1u, Enumerable.Repeat(new VectorCommand(position, duration), (int)cmdCount));
		}

		public static LinearCmd Create(uint deviceIndex, uint msgId, uint duration, double position, uint cmdCount)
		{
			return Create(deviceIndex, msgId, Enumerable.Repeat(new VectorCommand(position, duration), (int)cmdCount));
		}

		public static LinearCmd Create(IEnumerable<VectorCommand> cmds)
		{
			return Create(uint.MaxValue, 1u, cmds);
		}

		public static LinearCmd Create(uint deviceIndex, uint msgId, IEnumerable<VectorCommand> cmds)
		{
			List<VectorSubcommand> list = new List<VectorSubcommand>(cmds.Count());
			uint num = 0u;
			foreach (VectorCommand cmd in cmds)
			{
				list.Add(new VectorSubcommand(num, cmd.duration, cmd.position));
				num++;
			}
			return new LinearCmd(deviceIndex, list, msgId);
		}

		[JsonConstructor]
		public LinearCmd(uint deviceIndex, List<VectorSubcommand> vectors, uint id = 1u)
			: base(id, deviceIndex)
		{
			Vectors = vectors;
		}

		public LinearCmd(List<VectorSubcommand> vectors)
			: this(uint.MaxValue, vectors)
		{
		}
	}
	[ButtplugMessageMetadata("StopDeviceCmd")]
	public class StopDeviceCmd : ButtplugDeviceMessage
	{
		public StopDeviceCmd(uint deviceIndex = uint.MaxValue, uint id = 1u)
			: base(id, deviceIndex)
		{
		}
	}
	[ButtplugMessageMetadata("StopAllDevices")]
	public class StopAllDevices : ButtplugMessage
	{
		public StopAllDevices(uint id = 1u)
			: base(id)
		{
		}
	}
	[ButtplugMessageMetadata("SensorReadCmd")]
	public class SensorReadCmd : ButtplugDeviceMessage
	{
		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public uint SensorIndex;

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public SensorType SensorType;

		[JsonConstructor]
		public SensorReadCmd(uint deviceIndex, uint sensorIndex, SensorType sensorType, uint id = 1u)
			: base(id, deviceIndex)
		{
			SensorIndex = sensorIndex;
			SensorType = sensorType;
		}

		public SensorReadCmd(uint sensorIndex, SensorType sensorType)
			: this(uint.MaxValue, sensorIndex, sensorType)
		{
		}
	}
	[ButtplugMessageMetadata("SensorReading")]
	public class SensorReading : ButtplugDeviceMessage
	{
		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public readonly uint SensorIndex;

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public readonly SensorType SensorType;

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public readonly List<int> data;
	}
}
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;

		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 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;
			_connector.Disconnected += delegate(object obj, EventArgs eventArgs)
			{
				this.ServerDisconnect?.Invoke(obj, eventArgs);
			};
			_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.MessageVersion < 3)
			{
				await DisconnectAsync().ConfigureAwait(continueOnCapturedContext: false);
				throw new ButtplugHandshakeException($"Buttplug Server's schema version ({si.MessageVersion}) is less than the client's ({3u}). A newer server is required.", res.Id);
			}
			ButtplugMessage resp = await _handler.SendMessageAsync(new RequestDeviceList()).ConfigureAwait(continueOnCapturedContext: false);
			if (resp is DeviceList deviceList)
			{
				DeviceMessageInfo[] devices = deviceList.Devices;
				foreach (DeviceMessageInfo deviceMessageInfo in devices)
				{
					if (!_devices.ContainsKey(deviceMessageInfo.DeviceIndex))
					{
						ButtplugClientDevice buttplugClientDevice = new ButtplugClientDevice(_handler, deviceMessageInfo);
						_devices[deviceMessageInfo.DeviceIndex] = buttplugClientDevice;
						this.DeviceAdded?.Invoke(this, new DeviceAddedEventArgs(buttplugClientDevice));
					}
				}
				return;
			}
			await DisconnectAsync().ConfigureAwait(continueOnCapturedContext: false);
			if (resp is Error msg)
			{
				throw ButtplugException.FromError(msg);
			}
			throw new ButtplugHandshakeException("Received unknown response to DeviceList handshake query");
		}

		public async Task DisconnectAsync()
		{
			if (Connected)
			{
				_connector.MessageReceived -= MessageReceivedHandler;
				await _connector.DisconnectAsync().ConfigureAwait(continueOnCapturedContext: false);
				this.ServerDisconnect?.Invoke(this, EventArgs.Empty);
			}
		}

		public async Task StartScanningAsync(CancellationToken token = default(CancellationToken))
		{
			await _handler.SendMessageExpectOk(new StartScanning(), token).ConfigureAwait(continueOnCapturedContext: false);
		}

		public async Task StopScanningAsync(CancellationToken token = default(CancellationToken))
		{
			await _handler.SendMessageExpectOk(new StopScanning(), token).ConfigureAwait(continueOnCapturedContext: false);
		}

		public async Task StopAllDevicesAsync(CancellationToken token = default(CancellationToken))
		{
			await _handler.SendMessageExpectOk(new StopAllDevices(), token).ConfigureAwait(continueOnCapturedContext: false);
		}

		private void ConnectorErrorHandler(object sender, ButtplugExceptionEventArgs exception)
		{
			this.ErrorReceived?.Invoke(this, exception);
		}

		private async void MessageReceivedHandler(object sender, MessageReceivedEventArgs args)
		{
			ButtplugMessage message = args.Message;
			if (!(message is DeviceAdded deviceAdded))
			{
				ButtplugClientDevice value;
				if (!(message is DeviceRemoved deviceRemoved))
				{
					if (!(message is ScanningFinished))
					{
						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.ScanningFinished?.Invoke(this, EventArgs.Empty);
					}
				}
				else if (!_devices.ContainsKey(deviceRemoved.DeviceIndex))
				{
					this.ErrorReceived?.Invoke(this, new ButtplugExceptionEventArgs(new ButtplugDeviceException("Got device removed message for unknown device.", message.Id)));
				}
				else if (_devices.TryRemove(deviceRemoved.DeviceIndex, out value))
				{
					this.DeviceRemoved?.Invoke(this, new DeviceRemovedEventArgs(value));
				}
			}
			else
			{
				ButtplugClientDevice dev = new ButtplugClientDevice(_handler, deviceAdded);
				_devices.AddOrUpdate(deviceAdded.DeviceIndex, dev, (uint u, ButtplugClientDevice device) => dev);
				this.DeviceAdded?.Invoke(this, new DeviceAddedEventArgs(dev));
			}
		}

		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)
		{
			DisconnectAsync().GetAwaiter().GetResult();
		}

		public void Dispose()
		{
			Dispose(disposing: true);
			GC.SuppressFinalize(this);
		}

		protected virtual async ValueTask DisposeAsync(bool disposing)
		{
			await DisconnectAsync();
		}

		public async ValueTask DisposeAsync()
		{
			await DisposeAsync(disposing: true);
			GC.SuppressFinalize(this);
		}
	}
	public class ButtplugClientConnectorException : ButtplugException
	{
		public ButtplugClientConnectorException(string message, Exception inner = null)
			: base(message, Error.ErrorClass.ERROR_UNKNOWN, 0u, inner)
		{
		}
	}
	public class ButtplugClientDevice
	{
		private readonly ButtplugClientMessageHandler _handler;

		public uint Index { get; }

		public string Name { get; }

		public string DisplayName { get; }

		public uint MessageTimingGap { get; }

		public DeviceMessageAttributes MessageAttributes { get; }

		public List<GenericDeviceMessageAttributes> VibrateAttributes => GenericAcutatorAttributes(ActuatorType.Vibrate);

		public List<GenericDeviceMessageAttributes> OscillateAttributes => GenericAcutatorAttributes(ActuatorType.Oscillate);

		public List<GenericDeviceMessageAttributes> RotateAttributes
		{
			get
			{
				if (MessageAttributes.RotateCmd != null)
				{
					return MessageAttributes.RotateCmd.ToList();
				}
				return Enumerable.Empty<GenericDeviceMessageAttributes>().ToList();
			}
		}

		public List<GenericDeviceMessageAttributes> LinearAttributes
		{
			get
			{
				if (MessageAttributes.LinearCmd != null)
				{
					return MessageAttributes.LinearCmd.ToList();
				}
				return Enumerable.Empty<GenericDeviceMessageAttributes>().ToList();
			}
		}

		public bool HasBattery => SensorReadAttributes(SensorType.Battery).Any();

		internal ButtplugClientDevice(ButtplugClientMessageHandler handler, IButtplugDeviceInfoMessage devInfo)
			: this(handler, devInfo.DeviceIndex, devInfo.DeviceName, devInfo.DeviceMessages, devInfo.DeviceDisplayName, devInfo.DeviceMessageTimingGap)
		{
			ButtplugUtils.ArgumentNotNull(devInfo, "devInfo");
		}

		internal ButtplugClientDevice(ButtplugClientMessageHandler handler, uint index, string name, DeviceMessageAttributes messages, string displayName, uint messageTimingGap)
		{
			ButtplugUtils.ArgumentNotNull(handler, "handler");
			_handler = handler;
			Index = index;
			Name = name;
			MessageAttributes = messages;
			DisplayName = displayName;
			MessageTimingGap = messageTimingGap;
		}

		public List<GenericDeviceMessageAttributes> GenericAcutatorAttributes(ActuatorType actuator)
		{
			if (MessageAttributes.ScalarCmd != null)
			{
				return MessageAttributes.ScalarCmd.Where((GenericDeviceMessageAttributes x) => x.ActuatorType == actuator).ToList();
			}
			return Enumerable.Empty<GenericDeviceMessageAttributes>().ToList();
		}

		public async Task ScalarAsync(ScalarCmd.ScalarSubcommand command)
		{
			List<ScalarCmd.ScalarSubcommand> scalars = new List<ScalarCmd.ScalarSubcommand>();
			GenericAcutatorAttributes(command.ActuatorType).ForEach(delegate(GenericDeviceMessageAttributes x)
			{
				scalars.Add(new ScalarCmd.ScalarSubcommand(x.Index, command.Scalar, command.ActuatorType));
			});
			if (!scalars.Any())
			{
				throw new ButtplugDeviceException("Scalar command for device " + Name + " did not generate any commands. Are you sure the device supports the ActuatorType sent?");
			}
			await _handler.SendMessageExpectOk(new ScalarCmd(Index, scalars)).ConfigureAwait(continueOnCapturedContext: false);
		}

		public async Task ScalarAsync(List<ScalarCmd.ScalarSubcommand> command)
		{
			if (!command.Any())
			{
				throw new ArgumentException("Command List for ScalarAsync must have at least 1 command.");
			}
			await _handler.SendMessageExpectOk(new ScalarCmd(Index, command)).ConfigureAwait(continueOnCapturedContext: false);
		}

		public async Task VibrateAsync(double speed)
		{
			await ScalarAsync(new ScalarCmd.ScalarSubcommand(uint.MaxValue, speed, ActuatorType.Vibrate));
		}

		public async Task VibrateAsync(IEnumerable<double> cmds)
		{
			List<GenericDeviceMessageAttributes> vibrateAttributes = VibrateAttributes;
			if (cmds.Count() > vibrateAttributes.Count())
			{
				throw new ButtplugDeviceException($"Device {Name} only has {vibrateAttributes.Count()} vibrators, but {cmds.Count()} commands given.");
			}
			await ScalarAsync(vibrateAttributes.Select((GenericDeviceMessageAttributes x, int i) => new ScalarCmd.ScalarSubcommand(x.Index, cmds.ElementAt(i), ActuatorType.Vibrate)).ToList()).ConfigureAwait(continueOnCapturedContext: false);
		}

		public async Task VibrateAsync(IEnumerable<ScalarCmd.ScalarCommand> cmds)
		{
			await ScalarAsync(cmds.Select((ScalarCmd.ScalarCommand x) => new ScalarCmd.ScalarSubcommand(x.index, x.scalar, ActuatorType.Vibrate)).ToList()).ConfigureAwait(continueOnCapturedContext: false);
		}

		public async Task OscillateAsync(double speed)
		{
			await ScalarAsync(new ScalarCmd.ScalarSubcommand(uint.MaxValue, speed, ActuatorType.Oscillate));
		}

		public async Task OscillateAsync(IEnumerable<double> cmds)
		{
			List<GenericDeviceMessageAttributes> oscillateAttributes = OscillateAttributes;
			if (cmds.Count() > oscillateAttributes.Count())
			{
				throw new ButtplugDeviceException($"Device {Name} only has {oscillateAttributes.Count()} vibrators, but {cmds.Count()} commands given.");
			}
			await ScalarAsync(oscillateAttributes.Select((GenericDeviceMessageAttributes x, int i) => new ScalarCmd.ScalarSubcommand(x.Index, cmds.ElementAt(i), ActuatorType.Oscillate)).ToList()).ConfigureAwait(continueOnCapturedContext: false);
		}

		public async Task OscillateAsync(IEnumerable<ScalarCmd.ScalarCommand> cmds)
		{
			await ScalarAsync(cmds.Select((ScalarCmd.ScalarCommand x) => new ScalarCmd.ScalarSubcommand(x.index, x.scalar, ActuatorType.Oscillate)).ToList()).ConfigureAwait(continueOnCapturedContext: false);
		}

		public async Task RotateAsync(double speed, bool clockwise)
		{
			if (!RotateAttributes.Any())
			{
				throw new ButtplugDeviceException("Device " + Name + " does not support rotation");
			}
			RotateCmd rotateCmd = RotateCmd.Create(speed, clockwise, (uint)RotateAttributes.Count);
			rotateCmd.DeviceIndex = Index;
			await _handler.SendMessageExpectOk(rotateCmd).ConfigureAwait(continueOnCapturedContext: false);
		}

		public async Task RotateAsync(IEnumerable<RotateCmd.RotateCommand> cmds)
		{
			if (!RotateAttributes.Any())
			{
				throw new ButtplugDeviceException("Device " + Name + " does not support rotation");
			}
			RotateCmd rotateCmd = RotateCmd.Create(cmds);
			rotateCmd.DeviceIndex = Index;
			await _handler.SendMessageExpectOk(rotateCmd).ConfigureAwait(continueOnCapturedContext: false);
		}

		public async Task LinearAsync(uint duration, double position)
		{
			if (!LinearAttributes.Any())
			{
				throw new ButtplugDeviceException("Device " + Name + " does not support linear position");
			}
			LinearCmd linearCmd = LinearCmd.Create(duration, position, (uint)LinearAttributes.Count);
			linearCmd.DeviceIndex = Index;
			await _handler.SendMessageExpectOk(linearCmd).ConfigureAwait(continueOnCapturedContext: false);
		}

		public async Task LinearAsync(IEnumerable<LinearCmd.VectorCommand> cmds)
		{
			if (!LinearAttributes.Any())
			{
				throw new ButtplugDeviceException("Device " + Name + " does not support linear position");
			}
			LinearCmd linearCmd = LinearCmd.Create(cmds);
			linearCmd.DeviceIndex = Index;
			await _handler.SendMessageExpectOk(linearCmd).ConfigureAwait(continueOnCapturedContext: false);
		}

		public List<SensorDeviceMessageAttributes> SensorReadAttributes(SensorType sensor)
		{
			if (MessageAttributes.SensorReadCmd != null)
			{
				return MessageAttributes.SensorReadCmd.Where((SensorDeviceMessageAttributes x) => x.SensorType == sensor).ToList();
			}
			return Enumerable.Empty<SensorDeviceMessageAttributes>().ToList();
		}

		public async Task<double> BatteryAsync()
		{
			if (!HasBattery)
			{
				throw new ButtplugDeviceException("Device " + Name + " does not have battery capabilities.");
			}
			ButtplugMessage buttplugMessage = await _handler.SendMessageAsync(new SensorReadCmd(Index, SensorReadAttributes(SensorType.Battery).ElementAt(0).Index, SensorType.Battery)).ConfigureAwait(continueOnCapturedContext: false);
			if (!(buttplugMessage is SensorReading sensorReading))
			{
				if (buttplugMessage is Error msg)
				{
					throw ButtplugException.FromError(msg);
				}
				throw new ButtplugMessageException("Message type " + buttplugMessage.Name + " not handled by BatteryAsync", buttplugMessage.Id);
			}
			return (double)sensorReading.data[0] / 100.0;
		}

		public async Task Stop()
		{
			await _handler.SendMessageExpectOk(new StopDeviceCmd(Index)).ConfigureAwait(continueOnCapturedContext: false);
		}
	}
	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 int _counter;

		private readonly ConcurrentDictionary<uint, TaskCompletionSource<ButtplugMessage>> _waitingMsgs = new ConcurrentDictionary<uint, TaskCompletionSource<ButtplugMessage>>();

		public uint NextMsgId => Convert.ToUInt32(Interlocked.Increment(ref _counter));

		public Task<ButtplugMessage> PrepareMessage(ButtplugMessage msg)
		{
			msg.Id = NextMsgId;
			TaskCompletionSource<ButtplugMessage> taskCompletionSource = new TaskCompletionSource<ButtplugMessage>();
			_waitingMsgs.TryAdd(msg.Id, taskCompletionSource);
			return taskCompletionSource.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);
			}
			if (msg is Error msg2)
			{
				value.SetException(ButtplugException.FromError(msg2));
			}
			else
			{
				value.SetResult(msg);
			}
		}

		protected virtual void Dispose(bool disposing)
		{
			foreach (TaskCompletionSource<ButtplugMessage> value in _waitingMsgs.Values)
			{
				value.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)
		{
			Task<ButtplugMessage> promise = _msgSorter.PrepareMessage(msg);
			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;

		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);
			}
			_readTask = Task.Run(async delegate
			{
				await RunClientLoop(token).ConfigureAwait(continueOnCapturedContext: false);
			}, token);
		}

		public async Task DisconnectAsync(CancellationToken token = default(CancellationToken))
		{
			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;
			await _readTask.ConfigureAwait(continueOnCapturedContext: false);
		}

		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);
			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 @string = Encoding.Default.GetString(buff, 0, webSocketReceiveResult.Count);
						ReceiveMessages(@string);
					}
				}
			}
			catch (Exception)
			{
			}
			finally
			{
				if (_wsClient != null)
				{
					_wsClient.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", token).Dispose();
					_wsClient = null;
				}
				_owningDispatcher.Send(delegate
				{
					Dispose();
				}, null);
				_owningDispatcher.Send(delegate
				{
					this.Disconnected?.Invoke(this, EventArgs.Empty);
				}, null);
			}
		}
	}
	public class DeviceAddedEventArgs
	{
		public readonly ButtplugClientDevice Device;

		public DeviceAddedEventArgs(ButtplugClientDevice device)
		{
			Device = device;
		}
	}
	public class DeviceRemovedEventArgs
	{
		public readonly ButtplugClientDevice Device;

		public DeviceRemovedEventArgs(ButtplugClientDevice device)
		{
			Device = device;
		}
	}
	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));
	}
}

plugins/RumbleRain/RumbleRain.dll

Decompiled 2 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Buttplug.Client;
using Buttplug.Core;
using R2API.Utils;
using RiskOfOptions;
using RiskOfOptions.OptionConfigs;
using RiskOfOptions.Options;
using RoR2;
using UnityEngine;
using UnityEngine.Events;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("RumbleRain")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+07ff99695129e2116e6c32ba63c51895dd52eeb1")]
[assembly: AssemblyProduct("RumbleRain")]
[assembly: AssemblyTitle("RumbleRain")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace RumbleRain;

internal static class ConfigManager
{
	internal static ConfigFile VibrationConfigFile { get; set; }

	internal static ConfigEntry<string> ServerUri { get; set; }

	internal static ConfigEntry<float> PollingRateSeconds { get; set; }

	internal static ConfigEntry<bool> IsRewardingEnabled { get; set; }

	internal static ConfigEntry<bool> IsPunishingEnabled { get; set; }

	internal static ConfigEntry<bool> IsMinionRewardingEnabled { get; set; }

	internal static ConfigEntry<bool> IsMinionPunishingEnabled { get; set; }

	internal static ConfigEntry<float> DamageDealtBaseVibrationIntensity { get; set; }

	internal static ConfigEntry<float> DamageReceivedBaseVibrationIntensity { get; set; }

	internal static ConfigEntry<float> MaximumVibrationIntensity { get; set; }

	internal static ConfigEntry<float> BaseVibrationDurationSeconds { get; set; }

	internal static ConfigEntry<float> MaximumVibrationDurationSeconds { get; set; }

	internal static ConfigEntry<VibrationInfoProvider.VibrationBehavior> VibrationBehavior { get; set; }

	internal static ConfigEntry<bool> AllowExcessDamage { get; set; }

	internal static ConfigEntry<KeyboardShortcut> ToggleVibrationKeybind { get; set; }

	static ConfigManager()
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Expected O, but got Unknown
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Expected O, but got Unknown
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_008e: Expected O, but got Unknown
		//IL_0098: Unknown result type (might be due to invalid IL or missing references)
		//IL_009d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: Expected O, but got Unknown
		//IL_00be: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: Expected O, but got Unknown
		//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Expected O, but got Unknown
		//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Expected O, but got Unknown
		//IL_011a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0124: Expected O, but got Unknown
		//IL_0148: Unknown result type (might be due to invalid IL or missing references)
		//IL_0152: Expected O, but got Unknown
		//IL_0176: Unknown result type (might be due to invalid IL or missing references)
		//IL_0180: Expected O, but got Unknown
		//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ae: Expected O, but got Unknown
		//IL_01db: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e5: Expected O, but got Unknown
		//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_020a: Unknown result type (might be due to invalid IL or missing references)
		//IL_021a: Expected O, but got Unknown
		//IL_0215: Unknown result type (might be due to invalid IL or missing references)
		//IL_021f: Expected O, but got Unknown
		//IL_024c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0256: Expected O, but got Unknown
		//IL_0260: Unknown result type (might be due to invalid IL or missing references)
		//IL_0265: Unknown result type (might be due to invalid IL or missing references)
		//IL_0270: Unknown result type (might be due to invalid IL or missing references)
		//IL_027b: Unknown result type (might be due to invalid IL or missing references)
		//IL_028b: Expected O, but got Unknown
		//IL_0286: Unknown result type (might be due to invalid IL or missing references)
		//IL_0290: Expected O, but got Unknown
		//IL_02bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c7: Expected O, but got Unknown
		//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_02fc: 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_0340: Unknown result type (might be due to invalid IL or missing references)
		//IL_034a: Expected O, but got Unknown
		//IL_0354: Unknown result type (might be due to invalid IL or missing references)
		//IL_0359: Unknown result type (might be due to invalid IL or missing references)
		//IL_0364: Unknown result type (might be due to invalid IL or missing references)
		//IL_0381: Unknown result type (might be due to invalid IL or missing references)
		//IL_0391: Expected O, but got Unknown
		//IL_038c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0396: Expected O, but got Unknown
		//IL_03c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_03cd: Expected O, but got Unknown
		//IL_03d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_03dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_03e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_03f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0402: Expected O, but got Unknown
		//IL_03fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0407: Expected O, but got Unknown
		//IL_0445: Unknown result type (might be due to invalid IL or missing references)
		//IL_044f: Expected O, but got Unknown
		//IL_0473: Unknown result type (might be due to invalid IL or missing references)
		//IL_047d: Expected O, but got Unknown
		//IL_04a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_04bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_04c5: Expected O, but got Unknown
		VibrationConfigFile = new ConfigFile(Paths.ConfigPath + "\\RumbleRain.cfg", true);
		ModSettingsManager.SetModDescription("Vibrates BPio-capable devices in response to in-game damage events.");
		ServerUri = VibrationConfigFile.Bind<string>("Devices", "Server Uri", "ws://localhost:12345", "URI of the Intiface server.");
		ModSettingsManager.AddOption((BaseOption)new StringInputFieldOption(ServerUri, true));
		PollingRateSeconds = VibrationConfigFile.Bind<float>("Devices", "Polling Rate Seconds", 0.2f, new ConfigDescription("How often to update vibrations.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 3f), Array.Empty<object>()));
		ModSettingsManager.AddOption((BaseOption)new StepSliderOption(PollingRateSeconds, new StepSliderConfig
		{
			min = 0.1f,
			max = 3f,
			increment = 0.1f
		}));
		ModSettingsManager.AddOption((BaseOption)new GenericButtonOption("Connect Devices", "Devices", "Attempts to connect to the Intiface server.", "Connect", (UnityAction)delegate
		{
			RumbleRain.DeviceManager.ConnectDevices();
		}));
		IsRewardingEnabled = VibrationConfigFile.Bind<bool>("Activated By", "Dealing Damage", false, "Receive vibrations when dealing damage.");
		ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(IsRewardingEnabled));
		IsPunishingEnabled = VibrationConfigFile.Bind<bool>("Activated By", "Receiving Damage", true, "Receive vibrations when receiving damage.");
		ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(IsPunishingEnabled));
		IsMinionRewardingEnabled = VibrationConfigFile.Bind<bool>("Activated By", "Minions Dealing Damage", false, "Receive vibrations when your minions (drones, turrets, etc.) deal damage.");
		ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(IsMinionRewardingEnabled));
		IsMinionPunishingEnabled = VibrationConfigFile.Bind<bool>("Activated By", "Minions Receiving Damage", false, "Receive vibrations when your minions (drones, turrets, etc.) receive damage.");
		ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(IsMinionPunishingEnabled));
		DamageDealtBaseVibrationIntensity = VibrationConfigFile.Bind<float>("Vibration Values", "Damage Dealt Base Vibration Intensity", 0.05f, new ConfigDescription("Base vibration intensity percentage for vibrations generated by dealing damage. Multiplied by (damageDealt / maxHealthOfDamagedEntity).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
		ModSettingsManager.AddOption((BaseOption)new StepSliderOption(DamageDealtBaseVibrationIntensity, new StepSliderConfig
		{
			min = 0f,
			max = 1f,
			increment = 0.01f
		}));
		DamageReceivedBaseVibrationIntensity = VibrationConfigFile.Bind<float>("Vibration Values", "Damage Received Base Vibration Intensity", 1f, new ConfigDescription("Base vibration intensity percentage for vibrations generated by receiving damage. Multiplied by (damageDealt / maxHealthOfDamagedEntity).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
		ModSettingsManager.AddOption((BaseOption)new StepSliderOption(DamageReceivedBaseVibrationIntensity, new StepSliderConfig
		{
			min = 0f,
			max = 1f,
			increment = 0.01f
		}));
		MaximumVibrationIntensity = VibrationConfigFile.Bind<float>("Vibration Values", "Maximum Vibration Intensity", 1f, new ConfigDescription("Max percentage of total vibration intensity expressed as a decimal.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
		ModSettingsManager.AddOption((BaseOption)new StepSliderOption(MaximumVibrationIntensity, new StepSliderConfig
		{
			min = 0f,
			max = 1f,
			increment = 0.01f
		}));
		MaximumVibrationDurationSeconds = VibrationConfigFile.Bind<float>("Vibration Values", "Maximum Vibration Duration Seconds", 20f, new ConfigDescription("The longest amount of seconds vibrations will last for assuming no new vibrations are generated.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, (float)TimeSpan.FromMinutes(5.0).TotalSeconds), Array.Empty<object>()));
		ModSettingsManager.AddOption((BaseOption)new StepSliderOption(MaximumVibrationDurationSeconds, new StepSliderConfig
		{
			min = 0f,
			max = (float)TimeSpan.FromMinutes(5.0).TotalSeconds,
			increment = 0.5f
		}));
		BaseVibrationDurationSeconds = VibrationConfigFile.Bind<float>("Vibration Values", "Base Vibration Duration Seconds", 5f, new ConfigDescription("The base vibration duration in seconds to add when damage is dealt or received. Multiplied by (damageDealt / maxHealthOfDamagedEntity).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
		ModSettingsManager.AddOption((BaseOption)new StepSliderOption(BaseVibrationDurationSeconds, new StepSliderConfig
		{
			min = 0f,
			max = 100f,
			increment = 0.5f
		}));
		VibrationBehavior = VibrationConfigFile.Bind<VibrationInfoProvider.VibrationBehavior>("Vibration Behavior", "Vibration Behavior", VibrationInfoProvider.VibrationBehavior.AdditiveWithLinearDecay, "How vibrations should handle new information and evolve over time.");
		VibrationBehavior.SettingChanged += delegate
		{
			RumbleRain.DeviceManager.InfoProvider = VibrationInfoProvider.From(VibrationBehavior.Value);
		};
		ModSettingsManager.AddOption((BaseOption)new ChoiceOption((ConfigEntryBase)(object)VibrationBehavior));
		AllowExcessDamage = VibrationConfigFile.Bind<bool>("Vibration Behavior", "Allow For Excess Damage", false, "Allow for excess damage dealt over an entity's max combined health to affect vibrations.");
		ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(AllowExcessDamage));
		ConfigFile vibrationConfigFile = VibrationConfigFile;
		KeyCode[] array = new KeyCode[3];
		RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
		ToggleVibrationKeybind = vibrationConfigFile.Bind<KeyboardShortcut>("Keybinds", "Toggle Devices", new KeyboardShortcut((KeyCode)306, (KeyCode[])(object)array), "This keybind will toggle devices to be active or not. See the Properties section at https://docs.unity3d.com/ScriptReference/KeyCode.html for expected values.");
		ModSettingsManager.AddOption((BaseOption)new KeyBindOption(ToggleVibrationKeybind));
	}
}
internal class DeviceManager
{
	private enum DeviceState
	{
		Active,
		Inactive,
		Paused
	}

	private DeviceState _state;

	private VibrationInfoProvider _infoProvider;

	private DeviceState State
	{
		get
		{
			return _state;
		}
		set
		{
			_state = value;
		}
	}

	private ButtplugClient ButtplugClient { get; set; }

	private List<ButtplugClientDevice> ConnectedDevices { get; set; }

	internal VibrationInfoProvider InfoProvider
	{
		get
		{
			return _infoProvider;
		}
		set
		{
			StopConnectedDevices();
			_infoProvider = value;
		}
	}

	public DeviceManager(VibrationInfoProvider vibrationInfoProvider, string clientName)
	{
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Expected O, but got Unknown
		State = DeviceState.Inactive;
		ConnectedDevices = new List<ButtplugClientDevice>();
		ButtplugClient = new ButtplugClient(clientName);
		Log.Info("BP client created for " + clientName);
		ButtplugClient.DeviceAdded += HandleDeviceAdded;
		ButtplugClient.DeviceRemoved += HandleDeviceRemoved;
		InfoProvider = vibrationInfoProvider;
	}

	public async void ConnectDevices()
	{
		if (ButtplugClient.Connected)
		{
			return;
		}
		try
		{
			Log.Info("Attempting to connect to Intiface server at \"" + ConfigManager.ServerUri.Value + "\"");
			await ButtplugClient.ConnectAsync((IButtplugClientConnector)new ButtplugWebsocketConnector(new Uri(ConfigManager.ServerUri.Value)), default(CancellationToken));
			Log.Info("Connection successful. Beginning scan for devices");
			await ButtplugClient.StartScanningAsync(default(CancellationToken));
		}
		catch (ButtplugException)
		{
			Log.Error("Attempt to connect to devices failed. Ensure Intiface is running and attempt to reconnect from the 'Devices' section in the mod's in-game settings.");
		}
	}

	internal IEnumerator PollVibrations()
	{
		Log.Info($"Beginning polling every {ConfigManager.PollingRateSeconds.Value} seconds");
		while (true)
		{
			float secondsToWaitFor = ConfigManager.PollingRateSeconds.Value;
			yield return (object)new WaitForSeconds(secondsToWaitFor);
			if (ButtplugClient.Connected && State == DeviceState.Active)
			{
				if (InfoProvider.Info.IsImpotent() && State == DeviceState.Active)
				{
					StopConnectedDevices();
					continue;
				}
				InfoProvider.UpdateVibrationInfo(TimeSpan.FromSeconds(secondsToWaitFor));
				VibrateConnectedDevices(InfoProvider.Info.Intensity);
			}
		}
	}

	internal void SendVibrationInfo(VibrationInfo vibrationInfo)
	{
		if (State != DeviceState.Paused)
		{
			InfoProvider.Input(vibrationInfo);
			VibrateConnectedDevices(InfoProvider.Info.Intensity);
		}
	}

	private void VibrateConnectedDevices(float intensity)
	{
		State = DeviceState.Active;
		ConnectedDevices.ForEach(async delegate(ButtplugClientDevice device)
		{
			await device.VibrateAsync((double)intensity);
		});
	}

	private void StopConnectedDevices(DeviceState newState = DeviceState.Inactive)
	{
		if (newState == DeviceState.Active)
		{
			throw new ArgumentException(string.Format("{0}={1} is invalid. Expecting {2} or {3}.", "newState", newState, DeviceState.Inactive, DeviceState.Active));
		}
		State = newState;
		ConnectedDevices.ForEach(async delegate(ButtplugClientDevice device)
		{
			await device.Stop();
		});
	}

	internal void ToggleConnectedDevices()
	{
		switch (State)
		{
		case DeviceState.Active:
		case DeviceState.Inactive:
			StopConnectedDevices(DeviceState.Paused);
			break;
		case DeviceState.Paused:
			VibrateConnectedDevices(InfoProvider.Info.Intensity);
			break;
		}
	}

	internal void CleanUp()
	{
		StopConnectedDevices();
		InfoProvider.Reset();
	}

	private void HandleDeviceAdded(object sender, DeviceAddedEventArgs args)
	{
		if (!IsVibratableDevice(args.Device))
		{
			Log.Info(args.Device.Name + " was detected but ignored due to it not being vibratable.");
			return;
		}
		Log.Info(args.Device.Name + " connected to client " + ButtplugClient.Name);
		ConnectedDevices.Add(args.Device);
	}

	private void HandleDeviceRemoved(object sender, DeviceRemovedEventArgs args)
	{
		if (IsVibratableDevice(args.Device))
		{
			Log.Info(args.Device.Name + " disconnected from client " + ButtplugClient.Name);
			ConnectedDevices.Remove(args.Device);
		}
	}

	private bool IsVibratableDevice(ButtplugClientDevice device)
	{
		return device.VibrateAttributes.Count > 0;
	}
}
internal static class Log
{
	private static ManualLogSource Logger { get; set; }

	internal static void Init(ManualLogSource logger)
	{
		Logger = logger;
	}

	[Conditional("DEBUG")]
	internal static void Debug(object data)
	{
		Logger.LogDebug(data);
	}

	internal static void Info(object data)
	{
		Logger.LogInfo(data);
	}

	internal static void Message(object data)
	{
		Logger.LogMessage(data);
	}

	internal static void Warning(object data)
	{
		Logger.LogWarning(data);
	}

	internal static void Error(object data)
	{
		Logger.LogError(data);
	}

	internal static void Fatal(object data)
	{
		Logger.LogFatal(data);
	}
}
[BepInDependency(/*Could not decode attribute arguments.*/)]
[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
[BepInPlugin("quasikyo.RumbleRain", "RumbleRain", "0.5.0")]
public class RumbleRain : BaseUnityPlugin
{
	public const string PluginGUID = "quasikyo.RumbleRain";

	public const string PluginAuthor = "quasikyo";

	public const string PluginName = "RumbleRain";

	public const string PluginVersion = "0.5.0";

	internal static DeviceManager DeviceManager { get; private set; }

	public void Awake()
	{
		Log.Init(((BaseUnityPlugin)this).Logger);
		Log.Info("Performing setup for $RumbleRain");
		DeviceManager = new DeviceManager(VibrationInfoProvider.From(ConfigManager.VibrationBehavior.Value), "RumbleRain");
		DeviceManager.ConnectDevices();
		Run.onRunStartGlobal += delegate
		{
			((MonoBehaviour)this).StartCoroutine(DeviceManager.PollVibrations());
		};
		Run.onRunDestroyGlobal += delegate
		{
			((MonoBehaviour)this).StopAllCoroutines();
			DeviceManager.CleanUp();
		};
		GlobalEventManager.onClientDamageNotified += VibrateDevicesOnDamage;
	}

	public void Update()
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		KeyboardShortcut value = ConfigManager.ToggleVibrationKeybind.Value;
		if (((KeyboardShortcut)(ref value)).IsDown())
		{
			DeviceManager.ToggleConnectedDevices();
		}
	}

	private void VibrateDevicesOnDamage(DamageDealtMessage damageMessage)
	{
		CharacterMaster cachedMaster = LocalUserManager.GetFirstLocalUser().cachedMaster;
		CharacterBody body = cachedMaster.GetBody();
		GameObject victim = damageMessage.victim;
		CharacterBody val = ((victim != null) ? victim.GetComponent<CharacterBody>() : null);
		GameObject attacker = damageMessage.attacker;
		CharacterBody val2 = ((attacker != null) ? attacker.GetComponent<CharacterBody>() : null);
		if (!((Object)(object)val == (Object)null))
		{
			float fullCombinedHealth = val.healthComponent.fullCombinedHealth;
			float num = damageMessage.damage / fullCombinedHealth;
			if (!ConfigManager.AllowExcessDamage.Value)
			{
				num = Math.Min(num, 1f);
			}
			bool flag = (Object)(object)body == (Object)(object)val2;
			bool num2 = (Object)(object)body == (Object)(object)val;
			object obj;
			if (val2 == null)
			{
				obj = null;
			}
			else
			{
				CharacterMaster master = val2.master;
				obj = ((master != null) ? master.minionOwnership.ownerMaster : null);
			}
			bool flag2 = (Object)obj == (Object)(object)cachedMaster;
			CharacterMaster master2 = val.master;
			bool flag3 = (Object)(object)((master2 != null) ? master2.minionOwnership.ownerMaster : null) == (Object)(object)cachedMaster;
			VibrationInfo vibrationInfo = new VibrationInfo(TimeSpan.FromSeconds(ConfigManager.BaseVibrationDurationSeconds.Value * num));
			if ((flag && ConfigManager.IsRewardingEnabled.Value) || (flag2 && ConfigManager.IsMinionRewardingEnabled.Value))
			{
				vibrationInfo.Intensity = ConfigManager.DamageDealtBaseVibrationIntensity.Value * num;
				DeviceManager.SendVibrationInfo(vibrationInfo);
			}
			if ((num2 && ConfigManager.IsPunishingEnabled.Value) || (flag3 && ConfigManager.IsMinionPunishingEnabled.Value))
			{
				vibrationInfo.Intensity = ConfigManager.DamageReceivedBaseVibrationIntensity.Value * num;
				DeviceManager.SendVibrationInfo(vibrationInfo);
			}
		}
	}
}
internal class VibrationInfo
{
	private float _intensity;

	private TimeSpan _duration;

	private float _intensitySnapshot;

	private TimeSpan _durationSnapshot;

	protected internal float Intensity
	{
		get
		{
			return _intensity;
		}
		set
		{
			_intensity = Clamp(value, 0f, ConfigManager.MaximumVibrationIntensity.Value);
		}
	}

	protected internal TimeSpan Duration
	{
		get
		{
			return _duration;
		}
		set
		{
			_duration = Clamp(value, TimeSpan.Zero, TimeSpan.FromSeconds(ConfigManager.MaximumVibrationDurationSeconds.Value));
		}
	}

	protected internal float IntensitySnapshot
	{
		get
		{
			return _intensitySnapshot;
		}
		set
		{
			_intensitySnapshot = Clamp(value, 0f, ConfigManager.MaximumVibrationIntensity.Value);
		}
	}

	protected internal TimeSpan DurationSnapshot
	{
		get
		{
			return _durationSnapshot;
		}
		set
		{
			_durationSnapshot = Clamp(value, TimeSpan.Zero, TimeSpan.FromSeconds(ConfigManager.MaximumVibrationDurationSeconds.Value));
		}
	}

	internal VibrationInfo()
		: this(0f, TimeSpan.Zero)
	{
	}

	internal VibrationInfo(float intensity)
		: this(intensity, TimeSpan.Zero)
	{
	}

	internal VibrationInfo(TimeSpan duration)
		: this(0f, duration)
	{
	}

	internal VibrationInfo(float intensity, TimeSpan duration)
	{
		Intensity = intensity;
		Duration = duration;
		SnapshotValues();
	}

	private T Clamp<T>(T value, T inclusiveMinimum, T inclusiveMaximum) where T : IComparable<T>
	{
		if (value.CompareTo(inclusiveMinimum) < 0)
		{
			return inclusiveMinimum;
		}
		if (value.CompareTo(inclusiveMaximum) > 0)
		{
			return inclusiveMaximum;
		}
		return value;
	}

	public static VibrationInfo operator +(VibrationInfo a, VibrationInfo b)
	{
		return new VibrationInfo(a.Intensity + b.Intensity, a.Duration + b.Duration);
	}

	protected internal void SnapshotValues()
	{
		IntensitySnapshot = Intensity;
		DurationSnapshot = Duration;
	}

	protected internal bool IsImpotent()
	{
		if (!((double)Intensity < 0.01))
		{
			return Duration < TimeSpan.FromSeconds(1.0);
		}
		return true;
	}

	public override string ToString()
	{
		return $"{base.ToString()}(intensity={Intensity}, duration={Duration.TotalSeconds} seconds)";
	}
}
internal abstract class VibrationInfoProvider
{
	internal enum VibrationBehavior
	{
		AdditiveWithLinearDecay,
		AdditiveWithExponentialDecay
	}

	protected internal VibrationInfo Info { get; set; }

	protected internal VibrationInfoProvider()
	{
		Info = new VibrationInfo();
	}

	protected internal static VibrationInfoProvider From(VibrationBehavior vibrationBehavior)
	{
		return vibrationBehavior switch
		{
			VibrationBehavior.AdditiveWithLinearDecay => new AdditiveInfoProviderWithLinearDecay(), 
			VibrationBehavior.AdditiveWithExponentialDecay => new AdditiveInfoProvierWithExponentialDecay(), 
			_ => throw new ArgumentException($"{vibrationBehavior} is not a valid behavior."), 
		};
	}

	protected internal abstract void Input(VibrationInfo newVibrationInfo);

	protected internal abstract void UpdateVibrationInfo(TimeSpan timeSinceLastUpdate);

	protected internal virtual void Reset()
	{
		Info = new VibrationInfo();
	}
}
internal class AdditiveInfoProviderWithLinearDecay : VibrationInfoProvider
{
	protected internal override void Input(VibrationInfo newVibrationInfo)
	{
		base.Info += newVibrationInfo;
		base.Info.SnapshotValues();
	}

	protected internal override void UpdateVibrationInfo(TimeSpan timeSinceLastUpdate)
	{
		base.Info.Duration -= timeSinceLastUpdate;
		double num = base.Info.Duration.TotalSeconds / base.Info.DurationSnapshot.TotalSeconds;
		base.Info.Intensity = base.Info.IntensitySnapshot * (float)num;
	}
}
internal class AdditiveInfoProvierWithExponentialDecay : VibrationInfoProvider
{
	protected internal override void Input(VibrationInfo newVibrationInfo)
	{
		base.Info += newVibrationInfo;
		base.Info.SnapshotValues();
	}

	protected internal override void UpdateVibrationInfo(TimeSpan timeSinceLastUpdate)
	{
		base.Info.Duration -= timeSinceLastUpdate;
		double num = base.Info.Duration.TotalSeconds / base.Info.DurationSnapshot.TotalSeconds;
		base.Info.Intensity *= (float)num;
	}
}