Decompiled source of H3VR SimHaptics v1.0.0
BepInEx/plugins/SimHaptics/Bhaptics.Tact.dll
Decompiled 12 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Net; using System.Net.Security; using System.Net.Sockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Security.Authentication; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Security.Permissions; using System.Security.Principal; using System.Text; using System.Threading; using System.Timers; using CustomWebSocketSharp; using CustomWebSocketSharp.Net; using CustomWebSocketSharp.Net.WebSockets; using CustomWebSocketSharp.Server; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("Bhaptics.Tact")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Bhaptics.Tact")] [assembly: AssemblyCopyright("Copyright © 2018")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("f6971bb6-19f6-4d65-855d-fafa09351643")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyVersion("1.0.0.0")] namespace CustomWebSocketSharp { public enum ByteOrder { Little, Big } public class CloseEventArgs : EventArgs { private bool _clean; private PayloadData _payloadData; internal PayloadData PayloadData => _payloadData; public ushort Code => _payloadData.Code; public string Reason => _payloadData.Reason ?? string.Empty; public bool WasClean { get { return _clean; } internal set { _clean = value; } } internal CloseEventArgs() { _payloadData = PayloadData.Empty; } internal CloseEventArgs(ushort code) : this(code, null) { } internal CloseEventArgs(CloseStatusCode code) : this((ushort)code, null) { } internal CloseEventArgs(PayloadData payloadData) { _payloadData = payloadData; } internal CloseEventArgs(ushort code, string reason) { _payloadData = new PayloadData(code, reason); } internal CloseEventArgs(CloseStatusCode code, string reason) : this((ushort)code, reason) { } } public enum CloseStatusCode : ushort { Normal = 1000, Away = 1001, ProtocolError = 1002, UnsupportedData = 1003, Undefined = 1004, NoStatus = 1005, Abnormal = 1006, InvalidData = 1007, PolicyViolation = 1008, TooBig = 1009, MandatoryExtension = 1010, ServerError = 1011, TlsHandshakeFailure = 1015 } public enum CompressionMethod : byte { None, Deflate } public class ErrorEventArgs : EventArgs { private Exception _exception; private string _message; public Exception Exception => _exception; public string Message => _message; internal ErrorEventArgs(string message) : this(message, null) { } internal ErrorEventArgs(string message, Exception exception) { _message = message; _exception = exception; } } public static class Ext { private static readonly byte[] _last = new byte[1]; private static readonly int _retry = 5; private const string _tspecials = "()<>@,;:\\\"/[]?={} \t"; private static byte[] compress(this byte[] data) { if (data.Length == 0) { return data; } using MemoryStream stream = new MemoryStream(data); return stream.compressToArray(); } private static MemoryStream compress(this Stream stream) { MemoryStream memoryStream = new MemoryStream(); if (stream.Length == 0L) { return memoryStream; } stream.Position = 0L; using DeflateStream deflateStream = new DeflateStream(memoryStream, CompressionMode.Compress, leaveOpen: true); CopyTo(stream, deflateStream, 1024); deflateStream.Close(); memoryStream.Write(_last, 0, 1); memoryStream.Position = 0L; return memoryStream; } private static byte[] compressToArray(this Stream stream) { using MemoryStream memoryStream = stream.compress(); memoryStream.Close(); return memoryStream.ToArray(); } private static byte[] decompress(this byte[] data) { if (data.LongLength == 0L) { return data; } using MemoryStream stream = new MemoryStream(data); return stream.decompressToArray(); } private static MemoryStream decompress(this Stream stream) { MemoryStream memoryStream = new MemoryStream(); if (stream.Length == 0L) { return memoryStream; } stream.Position = 0L; using DeflateStream source = new DeflateStream(stream, CompressionMode.Decompress, leaveOpen: true); CopyTo(source, memoryStream, 1024); memoryStream.Position = 0L; return memoryStream; } private static byte[] decompressToArray(this Stream stream) { using MemoryStream memoryStream = stream.decompress(); memoryStream.Close(); return memoryStream.ToArray(); } private static void times(this ulong n, Action action) { for (ulong num = 0uL; num < n; num++) { action(); } } internal static byte[] Append(this ushort code, string reason) { byte[] array = code.InternalToByteArray(ByteOrder.Big); if (reason != null && reason.Length > 0) { List<byte> list = new List<byte>(array); list.AddRange(Encoding.UTF8.GetBytes(reason)); array = list.ToArray(); } return array; } internal static string CheckIfAvailable(this ServerState state, bool ready, bool start, bool shutting) { if ((ready || (state != 0 && state != ServerState.Stop)) && (start || state != ServerState.Start) && (shutting || state != ServerState.ShuttingDown)) { return null; } return "This operation isn't available in: " + state.ToString().ToLower(); } internal static string CheckIfAvailable(this WebSocketState state, bool connecting, bool open, bool closing, bool closed) { if ((connecting || state != 0) && (open || state != WebSocketState.Open) && (closing || state != WebSocketState.Closing) && (closed || state != WebSocketState.Closed)) { return null; } return "This operation isn't available in: " + state.ToString().ToLower(); } internal static string CheckIfValidProtocols(this string[] protocols) { if (!protocols.Contains((string protocol) => protocol == null || protocol.Length == 0 || !protocol.IsToken())) { if (!protocols.ContainsTwice()) { return null; } return "Contains a value twice."; } return "Contains an invalid value."; } internal static string CheckIfValidServicePath(this string path) { if (path != null && path.Length != 0) { if (path[0] == '/') { if (path.IndexOfAny(new char[2] { '?', '#' }) <= -1) { return null; } return "'path' includes either or both query and fragment components."; } return "'path' isn't an absolute path."; } return "'path' is null or empty."; } internal static string CheckIfValidSessionID(this string id) { if (id != null && id.Length != 0) { return null; } return "'id' is null or empty."; } internal static string CheckIfValidWaitTime(this TimeSpan time) { if (!(time <= TimeSpan.Zero)) { return null; } return "A wait time is zero or less."; } internal static bool CheckWaitTime(this TimeSpan time, out string message) { message = null; if (time <= TimeSpan.Zero) { message = "A wait time is zero or less."; return false; } return true; } internal static void Close(this CustomWebSocketSharp.Net.HttpListenerResponse response, CustomWebSocketSharp.Net.HttpStatusCode code) { response.StatusCode = (int)code; response.OutputStream.Close(); } internal static void CloseWithAuthChallenge(this CustomWebSocketSharp.Net.HttpListenerResponse response, string challenge) { response.Headers.InternalSet("WWW-Authenticate", challenge, response: true); response.Close(CustomWebSocketSharp.Net.HttpStatusCode.Unauthorized); } internal static byte[] Compress(this byte[] data, CompressionMethod method) { if (method != CompressionMethod.Deflate) { return data; } return data.compress(); } internal static Stream Compress(this Stream stream, CompressionMethod method) { if (method != CompressionMethod.Deflate) { return stream; } return stream.compress(); } internal static byte[] CompressToArray(this Stream stream, CompressionMethod method) { if (method != CompressionMethod.Deflate) { return stream.ToByteArray(); } return stream.compressToArray(); } internal static bool Contains<T>(this IEnumerable<T> source, Func<T, bool> condition) { foreach (T item in source) { if (condition(item)) { return true; } } return false; } internal static bool ContainsTwice(this string[] values) { int len = values.Length; Func<int, bool> contains = null; contains = delegate(int idx) { if (idx < len - 1) { for (int i = idx + 1; i < len; i++) { if (values[i] == values[idx]) { return true; } } return contains(++idx); } return false; }; return contains(0); } internal static T[] Copy<T>(this T[] source, int length) { T[] array = new T[length]; Array.Copy(source, 0, array, 0, length); return array; } internal static T[] Copy<T>(this T[] source, long length) { T[] array = new T[length]; Array.Copy(source, 0L, array, 0L, length); return array; } internal static void CopyTo(this Stream source, Stream destination, int bufferLength) { byte[] buffer = new byte[bufferLength]; int num = 0; while ((num = source.Read(buffer, 0, bufferLength)) > 0) { destination.Write(buffer, 0, num); } } internal static void CopyToAsync(this Stream source, Stream destination, int bufferLength, Action completed, Action<Exception> error) { byte[] buff = new byte[bufferLength]; AsyncCallback callback = null; callback = delegate(IAsyncResult ar) { try { int num = source.EndRead(ar); if (num <= 0) { if (completed != null) { completed(); } } else { destination.Write(buff, 0, num); source.BeginRead(buff, 0, bufferLength, callback, null); } } catch (Exception obj2) { if (error != null) { error(obj2); } } }; try { source.BeginRead(buff, 0, bufferLength, callback, null); } catch (Exception obj) { if (error != null) { error(obj); } } } internal static byte[] Decompress(this byte[] data, CompressionMethod method) { if (method != CompressionMethod.Deflate) { return data; } return data.decompress(); } internal static Stream Decompress(this Stream stream, CompressionMethod method) { if (method != CompressionMethod.Deflate) { return stream; } return stream.decompress(); } internal static byte[] DecompressToArray(this Stream stream, CompressionMethod method) { if (method != CompressionMethod.Deflate) { return stream.ToByteArray(); } return stream.decompressToArray(); } internal static bool EqualsWith(this int value, char c, Action<int> action) { action(value); return value == c; } internal static string GetAbsolutePath(this Uri uri) { if (uri.IsAbsoluteUri) { return uri.AbsolutePath; } string originalString = uri.OriginalString; if (originalString[0] != '/') { return null; } int num = originalString.IndexOfAny(new char[2] { '?', '#' }); if (num <= 0) { return originalString; } return originalString.Substring(0, num); } internal static string GetMessage(this CloseStatusCode code) { return code switch { CloseStatusCode.TlsHandshakeFailure => "An error has occurred during a TLS handshake.", CloseStatusCode.ServerError => "WebSocket server got an internal error.", CloseStatusCode.MandatoryExtension => "WebSocket client didn't receive expected extension(s).", CloseStatusCode.TooBig => "A too big message has been received.", CloseStatusCode.PolicyViolation => "A policy violation has occurred.", CloseStatusCode.InvalidData => "Invalid data has been received.", CloseStatusCode.Abnormal => "An exception has occurred.", CloseStatusCode.UnsupportedData => "Unsupported data has been received.", CloseStatusCode.ProtocolError => "A WebSocket protocol error has occurred.", _ => string.Empty, }; } internal static string GetName(this string nameAndValue, char separator) { int num = nameAndValue.IndexOf(separator); if (num <= 0) { return null; } return nameAndValue.Substring(0, num).Trim(); } internal static string GetValue(this string nameAndValue, char separator) { int num = nameAndValue.IndexOf(separator); if (num <= -1 || num >= nameAndValue.Length - 1) { return null; } return nameAndValue.Substring(num + 1).Trim(); } internal static string GetValue(this string nameAndValue, char separator, bool unquote) { int num = nameAndValue.IndexOf(separator); if (num < 0 || num == nameAndValue.Length - 1) { return null; } string text = nameAndValue.Substring(num + 1).Trim(); if (!unquote) { return text; } return text.Unquote(); } internal static TcpListenerWebSocketContext GetWebSocketContext(this TcpClient tcpClient, string protocol, bool secure, ServerSslConfiguration sslConfig, Logger logger) { return new TcpListenerWebSocketContext(tcpClient, protocol, secure, sslConfig, logger); } internal static byte[] InternalToByteArray(this ushort value, ByteOrder order) { byte[] bytes = BitConverter.GetBytes(value); if (!order.IsHostOrder()) { Array.Reverse((Array)bytes); } return bytes; } internal static byte[] InternalToByteArray(this ulong value, ByteOrder order) { byte[] bytes = BitConverter.GetBytes(value); if (!order.IsHostOrder()) { Array.Reverse((Array)bytes); } return bytes; } internal static bool IsCompressionExtension(this string value, CompressionMethod method) { return value.StartsWith(method.ToExtensionString()); } internal static bool IsControl(this byte opcode) { if (opcode > 7) { return opcode < 16; } return false; } internal static bool IsControl(this Opcode opcode) { return (int)opcode >= 8; } internal static bool IsData(this byte opcode) { if (opcode != 1) { return opcode == 2; } return true; } internal static bool IsData(this Opcode opcode) { if (opcode != Opcode.Text) { return opcode == Opcode.Binary; } return true; } internal static bool IsPortNumber(this int value) { if (value > 0) { return value < 65536; } return false; } internal static bool IsReserved(this ushort code) { if (code != 1004 && code != 1005 && code != 1006) { return code == 1015; } return true; } internal static bool IsReserved(this CloseStatusCode code) { if (code != CloseStatusCode.Undefined && code != CloseStatusCode.NoStatus && code != CloseStatusCode.Abnormal) { return code == CloseStatusCode.TlsHandshakeFailure; } return true; } internal static bool IsSupported(this byte opcode) { return Enum.IsDefined(typeof(Opcode), opcode); } internal static bool IsText(this string value) { int length = value.Length; for (int i = 0; i < length; i++) { char c = value[i]; if (c < ' ' && !Contains("\r\n\t", c)) { return false; } switch (c) { case '\u007f': return false; case '\n': if (++i < length) { c = value[i]; if (!Contains(" \t", c)) { return false; } } break; } } return true; } internal static bool IsToken(this string value) { foreach (char c in value) { if (c < ' ' || c >= '\u007f' || Contains("()<>@,;:\\\"/[]?={} \t", c)) { return false; } } return true; } internal static string Quote(this string value) { return string.Format("\"{0}\"", value.Replace("\"", "\\\"")); } internal static byte[] ReadBytes(this Stream stream, int length) { byte[] array = new byte[length]; int num = 0; try { int num2 = 0; while (length > 0) { num2 = stream.Read(array, num, length); if (num2 != 0) { num += num2; length -= num2; continue; } break; } } catch { } return array.SubArray(0, num); } internal static byte[] ReadBytes(this Stream stream, long length, int bufferLength) { using MemoryStream memoryStream = new MemoryStream(); try { byte[] buffer = new byte[bufferLength]; int num = 0; while (length > 0) { if (length < bufferLength) { bufferLength = (int)length; } num = stream.Read(buffer, 0, bufferLength); if (num != 0) { memoryStream.Write(buffer, 0, num); length -= num; continue; } break; } } catch { } memoryStream.Close(); return memoryStream.ToArray(); } internal static void ReadBytesAsync(this Stream stream, int length, Action<byte[]> completed, Action<Exception> error) { byte[] buff = new byte[length]; int offset = 0; int retry = 0; AsyncCallback callback = null; callback = delegate(IAsyncResult ar) { try { int num = stream.EndRead(ar); if (num == 0 && retry < _retry) { retry++; stream.BeginRead(buff, offset, length, callback, null); } else if (num == 0 || num == length) { if (completed != null) { completed(buff.SubArray(0, offset + num)); } } else { retry = 0; offset += num; length -= num; stream.BeginRead(buff, offset, length, callback, null); } } catch (Exception obj2) { if (error != null) { error(obj2); } } }; try { stream.BeginRead(buff, offset, length, callback, null); } catch (Exception obj) { if (error != null) { error(obj); } } } internal static void ReadBytesAsync(this Stream stream, long length, int bufferLength, Action<byte[]> completed, Action<Exception> error) { MemoryStream dest = new MemoryStream(); byte[] buff = new byte[bufferLength]; int retry = 0; Action<long> read = null; read = delegate(long len) { if (len < bufferLength) { bufferLength = (int)len; } stream.BeginRead(buff, 0, bufferLength, delegate(IAsyncResult ar) { try { int num = stream.EndRead(ar); if (num > 0) { dest.Write(buff, 0, num); } if (num == 0 && retry < _retry) { int num2 = retry; retry = num2 + 1; read(len); } else if (num == 0 || num == len) { if (completed != null) { dest.Close(); completed(dest.ToArray()); } dest.Dispose(); } else { retry = 0; read(len - num); } } catch (Exception obj2) { dest.Dispose(); if (error != null) { error(obj2); } } }, null); }; try { read(length); } catch (Exception obj) { dest.Dispose(); if (error != null) { error(obj); } } } internal static string RemovePrefix(this string value, params string[] prefixes) { int num = 0; foreach (string text in prefixes) { if (value.StartsWith(text)) { num = text.Length; break; } } if (num <= 0) { return value; } return value.Substring(num); } internal static T[] Reverse<T>(this T[] array) { int num = array.Length; T[] array2 = new T[num]; int num2 = num - 1; for (int i = 0; i <= num2; i++) { array2[i] = array[num2 - i]; } return array2; } internal static IEnumerable<string> SplitHeaderValue(this string value, params char[] separators) { int len = value.Length; string seps = new string(separators); StringBuilder buff = new StringBuilder(32); bool escaped = false; bool quoted = false; for (int i = 0; i < len; i++) { char c = value[i]; switch (c) { case '"': if (escaped) { escaped = !escaped; } else { quoted = !quoted; } break; case '\\': if (i < len - 1 && value[i + 1] == '"') { escaped = true; } break; default: if (Contains(seps, c) && !quoted) { yield return buff.ToString(); buff.Length = 0; continue; } break; } buff.Append(c); } if (buff.Length > 0) { yield return buff.ToString(); } } internal static byte[] ToByteArray(this Stream stream) { using MemoryStream memoryStream = new MemoryStream(); stream.Position = 0L; CopyTo(stream, memoryStream, 1024); memoryStream.Close(); return memoryStream.ToArray(); } internal static CompressionMethod ToCompressionMethod(this string value) { foreach (CompressionMethod value2 in Enum.GetValues(typeof(CompressionMethod))) { if (value2.ToExtensionString() == value) { return value2; } } return CompressionMethod.None; } internal static string ToExtensionString(this CompressionMethod method, params string[] parameters) { if (method == CompressionMethod.None) { return string.Empty; } string text = $"permessage-{method.ToString().ToLower()}"; if (parameters == null || parameters.Length == 0) { return text; } return string.Format("{0}; {1}", text, parameters.ToString("; ")); } internal static IPAddress ToIPAddress(this string value) { if (value == null || value.Length == 0) { return null; } if (IPAddress.TryParse(value, out IPAddress address)) { return address; } try { return Dns.GetHostAddresses(value)[0]; } catch { return null; } } internal static List<TSource> ToList<TSource>(this IEnumerable<TSource> source) { return new List<TSource>(source); } internal static ushort ToUInt16(this byte[] source, ByteOrder sourceOrder) { return BitConverter.ToUInt16(source.ToHostOrder(sourceOrder), 0); } internal static ulong ToUInt64(this byte[] source, ByteOrder sourceOrder) { return BitConverter.ToUInt64(source.ToHostOrder(sourceOrder), 0); } internal static string TrimSlashFromEnd(this string value) { string text = value.TrimEnd(new char[1] { '/' }); if (text.Length <= 0) { return "/"; } return text; } internal static bool TryCreateWebSocketUri(this string uriString, out Uri result, out string message) { result = null; message = null; Uri uri = uriString.ToUri(); if (uri == null) { message = "An invalid URI string."; return false; } if (!uri.IsAbsoluteUri) { message = "A relative URI."; return false; } string scheme = uri.Scheme; if (!(scheme == "ws") && !(scheme == "wss")) { message = "The scheme part is not 'ws' or 'wss'."; return false; } int port = uri.Port; if (port == 0) { message = "The port part is zero."; return false; } if (uri.Fragment.Length > 0) { message = "It includes the fragment component."; return false; } result = ((port != -1) ? uri : new Uri(string.Format("{0}://{1}:{2}{3}", scheme, uri.Host, (scheme == "ws") ? 80 : 443, uri.PathAndQuery))); return true; } internal static bool TryGetUTF8DecodedString(this byte[] bytes, out string s) { s = null; try { s = Encoding.UTF8.GetString(bytes); } catch { return false; } return true; } internal static bool TryGetUTF8EncodedBytes(this string s, out byte[] bytes) { bytes = null; try { bytes = Encoding.UTF8.GetBytes(s); } catch { return false; } return true; } internal static bool TryOpenRead(this FileInfo fileInfo, out FileStream fileStream) { fileStream = null; try { fileStream = fileInfo.OpenRead(); } catch { return false; } return true; } internal static string Unquote(this string value) { int num = value.IndexOf('"'); if (num < 0) { return value; } int num2 = value.LastIndexOf('"') - num - 1; if (num2 >= 0) { if (num2 != 0) { return value.Substring(num + 1, num2).Replace("\\\"", "\""); } return string.Empty; } return value; } internal static string UTF8Decode(this byte[] bytes) { try { return Encoding.UTF8.GetString(bytes); } catch { return null; } } internal static byte[] UTF8Encode(this string s) { return Encoding.UTF8.GetBytes(s); } internal static void WriteBytes(this Stream stream, byte[] bytes, int bufferLength) { using MemoryStream source = new MemoryStream(bytes); CopyTo(source, stream, bufferLength); } internal static void WriteBytesAsync(this Stream stream, byte[] bytes, int bufferLength, Action completed, Action<Exception> error) { MemoryStream input = new MemoryStream(bytes); input.CopyToAsync(stream, bufferLength, delegate { if (completed != null) { completed(); } input.Dispose(); }, delegate(Exception ex) { input.Dispose(); if (error != null) { error(ex); } }); } public static bool Contains(this string value, params char[] chars) { if (chars != null && chars.Length != 0) { if (value != null && value.Length != 0) { return value.IndexOfAny(chars) > -1; } return false; } return true; } public static bool Contains(this NameValueCollection collection, string name) { if (collection == null || collection.Count <= 0) { return false; } return collection[name] != null; } public static bool Contains(this NameValueCollection collection, string name, string value) { if (collection == null || collection.Count == 0) { return false; } string text = collection[name]; if (text == null) { return false; } string[] array = text.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { if (array[i].Trim().Equals(value, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } public static void Emit(this EventHandler eventHandler, object sender, EventArgs e) { eventHandler?.Invoke(sender, e); } public static void Emit<TEventArgs>(this EventHandler<TEventArgs> eventHandler, object sender, TEventArgs e) where TEventArgs : EventArgs { eventHandler?.Invoke(sender, e); } public static CustomWebSocketSharp.Net.CookieCollection GetCookies(this NameValueCollection headers, bool response) { string name = (response ? "Set-Cookie" : "Cookie"); if (headers == null || !headers.Contains(name)) { return new CustomWebSocketSharp.Net.CookieCollection(); } return CustomWebSocketSharp.Net.CookieCollection.Parse(headers[name], response); } public static string GetDescription(this CustomWebSocketSharp.Net.HttpStatusCode code) { return ((int)code).GetStatusDescription(); } public static string GetStatusDescription(this int code) { return code switch { 100 => "Continue", 101 => "Switching Protocols", 102 => "Processing", 200 => "OK", 201 => "Created", 202 => "Accepted", 203 => "Non-Authoritative Information", 204 => "No Content", 205 => "Reset Content", 206 => "Partial Content", 207 => "Multi-Status", 300 => "Multiple Choices", 301 => "Moved Permanently", 302 => "Found", 303 => "See Other", 304 => "Not Modified", 305 => "Use Proxy", 307 => "Temporary Redirect", 400 => "Bad Request", 401 => "Unauthorized", 402 => "Payment Required", 403 => "Forbidden", 404 => "Not Found", 405 => "Method Not Allowed", 406 => "Not Acceptable", 407 => "Proxy Authentication Required", 408 => "Request Timeout", 409 => "Conflict", 410 => "Gone", 411 => "Length Required", 412 => "Precondition Failed", 413 => "Request Entity Too Large", 414 => "Request-Uri Too Long", 415 => "Unsupported Media Type", 416 => "Requested Range Not Satisfiable", 417 => "Expectation Failed", 422 => "Unprocessable Entity", 423 => "Locked", 424 => "Failed Dependency", 500 => "Internal Server Error", 501 => "Not Implemented", 502 => "Bad Gateway", 503 => "Service Unavailable", 504 => "Gateway Timeout", 505 => "Http Version Not Supported", 507 => "Insufficient Storage", _ => string.Empty, }; } public static bool IsCloseStatusCode(this ushort value) { if (value > 999) { return value < 5000; } return false; } public static bool IsEnclosedIn(this string value, char c) { if (value != null && value.Length > 1 && value[0] == c) { return value[value.Length - 1] == c; } return false; } public static bool IsHostOrder(this ByteOrder order) { return BitConverter.IsLittleEndian == (order == ByteOrder.Little); } public static bool IsLocal(this IPAddress address) { if (address == null) { return false; } if (address.Equals(IPAddress.Any)) { return true; } if (address.Equals(IPAddress.Loopback)) { return true; } if (Socket.OSSupportsIPv6) { if (address.Equals(IPAddress.IPv6Any)) { return true; } if (address.Equals(IPAddress.IPv6Loopback)) { return true; } } IPAddress[] hostAddresses = Dns.GetHostAddresses(Dns.GetHostName()); foreach (IPAddress obj in hostAddresses) { if (address.Equals(obj)) { return true; } } return false; } public static bool IsNullOrEmpty(this string value) { if (value != null) { return value.Length == 0; } return true; } public static bool IsPredefinedScheme(this string value) { if (value == null || value.Length < 2) { return false; } switch (value[0]) { case 'h': if (!(value == "http")) { return value == "https"; } return true; case 'w': if (!(value == "ws")) { return value == "wss"; } return true; case 'f': if (!(value == "file")) { return value == "ftp"; } return true; case 'g': return value == "gopher"; case 'm': return value == "mailto"; case 'n': { char c = value[1]; if (c != 'e') { return value == "nntp"; } if (!(value == "news") && !(value == "net.pipe")) { return value == "net.tcp"; } return true; } default: return false; } } public static bool IsUpgradeTo(this CustomWebSocketSharp.Net.HttpListenerRequest request, string protocol) { if (request == null) { throw new ArgumentNullException("request"); } if (protocol == null) { throw new ArgumentNullException("protocol"); } if (protocol.Length == 0) { throw new ArgumentException("An empty string.", "protocol"); } if (request.Headers.Contains("Upgrade", protocol)) { return request.Headers.Contains("Connection", "Upgrade"); } return false; } public static bool MaybeUri(this string value) { if (value == null || value.Length == 0) { return false; } int num = value.IndexOf(':'); if (num == -1) { return false; } if (num >= 10) { return false; } return value.Substring(0, num).IsPredefinedScheme(); } public static T[] SubArray<T>(this T[] array, int startIndex, int length) { int num; if (array == null || (num = array.Length) == 0) { return new T[0]; } if (startIndex < 0 || length <= 0 || startIndex + length > num) { return new T[0]; } if (startIndex == 0 && length == num) { return array; } T[] array2 = new T[length]; Array.Copy(array, startIndex, array2, 0, length); return array2; } public static T[] SubArray<T>(this T[] array, long startIndex, long length) { long num; if (array == null || (num = array.LongLength) == 0L) { return new T[0]; } if (startIndex < 0 || length <= 0 || startIndex + length > num) { return new T[0]; } if (startIndex == 0L && length == num) { return array; } T[] array2 = new T[length]; Array.Copy(array, startIndex, array2, 0L, length); return array2; } public static void Times(this int n, Action action) { if (n > 0 && action != null) { ((ulong)n).times(action); } } public static void Times(this long n, Action action) { if (n > 0 && action != null) { ((ulong)n).times(action); } } public static void Times(this uint n, Action action) { if (n != 0 && action != null) { times(n, action); } } public static void Times(this ulong n, Action action) { if (n != 0 && action != null) { n.times(action); } } public static void Times(this int n, Action<int> action) { if (n > 0 && action != null) { for (int i = 0; i < n; i++) { action(i); } } } public static void Times(this long n, Action<long> action) { if (n > 0 && action != null) { for (long num = 0L; num < n; num++) { action(num); } } } public static void Times(this uint n, Action<uint> action) { if (n != 0 && action != null) { for (uint num = 0u; num < n; num++) { action(num); } } } public static void Times(this ulong n, Action<ulong> action) { if (n != 0 && action != null) { for (ulong num = 0uL; num < n; num++) { action(num); } } } public static T To<T>(this byte[] source, ByteOrder sourceOrder) where T : struct { if (source == null) { throw new ArgumentNullException("source"); } if (source.Length == 0) { return default(T); } Type typeFromHandle = typeof(T); byte[] value = source.ToHostOrder(sourceOrder); if ((object)typeFromHandle != typeof(bool)) { if ((object)typeFromHandle != typeof(char)) { if ((object)typeFromHandle != typeof(double)) { if ((object)typeFromHandle != typeof(short)) { if ((object)typeFromHandle != typeof(int)) { if ((object)typeFromHandle != typeof(long)) { if ((object)typeFromHandle != typeof(float)) { if ((object)typeFromHandle != typeof(ushort)) { if ((object)typeFromHandle != typeof(uint)) { if ((object)typeFromHandle != typeof(ulong)) { return default(T); } return (T)(object)BitConverter.ToUInt64(value, 0); } return (T)(object)BitConverter.ToUInt32(value, 0); } return (T)(object)BitConverter.ToUInt16(value, 0); } return (T)(object)BitConverter.ToSingle(value, 0); } return (T)(object)BitConverter.ToInt64(value, 0); } return (T)(object)BitConverter.ToInt32(value, 0); } return (T)(object)BitConverter.ToInt16(value, 0); } return (T)(object)BitConverter.ToDouble(value, 0); } return (T)(object)BitConverter.ToChar(value, 0); } return (T)(object)BitConverter.ToBoolean(value, 0); } public static byte[] ToByteArray<T>(this T value, ByteOrder order) where T : struct { Type typeFromHandle = typeof(T); byte[] array = (((object)typeFromHandle == typeof(bool)) ? BitConverter.GetBytes((bool)(object)value) : (((object)typeFromHandle != typeof(byte)) ? (((object)typeFromHandle == typeof(char)) ? BitConverter.GetBytes((char)(object)value) : (((object)typeFromHandle == typeof(double)) ? BitConverter.GetBytes((double)(object)value) : (((object)typeFromHandle == typeof(short)) ? BitConverter.GetBytes((short)(object)value) : (((object)typeFromHandle == typeof(int)) ? BitConverter.GetBytes((int)(object)value) : (((object)typeFromHandle == typeof(long)) ? BitConverter.GetBytes((long)(object)value) : (((object)typeFromHandle == typeof(float)) ? BitConverter.GetBytes((float)(object)value) : (((object)typeFromHandle == typeof(ushort)) ? BitConverter.GetBytes((ushort)(object)value) : (((object)typeFromHandle == typeof(uint)) ? BitConverter.GetBytes((uint)(object)value) : (((object)typeFromHandle == typeof(ulong)) ? BitConverter.GetBytes((ulong)(object)value) : WebSocket.EmptyBytes))))))))) : new byte[1] { (byte)(object)value })); if (array.Length > 1 && !order.IsHostOrder()) { Array.Reverse((Array)array); } return array; } public static byte[] ToHostOrder(this byte[] source, ByteOrder sourceOrder) { if (source == null) { throw new ArgumentNullException("source"); } if (source.Length <= 1 || sourceOrder.IsHostOrder()) { return source; } return source.Reverse(); } public static string ToString<T>(this T[] array, string separator) { if (array == null) { throw new ArgumentNullException("array"); } int num = array.Length; if (num == 0) { return string.Empty; } if (separator == null) { separator = string.Empty; } StringBuilder buff = new StringBuilder(64); (num - 1).Times(delegate(int i) { buff.AppendFormat("{0}{1}", array[i].ToString(), separator); }); buff.Append(array[num - 1].ToString()); return buff.ToString(); } public static Uri ToUri(this string value) { Uri.TryCreate(value, value.MaybeUri() ? UriKind.Absolute : UriKind.Relative, out Uri result); return result; } public static string UrlDecode(this string value) { if (value == null || value.Length <= 0) { return value; } return HttpUtility.UrlDecode(value); } public static string UrlEncode(this string value) { if (value == null || value.Length <= 0) { return value; } return HttpUtility.UrlEncode(value); } public static void WriteContent(this CustomWebSocketSharp.Net.HttpListenerResponse response, byte[] content) { if (response == null) { throw new ArgumentNullException("response"); } if (content == null) { throw new ArgumentNullException("content"); } long num = content.LongLength; if (num == 0L) { response.Close(); return; } response.ContentLength64 = num; Stream outputStream = response.OutputStream; if (num <= int.MaxValue) { outputStream.Write(content, 0, (int)num); } else { outputStream.WriteBytes(content, 1024); } outputStream.Close(); } } internal enum Fin : byte { More, Final } internal abstract class HttpBase { private NameValueCollection _headers; private const int _headersMaxLength = 8192; private Version _version; internal byte[] EntityBodyData; protected const string CrLf = "\r\n"; public string EntityBody { get { if (EntityBodyData == null || EntityBodyData.LongLength == 0L) { return string.Empty; } Encoding encoding = null; string text = _headers["Content-Type"]; if (text != null && text.Length > 0) { encoding = HttpUtility.GetEncoding(text); } return (encoding ?? Encoding.UTF8).GetString(EntityBodyData); } } public NameValueCollection Headers => _headers; public Version ProtocolVersion => _version; protected HttpBase(Version version, NameValueCollection headers) { _version = version; _headers = headers; } private static byte[] readEntityBody(Stream stream, string length) { if (!long.TryParse(length, out var result)) { throw new ArgumentException("Cannot be parsed.", "length"); } if (result < 0) { throw new ArgumentOutOfRangeException("length", "Less than zero."); } if (result <= 1024) { if (result <= 0) { return null; } return stream.ReadBytes((int)result); } return stream.ReadBytes(result, 1024); } private static string[] readHeaders(Stream stream, int maxLength) { List<byte> buff = new List<byte>(); int cnt = 0; Action<int> action = delegate(int i) { if (i == -1) { throw new EndOfStreamException("The header cannot be read from the data source."); } buff.Add((byte)i); cnt++; }; bool flag = false; while (cnt < maxLength) { if (stream.ReadByte().EqualsWith('\r', action) && stream.ReadByte().EqualsWith('\n', action) && stream.ReadByte().EqualsWith('\r', action) && stream.ReadByte().EqualsWith('\n', action)) { flag = true; break; } } if (!flag) { throw new WebSocketException("The length of header part is greater than the max length."); } return Encoding.UTF8.GetString(buff.ToArray()).Replace("\r\n ", " ").Replace("\r\n\t", " ") .Split(new string[1] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); } protected static T Read<T>(Stream stream, Func<string[], T> parser, int millisecondsTimeout) where T : HttpBase { bool timeout = false; System.Threading.Timer timer = new System.Threading.Timer(delegate { timeout = true; stream.Close(); }, null, millisecondsTimeout, -1); T val = null; Exception ex = null; try { val = parser(readHeaders(stream, 8192)); string text = val.Headers["Content-Length"]; if (text != null && text.Length > 0) { val.EntityBodyData = readEntityBody(stream, text); } } catch (Exception ex2) { ex = ex2; } finally { timer.Change(-1, -1); timer.Dispose(); } string text2 = (timeout ? "A timeout has occurred while reading an HTTP request/response." : ((ex != null) ? "An exception has occurred while reading an HTTP request/response." : null)); if (text2 != null) { throw new WebSocketException(text2, ex); } return val; } public byte[] ToByteArray() { return Encoding.UTF8.GetBytes(ToString()); } } internal class HttpRequest : HttpBase { private string _method; private string _uri; private bool _websocketRequest; private bool _websocketRequestSet; public AuthenticationResponse AuthenticationResponse { get { string text = base.Headers["Authorization"]; if (text == null || text.Length <= 0) { return null; } return AuthenticationResponse.Parse(text); } } public CustomWebSocketSharp.Net.CookieCollection Cookies => base.Headers.GetCookies(response: false); public string HttpMethod => _method; public bool IsWebSocketRequest { get { if (!_websocketRequestSet) { NameValueCollection headers = base.Headers; _websocketRequest = _method == "GET" && base.ProtocolVersion > CustomWebSocketSharp.Net.HttpVersion.Version10 && headers.Contains("Upgrade", "websocket") && headers.Contains("Connection", "Upgrade"); _websocketRequestSet = true; } return _websocketRequest; } } public string RequestUri => _uri; private HttpRequest(string method, string uri, Version version, NameValueCollection headers) : base(version, headers) { _method = method; _uri = uri; } internal HttpRequest(string method, string uri) : this(method, uri, CustomWebSocketSharp.Net.HttpVersion.Version11, new NameValueCollection()) { base.Headers["User-Agent"] = "websocket-sharp/1.0"; } internal static HttpRequest CreateConnectRequest(Uri uri) { string dnsSafeHost = uri.DnsSafeHost; int port = uri.Port; string text = $"{dnsSafeHost}:{port}"; HttpRequest httpRequest = new HttpRequest("CONNECT", text); httpRequest.Headers["Host"] = ((port == 80) ? dnsSafeHost : text); return httpRequest; } internal static HttpRequest CreateWebSocketRequest(Uri uri) { HttpRequest httpRequest = new HttpRequest("GET", uri.PathAndQuery); NameValueCollection headers = httpRequest.Headers; int port = uri.Port; string scheme = uri.Scheme; headers["Host"] = (((port == 80 && scheme == "ws") || (port == 443 && scheme == "wss")) ? uri.DnsSafeHost : uri.Authority); headers["Upgrade"] = "websocket"; headers["Connection"] = "Upgrade"; return httpRequest; } internal HttpResponse GetResponse(Stream stream, int millisecondsTimeout) { byte[] array = ToByteArray(); stream.Write(array, 0, array.Length); return HttpBase.Read(stream, HttpResponse.Parse, millisecondsTimeout); } internal static HttpRequest Parse(string[] headerParts) { string[] array = headerParts[0].Split(new char[1] { ' ' }, 3); if (array.Length != 3) { throw new ArgumentException("Invalid request line: " + headerParts[0]); } CustomWebSocketSharp.Net.WebHeaderCollection webHeaderCollection = new CustomWebSocketSharp.Net.WebHeaderCollection(); for (int i = 1; i < headerParts.Length; i++) { webHeaderCollection.InternalSet(headerParts[i], response: false); } return new HttpRequest(array[0], array[1], new Version(array[2].Substring(5)), webHeaderCollection); } internal static HttpRequest Read(Stream stream, int millisecondsTimeout) { return HttpBase.Read(stream, Parse, millisecondsTimeout); } public void SetCookies(CustomWebSocketSharp.Net.CookieCollection cookies) { if (cookies == null || cookies.Count == 0) { return; } StringBuilder stringBuilder = new StringBuilder(64); foreach (CustomWebSocketSharp.Net.Cookie item in cookies.Sorted) { if (!item.Expired) { stringBuilder.AppendFormat("{0}; ", item.ToString()); } } int length = stringBuilder.Length; if (length > 2) { stringBuilder.Length = length - 2; base.Headers["Cookie"] = stringBuilder.ToString(); } } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(64); stringBuilder.AppendFormat("{0} {1} HTTP/{2}{3}", _method, _uri, base.ProtocolVersion, "\r\n"); NameValueCollection headers = base.Headers; string[] allKeys = headers.AllKeys; foreach (string text in allKeys) { stringBuilder.AppendFormat("{0}: {1}{2}", text, headers[text], "\r\n"); } stringBuilder.Append("\r\n"); string entityBody = base.EntityBody; if (entityBody.Length > 0) { stringBuilder.Append(entityBody); } return stringBuilder.ToString(); } } internal class HttpResponse : HttpBase { private string _code; private string _reason; public CustomWebSocketSharp.Net.CookieCollection Cookies => base.Headers.GetCookies(response: true); public bool HasConnectionClose => base.Headers.Contains("Connection", "close"); public bool IsProxyAuthenticationRequired => _code == "407"; public bool IsRedirect { get { if (!(_code == "301")) { return _code == "302"; } return true; } } public bool IsUnauthorized => _code == "401"; public bool IsWebSocketResponse { get { NameValueCollection headers = base.Headers; if (base.ProtocolVersion > CustomWebSocketSharp.Net.HttpVersion.Version10 && _code == "101" && headers.Contains("Upgrade", "websocket")) { return headers.Contains("Connection", "Upgrade"); } return false; } } public string Reason => _reason; public string StatusCode => _code; private HttpResponse(string code, string reason, Version version, NameValueCollection headers) : base(version, headers) { _code = code; _reason = reason; } internal HttpResponse(CustomWebSocketSharp.Net.HttpStatusCode code) : this(code, code.GetDescription()) { } internal HttpResponse(CustomWebSocketSharp.Net.HttpStatusCode code, string reason) : this(((int)code).ToString(), reason, CustomWebSocketSharp.Net.HttpVersion.Version11, new NameValueCollection()) { base.Headers["Server"] = "websocket-sharp/1.0"; } internal static HttpResponse CreateCloseResponse(CustomWebSocketSharp.Net.HttpStatusCode code) { HttpResponse httpResponse = new HttpResponse(code); httpResponse.Headers["Connection"] = "close"; return httpResponse; } internal static HttpResponse CreateUnauthorizedResponse(string challenge) { HttpResponse httpResponse = new HttpResponse(CustomWebSocketSharp.Net.HttpStatusCode.Unauthorized); httpResponse.Headers["WWW-Authenticate"] = challenge; return httpResponse; } internal static HttpResponse CreateWebSocketResponse() { HttpResponse httpResponse = new HttpResponse(CustomWebSocketSharp.Net.HttpStatusCode.SwitchingProtocols); NameValueCollection headers = httpResponse.Headers; headers["Upgrade"] = "websocket"; headers["Connection"] = "Upgrade"; return httpResponse; } internal static HttpResponse Parse(string[] headerParts) { string[] array = headerParts[0].Split(new char[1] { ' ' }, 3); if (array.Length != 3) { throw new ArgumentException("Invalid status line: " + headerParts[0]); } CustomWebSocketSharp.Net.WebHeaderCollection webHeaderCollection = new CustomWebSocketSharp.Net.WebHeaderCollection(); for (int i = 1; i < headerParts.Length; i++) { webHeaderCollection.InternalSet(headerParts[i], response: true); } return new HttpResponse(array[1], array[2], new Version(array[0].Substring(5)), webHeaderCollection); } internal static HttpResponse Read(Stream stream, int millisecondsTimeout) { return HttpBase.Read(stream, Parse, millisecondsTimeout); } public void SetCookies(CustomWebSocketSharp.Net.CookieCollection cookies) { if (cookies == null || cookies.Count == 0) { return; } NameValueCollection headers = base.Headers; foreach (CustomWebSocketSharp.Net.Cookie item in cookies.Sorted) { headers.Add("Set-Cookie", item.ToResponseString()); } } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(64); stringBuilder.AppendFormat("HTTP/{0} {1} {2}{3}", base.ProtocolVersion, _code, _reason, "\r\n"); NameValueCollection headers = base.Headers; string[] allKeys = headers.AllKeys; foreach (string text in allKeys) { stringBuilder.AppendFormat("{0}: {1}{2}", text, headers[text], "\r\n"); } stringBuilder.Append("\r\n"); string entityBody = base.EntityBody; if (entityBody.Length > 0) { stringBuilder.Append(entityBody); } return stringBuilder.ToString(); } } public class LogData { private StackFrame _caller; private DateTime _date; private LogLevel _level; private string _message; public StackFrame Caller => _caller; public DateTime Date => _date; public LogLevel Level => _level; public string Message => _message; internal LogData(LogLevel level, StackFrame caller, string message) { _level = level; _caller = caller; _message = message ?? string.Empty; _date = DateTime.Now; } public override string ToString() { string text = $"{_date}|{_level,-5}|"; MethodBase method = _caller.GetMethod(); Type declaringType = method.DeclaringType; string arg = $"{text}{declaringType.Name}.{method.Name}|"; string[] array = _message.Replace("\r\n", "\n").TrimEnd(new char[1] { '\n' }).Split(new char[1] { '\n' }); if (array.Length <= 1) { return $"{arg}{_message}"; } StringBuilder stringBuilder = new StringBuilder($"{arg}{array[0]}\n", 64); string format = $"{{0,{text.Length}}}{{1}}\n"; for (int i = 1; i < array.Length; i++) { stringBuilder.AppendFormat(format, "", array[i]); } stringBuilder.Length--; return stringBuilder.ToString(); } } public class Logger { private volatile string _file; private volatile LogLevel _level; private Action<LogData, string> _output; private object _sync; public string File { get { return _file; } set { lock (_sync) { _file = value; Warn($"The current path to the log file has been changed to {_file}."); } } } public LogLevel Level { get { return _level; } set { lock (_sync) { _level = value; Warn($"The current logging level has been changed to {_level}."); } } } public Action<LogData, string> Output { get { return _output; } set { lock (_sync) { _output = value ?? new Action<LogData, string>(defaultOutput); Warn("The current output action has been changed."); } } } public Logger() : this(LogLevel.Error, null, null) { } public Logger(LogLevel level) : this(level, null, null) { } public Logger(LogLevel level, string file, Action<LogData, string> output) { _level = level; _file = file; _output = output ?? new Action<LogData, string>(defaultOutput); _sync = new object(); } private static void defaultOutput(LogData data, string path) { string value = data.ToString(); Console.WriteLine(value); if (path != null && path.Length > 0) { writeToFile(value, path); } } private void output(string message, LogLevel level) { lock (_sync) { if (_level > level) { return; } LogData logData = null; try { logData = new LogData(level, new StackFrame(2, needFileInfo: true), message); _output(logData, _file); } catch (Exception ex) { logData = new LogData(LogLevel.Fatal, new StackFrame(0, needFileInfo: true), ex.Message); Console.WriteLine(logData.ToString()); } } } private static void writeToFile(string value, string path) { using StreamWriter writer = new StreamWriter(path, append: true); using TextWriter textWriter = TextWriter.Synchronized(writer); textWriter.WriteLine(value); } public void Debug(string message) { if (_level <= LogLevel.Debug) { output(message, LogLevel.Debug); } } public void Error(string message) { if (_level <= LogLevel.Error) { output(message, LogLevel.Error); } } public void Fatal(string message) { output(message, LogLevel.Fatal); } public void Info(string message) { if (_level <= LogLevel.Info) { output(message, LogLevel.Info); } } public void Trace(string message) { if (_level <= LogLevel.Trace) { output(message, LogLevel.Trace); } } public void Warn(string message) { if (_level <= LogLevel.Warn) { output(message, LogLevel.Warn); } } } public enum LogLevel { Trace, Debug, Info, Warn, Error, Fatal } internal enum Mask : byte { Off, On } public class MessageEventArgs : EventArgs { private string _data; private bool _dataSet; private Opcode _opcode; private byte[] _rawData; internal Opcode Opcode => _opcode; public string Data { get { setData(); return _data; } } public bool IsBinary => _opcode == Opcode.Binary; public bool IsPing => _opcode == Opcode.Ping; public bool IsText => _opcode == Opcode.Text; public byte[] RawData { get { setData(); return _rawData; } } internal MessageEventArgs(WebSocketFrame frame) { _opcode = frame.Opcode; _rawData = frame.PayloadData.ApplicationData; } internal MessageEventArgs(Opcode opcode, byte[] rawData) { if ((ulong)rawData.LongLength > PayloadData.MaxLength) { throw new WebSocketException(CloseStatusCode.TooBig); } _opcode = opcode; _rawData = rawData; } private void setData() { if (!_dataSet) { if (_opcode == Opcode.Binary) { _dataSet = true; return; } _data = _rawData.UTF8Decode(); _dataSet = true; } } } internal enum Opcode : byte { Cont = 0, Text = 1, Binary = 2, Close = 8, Ping = 9, Pong = 10 } internal class PayloadData : IEnumerable<byte>, IEnumerable { private ushort _code; private bool _codeSet; private byte[] _data; private long _extDataLength; private long _length; private string _reason; private bool _reasonSet; public static readonly PayloadData Empty; public static readonly ulong MaxLength; internal ushort Code { get { if (!_codeSet) { _code = (ushort)((_length > 1) ? _data.SubArray(0, 2).ToUInt16(ByteOrder.Big) : 1005); _codeSet = true; } return _code; } } internal long ExtensionDataLength { get { return _extDataLength; } set { _extDataLength = value; } } internal bool HasReservedCode { get { if (_length > 1) { return Code.IsReserved(); } return false; } } internal string Reason { get { if (!_reasonSet) { _reason = ((_length > 2) ? _data.SubArray(2L, _length - 2).UTF8Decode() : string.Empty); _reasonSet = true; } return _reason; } } public byte[] ApplicationData { get { if (_extDataLength <= 0) { return _data; } return _data.SubArray(_extDataLength, _length - _extDataLength); } } public byte[] ExtensionData { get { if (_extDataLength <= 0) { return WebSocket.EmptyBytes; } return _data.SubArray(0L, _extDataLength); } } public ulong Length => (ulong)_length; static PayloadData() { Empty = new PayloadData(); MaxLength = 9223372036854775807uL; } internal PayloadData() { _code = 1005; _reason = string.Empty; _data = WebSocket.EmptyBytes; _codeSet = true; _reasonSet = true; } internal PayloadData(byte[] data) : this(data, data.LongLength) { } internal PayloadData(byte[] data, long length) { _data = data; _length = length; } internal PayloadData(ushort code, string reason) { _code = code; _reason = reason ?? string.Empty; _data = code.Append(reason); _length = _data.LongLength; _codeSet = true; _reasonSet = true; } internal void Mask(byte[] key) { for (long num = 0L; num < _length; num++) { _data[num] ^= key[num % 4]; } } public IEnumerator<byte> GetEnumerator() { byte[] data = _data; for (int i = 0; i < data.Length; i++) { yield return data[i]; } } public byte[] ToArray() { return _data; } public override string ToString() { return BitConverter.ToString(_data); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } internal enum Rsv : byte { Off, On } public class WebSocket : IDisposable { private AuthenticationChallenge _authChallenge; private string _base64Key; private bool _client; private Action _closeContext; private CompressionMethod _compression; private WebSocketContext _context; private CustomWebSocketSharp.Net.CookieCollection _cookies; private CustomWebSocketSharp.Net.NetworkCredential _credentials; private bool _emitOnPing; private bool _enableRedirection; private string _extensions; private bool _extensionsRequested; private object _forMessageEventQueue; private object _forPing; private object _forSend; private object _forState; private MemoryStream _fragmentsBuffer; private bool _fragmentsCompressed; private Opcode _fragmentsOpcode; private const string _guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; private Func<WebSocketContext, string> _handshakeRequestChecker; private bool _ignoreExtensions; private bool _inContinuation; private volatile bool _inMessage; private volatile Logger _logger; private static readonly int _maxRetryCountForConnect; private Action<MessageEventArgs> _message; private Queue<MessageEventArgs> _messageEventQueue; private uint _nonceCount; private string _origin; private ManualResetEvent _pongReceived; private bool _preAuth; private string _protocol; private string[] _protocols; private bool _protocolsRequested; private CustomWebSocketSharp.Net.NetworkCredential _proxyCredentials; private Uri _proxyUri; private volatile WebSocketState _readyState; private ManualResetEvent _receivingExited; private int _retryCountForConnect; private bool _secure; private ClientSslConfiguration _sslConfig; private Stream _stream; private TcpClient _tcpClient; private Uri _uri; private const string _version = "13"; private TimeSpan _waitTime; internal static readonly byte[] EmptyBytes; internal static readonly int FragmentLength; internal static readonly RandomNumberGenerator RandomNumber; internal CustomWebSocketSharp.Net.CookieCollection CookieCollection => _cookies; internal Func<WebSocketContext, string> CustomHandshakeRequestChecker { get { return _handshakeRequestChecker; } set { _handshakeRequestChecker = value; } } internal bool HasMessage { get { lock (_forMessageEventQueue) { return _messageEventQueue.Count > 0; } } } internal bool IgnoreExtensions { get { return _ignoreExtensions; } set { _ignoreExtensions = value; } } internal bool IsConnected { get { if (_readyState != WebSocketState.Open) { return _readyState == WebSocketState.Closing; } return true; } } public CompressionMethod Compression { get { return _compression; } set { if (!checkIfAvailable(client: true, server: false, connecting: true, open: false, closing: false, closed: true, out var text)) { _logger.Error(text); error("An error has occurred in setting the compression.", null); return; } lock (_forState) { if (!checkIfAvailable(connecting: true, open: false, closing: false, closed: true, out text)) { _logger.Error(text); error("An error has occurred in setting the compression.", null); } else { _compression = value; } } } } public IEnumerable<CustomWebSocketSharp.Net.Cookie> Cookies { get { lock (_cookies.SyncRoot) { foreach (CustomWebSocketSharp.Net.Cookie cookie in _cookies) { yield return cookie; } } } } public CustomWebSocketSharp.Net.NetworkCredential Credentials => _credentials; public bool EmitOnPing { get { return _emitOnPing; } set { _emitOnPing = value; } } public bool EnableRedirection { get { return _enableRedirection; } set { lock (_forState) { if (!checkIfAvailable(client: true, server: false, connecting: true, open: false, closing: false, closed: true, out var text)) { _logger.Error(text); error("An error has occurred in setting the enable redirection.", null); } else { _enableRedirection = value; } } } } public string Extensions => _extensions ?? string.Empty; public bool IsAlive => ping(EmptyBytes); public bool IsSecure => _secure; public Logger Log { get { return _logger; } internal set { _logger = value; } } public string Origin { get { return _origin; } set { lock (_forState) { Uri result; if (!checkIfAvailable(client: true, server: false, connecting: true, open: false, closing: false, closed: true, out var text)) { _logger.Error(text); error("An error has occurred in setting the origin.", null); } else if (value.IsNullOrEmpty()) { _origin = value; } else if (!Uri.TryCreate(value, UriKind.Absolute, out result) || result.Segments.Length > 1) { _logger.Error("The syntax of an origin must be '<scheme>://<host>[:<port>]'."); error("An error has occurred in setting the origin.", null); } else { _origin = value.TrimEnd(new char[1] { '/' }); } } } } public string Protocol { get { return _protocol ?? string.Empty; } internal set { _protocol = value; } } public WebSocketState ReadyState => _readyState; public ClientSslConfiguration SslConfiguration { get { if (!_client) { return null; } return _sslConfig ?? (_sslConfig = new ClientSslConfiguration(_uri.DnsSafeHost)); } set { lock (_forState) { if (!checkIfAvailable(client: true, server: false, connecting: true, open: false, closing: false, closed: true, out var text)) { _logger.Error(text); error("An error has occurred in setting the ssl configuration.", null); } else { _sslConfig = value; } } } } public Uri Url { get { if (!_client) { return _context.RequestUri; } return _uri; } } public TimeSpan WaitTime { get { return _waitTime; } set { lock (_forState) { if (!checkIfAvailable(client: true, server: true, connecting: true, open: false, closing: false, closed: true, out var text) || !value.CheckWaitTime(out text)) { _logger.Error(text); error("An error has occurred in setting the wait time.", null); } else { _waitTime = value; } } } } public event EventHandler<CloseEventArgs> OnClose; public event EventHandler<ErrorEventArgs> OnError; public event EventHandler<MessageEventArgs> OnMessage; public event EventHandler OnOpen; static WebSocket() { _maxRetryCountForConnect = 10; EmptyBytes = new byte[0]; FragmentLength = 1016; RandomNumber = new RNGCryptoServiceProvider(); } internal WebSocket(HttpListenerWebSocketContext context, string protocol) { _context = context; _protocol = protocol; _closeContext = context.Close; _logger = context.Log; _message = messages; _secure = context.IsSecureConnection; _stream = context.Stream; _waitTime = TimeSpan.FromSeconds(1.0); init(); } internal WebSocket(TcpListenerWebSocketContext context, string protocol) { _context = context; _protocol = protocol; _closeContext = context.Close; _logger = context.Log; _message = messages; _secure = context.IsSecureConnection; _stream = context.Stream; _waitTime = TimeSpan.FromSeconds(1.0); init(); } public WebSocket(string url, params string[] protocols) { if (url == null) { throw new ArgumentNullException("url"); } if (url.Length == 0) { throw new ArgumentException("An empty string.", "url"); } if (!url.TryCreateWebSocketUri(out _uri, out var text)) { throw new ArgumentException(text, "url"); } if (protocols != null && protocols.Length != 0) { text = protocols.CheckIfValidProtocols(); if (text != null) { throw new ArgumentException(text, "protocols"); } _protocols = protocols; } _base64Key = CreateBase64Key(); _client = true; _logger = new Logger(); _message = messagec; _secure = _uri.Scheme == "wss"; _waitTime = TimeSpan.FromSeconds(5.0); init(); } private bool accept() { lock (_forState) { if (!checkIfAvailable(connecting: true, open: false, closing: false, closed: false, out var text)) { _logger.Error(text); error("An error has occurred in accepting.", null); return false; } try { if (!acceptHandshake()) { return false; } _readyState = WebSocketState.Open; } catch (Exception ex) { _logger.Fatal(ex.ToString()); fatal("An exception has occurred while accepting.", ex); return false; } return true; } } private bool acceptHandshake() { _logger.Debug($"A request from {_context.UserEndPoint}:\n{_context}"); if (!checkHandshakeRequest(_context, out var text)) { sendHttpResponse(createHandshakeFailureResponse(CustomWebSocketSharp.Net.HttpStatusCode.BadRequest)); _logger.Fatal(text); fatal("An error has occurred while accepting.", CloseStatusCode.ProtocolError); return false; } if (!customCheckHandshakeRequest(_context, out text)) { sendHttpResponse(createHandshakeFailureResponse(CustomWebSocketSharp.Net.HttpStatusCode.BadRequest)); _logger.Fatal(text); fatal("An error has occurred while accepting.", CloseStatusCode.PolicyViolation); return false; } _base64Key = _context.Headers["Sec-WebSocket-Key"]; if (_protocol != null) { processSecWebSocketProtocolHeader(_context.SecWebSocketProtocols); } if (!_ignoreExtensions) { processSecWebSocketExtensionsClientHeader(_context.Headers["Sec-WebSocket-Extensions"]); } return sendHttpResponse(createHandshakeResponse()); } private bool checkHandshakeRequest(WebSocketContext context, out string message) { message = null; if (context.RequestUri == null) { message = "Specifies an invalid Request-URI."; return false; } if (!context.IsWebSocketRequest) { message = "Not a WebSocket handshake request."; return false; } NameValueCollection headers = context.Headers; if (!validateSecWebSocketKeyHeader(headers["Sec-WebSocket-Key"])) { message = "Includes no Sec-WebSocket-Key header, or it has an invalid value."; return false; } if (!validateSecWebSocketVersionClientHeader(headers["Sec-WebSocket-Version"])) { message = "Includes no Sec-WebSocket-Version header, or it has an invalid value."; return false; } if (!validateSecWebSocketProtocolClientHeader(headers["Sec-WebSocket-Protocol"])) { message = "Includes an invalid Sec-WebSocket-Protocol header."; return false; } if (!_ignoreExtensions && !validateSecWebSocketExtensionsClientHeader(headers["Sec-WebSocket-Extensions"])) { message = "Includes an invalid Sec-WebSocket-Extensions header."; return false; } return true; } private bool checkHandshakeResponse(HttpResponse response, out string message) { message = null; if (response.IsRedirect) { message = "Indicates the redirection."; return false; } if (response.IsUnauthorized) { message = "Requires the authentication."; return false; } if (!response.IsWebSocketResponse) { message = "Not a WebSocket handshake response."; return false; } NameValueCollection headers = response.Headers; if (!validateSecWebSocketAcceptHeader(headers["Sec-WebSocket-Accept"])) { message = "Includes no Sec-WebSocket-Accept header, or it has an invalid value."; return false; } if (!validateSecWebSocketProtocolServerHeader(headers["Sec-WebSocket-Protocol"])) { message = "Includes no Sec-WebSocket-Protocol header, or it has an invalid value."; return false; } if (!validateSecWebSocketExtensionsServerHeader(headers["Sec-WebSocket-Extensions"])) { message = "Includes an invalid Sec-WebSocket-Extensions header."; return false; } if (!validateSecWebSocketVersionServerHeader(headers["Sec-WebSocket-Version"])) { message = "Includes an invalid Sec-WebSocket-Version header."; return false; } return true; } private bool checkIfAvailable(bool connecting, bool open, bool closing, bool closed, out string message) { message = null; if (!connecting && _readyState == WebSocketState.Connecting) { message = "This operation is not available in: connecting"; return false; } if (!open && _readyState == WebSocketState.Open) { message = "This operation is not available in: open"; return false; } if (!closing && _readyState == WebSocketState.Closing) { message = "This operation is not available in: closing"; return false; } if (!closed && _readyState == WebSocketState.Closed) { message = "This operation is not available in: closed"; return false; } return true; } private bool checkIfAvailable(bool client, bool server, bool connecting, bool open, bool closing, bool closed, out string message) { message = null; if (!client && _client) { message = "This operation is not available in: client"; return false; } if (!server && !_client) { message = "This operation is not available in: server"; return false; } return checkIfAvailable(connecting, open, closing, closed, out message); } private static bool checkParametersForSetCredentials(string username, string password, out string message) { message = null; if (username.IsNullOrEmpty()) { return true; } if (Ext.Contains(username, ':') || !username.IsText()) { message = "'username' contains an invalid character."; return false; } if (password.IsNullOrEmpty()) { return true; } if (!password.IsText()) { message = "'password' contains an invalid character."; return false; } return true; } private static bool checkParametersForSetProxy(string url, string username, string password, out string message) { message = null; if (url.IsNullOrEmpty()) { return true; } if (!Uri.TryCreate(url, UriKind.Absolute, out Uri result) || result.Scheme != "http" || result.Segments.Length > 1) { message = "'url' is an invalid URL."; return false; } if (username.IsNullOrEmpty()) { return true; } if (Ext.Contains(username, ':') || !username.IsText()) { message = "'username' contains an invalid character."; return false; } if (password.IsNullOrEmpty()) { return true; } if (!password.IsText()) { message = "'password' contains an invalid character."; return false; } return true; } private bool checkReceivedFrame(WebSocketFrame frame, out string message) { message = null; bool isMasked = frame.IsMasked; if (_client && isMasked) { message = "A frame from the server is masked."; return false; } if (!_client && !isMasked) { message = "A frame from a client is not masked."; return false; } if (_inContinuation && frame.IsData) { message = "A data frame has been received while receiving continuation frames."; return false; } if (frame.IsCompressed && _compression == CompressionMethod.None) { message = "A compressed frame has been received without any agreement for it."; return false; } if (frame.Rsv2 == Rsv.On) { message = "The RSV2 of a frame is non-zero without any negotiation for it."; return false; } if (frame.Rsv3 == Rsv.On) { message = "The RSV3 of a frame is non-zero without any negotiation for it."; return false; } return true; } private void close(ushort code, string reason) { if (_readyState == WebSocketState.Closing) { _logger.Info("The closing is already in progress."); return; } if (_readyState == WebSocketState.Closed) { _logger.Info("The connection has already been closed."); return; } if (code == 1005) { close(PayloadData.Empty, send: true, receive: true, received: false); return; } bool receive = !code.IsReserved(); close(new PayloadData(code, reason), receive, receive, received: false); } private void close(PayloadData payloadData, bool send, bool receive, bool received) { lock (_forState) { if (_readyState == WebSocketState.Closing) { _logger.Info("The closing is already in progress."); return; } if (_readyState == WebSocketState.Closed) { _logger.Info("The connection has already been closed."); return; } send = send && _readyState == WebSocketState.Open; receive = send && receive; _readyState = WebSocketState.Closing; } _logger.Trace("Begin closing the connection."); bool wasClean = closeHandshake(payloadData, send, receive, received); releaseResources(); _logger.Trace("End closing the connection."); _readyState = WebSocketState.Closed; CloseEventArgs closeEventArgs = new CloseEventArgs(payloadData); closeEventArgs.WasClean = wasClean; try { this.OnClose.Emit(this, closeEventArgs); } catch (Exception ex) { _logger.Error(ex.ToString()); error("An error has occurred during the OnClose event.", ex); } } private void closeAsync(ushort code, string reason) { if (_readyState == WebSocketState.Closing) { _logger.Info("The closing is already in progress."); return; } if (_readyState == WebSocketState.Closed) { _logger.Info("The connection has already been closed."); return; } if (code == 1005) { closeAsync(PayloadData.Empty, send: true, receive: true, received: false); return; } bool receive = !code.IsReserved(); closeAsync(new PayloadData(code, reason), receive, receive, received: false); } private void closeAsync(PayloadData payloadData, bool send, bool receive, bool received) { Action<PayloadData, bool, bool, bool> closer = close; closer.BeginInvoke(payloadData, send, receive, received, delegate(IAsyncResult ar) { closer.EndInvoke(ar); }, null); } private bool closeHandshake(byte[] frameAsBytes, bool receive, bool received) { bool flag = frameAsBytes != null && sendBytes(frameAsBytes); if (!received && flag && receive && _receivingExited != null) { received = _receivingExited.WaitOne(_waitTime); } bool flag2 = flag && received; _logger.Debug($"Was clean?: {flag2}\n sent: {flag}\n received: {received}"); return flag2; } private bool closeHandshake(PayloadData payloadData, bool send, bool receive, bool received) { bool flag = false; if (send) { WebSocketFrame webSocketFrame = WebSocketFrame.CreateCloseFrame(payloadData, _client); flag = sendBytes(webSocketFrame.ToArray()); if (_client) { webSocketFrame.Unmask(); } } if (!received && flag && receive && _receivingExited != null) { received = _receivingExited.WaitOne(_waitTime); } bool flag2 = flag && received; _logger.Debug($"Was clean?: {flag2}\n sent: {flag}\n received: {received}"); return flag2; } private bool connect() { lock (_forState) { if (!checkIfAvailable(connecting: true, open: false, closing: false, closed: true, out var text)) { _logger.Error(text); error("An error has occurred in connecting.", null); return false; } if (_retryCountForConnect > _maxRetryCountForConnect) { _retryCountForConnect = 0; _logger.Fatal("A series of reconnecting has failed."); return false; } _readyState = WebSocketState.Connecting; try { doHandshake(); } catch (Exception ex) { _retryCountForConnect++; _logger.Fatal(ex.ToString()); fatal("An exception has occurred while connecting.", ex); return false; } _retryCountForConnect = 1; _readyState = WebSocketState.Open; return true; } } private string createExtensions() { StringBuilder stringBuilder = new StringBuilder(80); if (_compression != 0) { string arg = _compression.ToExtensionString("server_no_context_takeover", "client_no_context_takeover"); stringBuilder.AppendFormat("{0}, ", arg); } int length = stringBuilder.Length; if (length > 2) { stringBuilder.Length = length - 2; return stringBuilder.ToString(); } return null; } private HttpResponse createHandshakeFailureResponse(CustomWebSocketSharp.Net.HttpStatusCode code) { HttpResponse httpResponse = HttpResponse.CreateCloseResponse(code); httpResponse.Headers["Sec-WebSocket-Version"] = "13"; return httpResponse; } private HttpRequest createHandshakeRequest() { HttpRequest httpRequest = HttpRequest.CreateWebSocketRequest(_uri); NameValueCollection headers = httpRequest.Headers; if (!_origin.IsNullOrEmpty()) { headers["Origin"] = _origin; } headers["Sec-WebSocket-Key"] = _base64Key; _protocolsRequested = _protocols != null; if (_protocolsRequested) { headers["Sec-WebSocket-Protocol"] = _protocols.ToString(", "); } _extensionsRequested = _compression != CompressionMethod.None; if (_extensionsRequested) { headers["Sec-WebSocket-Extensions"] = createExtensions(); } headers["Sec-WebSocket-Version"] = "13"; AuthenticationResponse authenticationResponse = null; if (_authChallenge != null && _credentials != null) { authenticationResponse = new AuthenticationResponse(_authChallenge, _credentials, _nonceCount); _nonceCount = authenticationResponse.NonceCount; } else if (_preAuth) { authenticationResponse = new AuthenticationResponse(_credentials); } if (authenticationResponse != null) { headers["Authorization"] = authenticationResponse.ToString(); } if (_cookies.Count > 0) { httpRequest.SetCookies(_cookies); } return httpRequest; } private HttpResponse createHandshakeResponse() { HttpResponse httpResponse = HttpResponse.CreateWebSocketResponse(); NameValueCollection headers = httpResponse.Headers; headers["Sec-WebSocket-Accept"] = CreateResponseKey(_base64Key); if (_protocol != null) { headers["Sec-WebSocket-Protocol"] = _protocol; } if (_extensions != null) { headers["Sec-WebSocket-Extensions"] = _extensions; } if (_cookies.Count > 0) { httpResponse.SetCookies(_cookies); } return httpResponse; } private bool customCheckHandshakeRequest(WebSocketContext context, out string message) { message = null; if (_handshakeRequestChecker != null) { return (message = _handshakeRequestChecker(context)) == null; } return true; } private MessageEventArgs dequeueFromMessageEventQueue() { lock (_forMessageEventQueue) { return (_messageEventQueue.Count > 0) ? _messageEventQueue.Dequeue() : null; } } private void doHandshake() { setClientStream(); HttpResponse httpResponse = sendHandshakeRequest(); if (!checkHandshakeResponse(httpResponse, out var text)) { throw new WebSocketException(CloseStatusCode.ProtocolError, text); } if (_protocolsRequested) { _protocol = httpResponse.Headers["Sec-WebSocket-Protocol"]; } if (_extensionsRequested) { processSecWebSocketExtensionsServerHeader(httpResponse.Headers["Sec-WebSocket-Extensions"]); } processCookies(httpResponse.Cookies); } private void enqueueToMessageEventQueue(MessageEventArgs e) { lock (_forMessageEventQueue) { _messageEventQueue.Enqueue(e); } } private void error(string message, Exception exception) { try { this.OnError.Emit(this, new ErrorEventArgs(message, exception)); } catch (Exception ex) { _logger.Error(ex.ToString()); } } private void fatal(string message, Exception exception) { CloseStatusCode code = ((exception is WebSocketException) ? ((WebSocketException)exception).Code : CloseStatusCode.Abnormal); fatal(message, (ushort)code); } private void fatal(string message, ushort code) { PayloadData payloadData = new PayloadData(code, message); close(payloadData, !code.IsReserved(), receive: false, received: false); } private void fatal(string message, CloseStatusCode code) { fatal(message, (ushort)code); } private void init() { _compression = CompressionMethod.None; _cookies = new CustomWebSocketSharp.Net.CookieCollection(); _forPing = new object(); _forSend = new object(); _forState = new object(); _messageEventQueue = new Queue<MessageEventArgs>(); _forMessageEventQueue = ((ICollection)_messageEventQueue).SyncRoot; _readyState = WebSocketState.Connecting; } private void message() { MessageEventArgs obj = null; lock (_forMessageEventQueue) { if (_inMessage || _messageEventQueue.Count == 0 || _readyState != WebSocketState.Open) { return; } _inMessage = true; obj = _messageEventQueue.Dequeue(); } _message(obj); } private void messagec(MessageEventArgs e) { while (true) { try { this.OnMessage.Emit(this, e); } catch (Exception ex) { _logger.Error(ex.ToString()); error("An error has occurred during an OnMessage event.", ex); } lock (_forMessageEventQueue) { if (_messageEventQueue.Count == 0 || _readyState != WebSocketState.Open) { _inMessage = false; break; } e = _messageEventQueue.Dequeue(); } } } private void messages(MessageEventArgs e) { try { this.OnMessage.Emit(this, e); } catch (Exception ex) { _logger.Error(ex.ToString()); error("An error has occurred during an OnMessage event.", ex); } lock (_forMessageEventQueue) { if (_messageEventQueue.Count == 0 || _readyState != WebSocketState.Open) { _inMessage = false; return; } e = _messageEventQueue.Dequeue(); } ThreadPool.QueueUserWorkItem(delegate { messages(e); }); } private void open() { _inMessage = true; startReceiving(); try { this.OnOpen.Emit(this, EventArgs.Empty); } catch (Exception ex) { _logger.Error(ex.ToString()); error("An error has occurred during the OnOpen event.", ex); } MessageEventArgs obj = null; lock (_forMessageEventQueue) { if (_messageEventQueue.Count == 0 || _readyState != WebSocketState.Open) { _inMessage = false; return; } obj = _messageEventQueue.Dequeue(); } _message.BeginInvoke(obj, delegate(IAsyncResult ar) { _message.EndInvoke(ar); }, null); } private bool ping(byte[] data) { if (_readyState != WebSocketState.Open) { return false; } ManualResetEvent pongReceived = _pongReceived; if (pongReceived == null) { return false; } lock (_forPing) { try { pongReceived.Reset(); if (!send(Fin.Final, Opcode.Ping, data, compressed: false)) { return false; } return pongReceived.WaitOne(_waitTime); } catch (ObjectDisposedException) { return false; } } } private bool processCloseFrame(WebSocketFrame frame) { PayloadData payloadData = frame.PayloadData; close(payloadData, !payloadData.HasReservedCode, receive: false, received: true); return false; } private void processCookies(CustomWebSocketSharp.Net.CookieCollection cookies) { if (cookies.Count != 0) { _cookies.SetOrRemove(cookies); } } private bool processDataFrame(WebSocketFrame frame) { enqueueToMessageEventQueue(frame.IsCompressed ? new MessageEventArgs(frame.Opcode, frame.PayloadData.ApplicationData.Decompress(_compression)) : new MessageEventArgs(frame)); return true; } private bool processFragmentFrame(WebSocketFrame frame) { if (!_inContinuation) { if (frame.IsContinuation) { return true; } _fragmentsOpcode = frame.Opcode; _fragmentsCompressed = frame.IsCompressed; _fragmentsBuffer = new MemoryStream(); _inContinuation = true; } _fragmentsBuffer.WriteBytes(frame.PayloadData.ApplicationData, 1024); if (frame.IsFinal) { using (_fragmentsBuffer) { byte[] rawData = (_fragmentsCompressed ? _fragmentsBuffer.DecompressToArray(_compression) : _fragmentsBuffer.ToArray()); enqueueToMessageEventQueue(new MessageEventArgs(_fragmentsOpcode, rawData)); } _fragmentsBuffer = null; _inContinuation = false; } return true; } private bool processPingFrame(WebSocketFrame frame) { WebSocketFrame webSocketFrame = WebSocketFrame.CreatePongFrame(frame.PayloadData, _client); lock (_forState) { if (_readyState != WebSocketState.Open) { _logger.Error("The state of the connection has been changed."); return true; } if (!sendBytes(webSocketFrame.ToArray())) { return false; } } _logger.Trace("A pong has been sent to respond to this ping."); if (_emitOnPing) { if (_client) { webSocketFrame.Unmask(); } enqueueToMessageEventQueue(new MessageEventArgs(frame)); } return true; } private bool processPongFrame(WebSocketFrame frame) { try { _pongReceived.Set(); } catch (NullReferenceException) { return false; } catch (ObjectDisposedException) { return false; } _logger.Trace("It has been signaled that a pong was received."); return true; } private bool processReceivedFrame(WebSocketFrame frame) { if (!checkReceivedFrame(frame, out var text)) { throw new WebSocketException(CloseStatusCode.ProtocolError, text); } frame.Unmask(); if (!frame.IsFragment) { if (!frame.IsData) { if (!frame.IsPing) { if (!frame.IsPong) { if (!frame.IsClose) { return processUnsupportedFrame(frame); } return processCloseFrame(frame); } return processPongFrame(frame); } return processPingFrame(frame); } return processDataFrame(frame); } return processFragmentFrame(frame); } private void processSecWebSocketExtensionsClientHeader(string value) { if (value == null) { return; } StringBuilder stringBuilder = new StringBuilder(80); bool flag = false; foreach (string item in value.SplitHeaderValue(',')) { string value2 = item.Trim(); if (!flag && value2.IsCompressionExtension(CompressionMethod.Deflate)) { _compression = CompressionMethod.Deflate; stringBuilder.AppendFormat("{0}, ", _compression.ToExtensionString("client_no_context_takeover", "server_no_context_takeover")); flag = true; } } int length = stringBuilder.Length; if (length > 2) { stringBuilder.Length = length - 2; _extensions = stringBuilder.ToString(); } } private void processSecWebSocketExtensionsServerHeader(string value) { if (value == null) { _compression = CompressionMethod.None; } else { _extensions = value; } } private void processSecWebSocketProtocolHeader(IEnumerable<string> values) { if (!values.Contains((string p) => p == _protocol)) { _protocol = null; } } private bool processUnsupportedFrame(WebSocketFrame frame) { _logger.Fatal("An unsupported frame:" + frame.PrintToString(dumped: false)); fatal("There is no way to handle it.", CloseStatusCode.PolicyViolation); return false; } private void releaseClientResources() { if (_stream != null) { _stream.Dispose(); _stream = null; } if (_tcpClient != null) { _tcpClient.Close(); _tcpClient = null; } } private void releaseCommonResources() { if (_fragmentsBuffer != null) { _fragmentsBuffer.Dispose(); _fragmentsBuffer = null; _inContinuation = false; } if (_pongReceived != null) { _pongReceived.Close(); _pongReceived = null; } if (_receivingExited != null) { _receivingExited.Close(); _receivingExited = null; } } private void releaseResources() { if (_client) { releaseClientResources(); } else { releaseServerResources(); } releaseCommonResources(); } private void releaseServerResources() { if (_closeContext != null) { _closeContext(); _closeContext = null; _stream = null; _context = null; } } private bool send(Opcode opcode, Stream stream) { lock (_forSend) { Stream stream2 = stream; bool flag = false; bool flag2 = false; try { if (_compression != 0) { stream = stream.Compress(_compression); flag = true; } flag2 = send(opcode, stream, flag); if (!flag2) { error("A send has been interrupted.", null); } } catch (Exception ex) { _logger.Error(ex.ToString()); error("An error has occurred during a send.", ex); } finally { if (flag) { stream.Dispose(); } stream2.Dispose(); } return flag2; } } private bool send(Opcode opcode, Stream stream, bool compressed) { long length = stream.Length; if (length == 0L) { return send(Fin.Final, opcode, EmptyBytes, compressed: false); } long num = length / FragmentLength; int num2 = (int)(length % FragmentLength); byte[] array = null; switch (num) { case 0L: array = new byte[num2]; if (stream.Read(array, 0, num2) == num2) { return send(Fin.Final, opcode, array, compressed); } return false; case 1L: if (num2 == 0) { array = new byte[FragmentLength]; if (stream.Read(array, 0, FragmentLength) == FragmentLength) { return send(Fin.Final, opcode, array, compressed); } return false; } break; } array = new byte[FragmentLength]; if (stream.Read(array, 0, FragmentLength) != FragmentLength || !send(Fin.More, opcode, array, compressed)) { return false; } long num3 = ((num2 == 0) ? (num - 2) : (num - 1)); for (long num4 = 0L; num4 < num3; num4++) { if (stream.Read(array, 0, FragmentLength) != FragmentLength || !send(Fin.More, Opcode.Cont, array, compressed: false)) { return false; } } if (num2 == 0) { num2 = FragmentLength; } else { array = new byte[num2]; } if (stream.Read(array, 0, num2) == num2) { return send(Fin.Final, Opcode.Cont, array, compressed: false); } return false; } private bool send(Fin fin, Opcode opcode, byte[] data, bool compressed) { lock (_forState) { if (_readyState != WebSocketState.Open) { _logger.Error("The state of the connection has been changed."); return false; } WebSocketFrame webSocketFrame = new WebSocketFrame(fin, opcode, data, compressed, _client); return sendBytes(webSocketFrame.ToArray()); } } private void sendAsync(Opcode opcode, Stream stream, Action<bool> completed) { Func<Opcode, Stream, bool> sender = send; sender.BeginInvoke(opcode, stream, delegate(IAsyncResult ar) { try { bool obj = sender.EndInvoke(ar); if (completed != null) { completed(obj); } } catch (Exception ex) { _logger.Error(ex.ToString()); error("An error has occurred during the callback for an async send.", ex); } }, null); } private bool sendBytes(byte[] bytes) { try { _stream.Write(bytes, 0, bytes.Length); return true; } catch (Exception ex) { _logger.Error(ex.ToString()); return false; } } private HttpResponse sendHandshakeRequest() { HttpRequest httpRequest = createHandshakeRequest(); HttpResponse httpResponse = sendHttpRequest(httpRequest, 90000); if (httpResponse.IsUnauthorized) { string text = httpResponse.Headers["WWW-Authenticate"]; _logger.Warn($"Received an authentication requirement for '{text}'."); if (text.IsNullOrEmpty()) { _logger.Error("No authentication challenge is specified."); return httpResponse; } _authChallenge = AuthenticationChallenge.Parse(text); if (_authChallenge == null) { _logger.Error("An invalid authentication challenge is specified."); return httpResponse; } if (_credentials != null && (!_preAuth || _authChallenge.Scheme == CustomWebSocketSharp.Net.AuthenticationSchemes.Digest)) { if (httpResponse.HasConnectionClose) { releaseClientResources(); setClientStream(); } AuthenticationResponse authenticationResponse = new AuthenticationResponse(_authChallenge, _credentials, _nonceCount); _nonceCount = authenticationResponse.NonceCount; httpRequest.Headers["Authorization"] = authenticationResponse.ToString(); httpResponse = sendHttpRequest(httpRequest, 15000); } } if (httpResponse.IsRedirect) { string text2 = httpResponse.Headers["Location"]; _logger.Warn($"Received a redirection to '{text2}'."); if (_enableRedirection) { if (text2.IsNullOrEmpty()) { _logger.Error("No url to redirect is located."); return httpResponse; } if (!text2.TryCreateWebSocketUri(out var result, out var text3)) { _logger.Error("An invalid url to redirect is located: " + text3); return httpResponse; } releaseClientResources(); _uri = result; _secure = result.Scheme == "wss"; setClientStream(); return sendHandshakeRequest(); } } return httpResponse; } private HttpResponse sendHttpRequest(HttpRequest request, int millisecondsTimeout) { _logger.Debug("A request to the server:\n" + request.ToString()); HttpResponse response = request.GetResponse(_stream, millisecondsTimeout); _logger.Debug("A response to this request:\n" + response.ToString()); return response; } private bool sendHttpResponse(HttpResponse response) { _logger.Debug("A response to this request:\n" + response.ToString()); return sendBytes(response.ToByteArray()); } private void sendProxyConnectRequest() { HttpRequest httpRequest = HttpRequest.CreateConnectRequest(_uri); HttpResponse httpResponse = sendHttpRequest(httpRequest, 90000); if (httpResponse.IsProxyAuthenticationRequired) { string text = httpResponse.Headers["Proxy-Authenticate"]; _logger.Warn($"Received a proxy authentication requirement for '{text}'."); if (text.IsNullOrEmpty()) { throw new WebSocketException("No proxy authentication challenge is specified."); } AuthenticationChallenge authenticationChallenge = AuthenticationChallenge.Parse(text); if (authenticationChallenge == null) { throw new WebSocketException("An invalid proxy authentication challenge is specified."); } if (_proxyCredentials != null) { if (httpResponse.HasConnectionClose) { releaseClientResources(); _tcpClient = new TcpClient(_proxyUri.DnsSafeHost, _proxyUri.Port); _stream = _tcpClient.GetStream(); } AuthenticationResponse authenticationResponse = new AuthenticationResponse(authenticationChallenge, _proxyCredentials, 0u); httpRequest.Headers["Proxy-Authorization"] = authenticationResponse.ToString(); httpResponse = sendHttpRequest(httpRequest, 15000); } if (httpResponse.IsProxyAuthenticationRequired) { throw new WebSocketException("A proxy authentication is required."); } } if (httpResponse.StatusCode[0] != '2') { throw new WebSocketException("The proxy has failed a connection to the requested host and port."); } } private void setClientStream() { if (_proxyUri != null) { _tcpClient = new TcpClient(_proxyUri.DnsSafeHost, _proxyUri.Port); _stream = _tcpClient.GetStream(); sendProxyConnectRequest(); } else { _tcpClient = new TcpClient(_uri.DnsSafeHost, _uri.Port); _stream = _tcpClient.GetStream(); } if (_secure) { ClientSslConfiguration sslConfiguration = SslConfiguration; string targetHost = sslConfiguration.TargetHost; if (targetHost != _uri.DnsSafeHost) { throw new WebSocketException(CloseStatusCode.TlsHandshakeFailure, "An invalid host name is specified."); } try { SslStream sslStream = new SslStream(_stream, leaveInnerStreamOpen: false, sslConfiguration.ServerCertificateValidationCallback, sslConfiguration.ClientCertificateSelectionCallback); sslStream.AuthenticateAsClient(targetHost, sslConfiguration.ClientCertificates, sslConfiguration.EnabledSslProtocols, sslConfiguration.CheckCertificateRevocation); _stream = sslStream; } catch (Exception innerException) { throw new WebSocketException(CloseStatusCode.TlsHandshakeFailure, innerException); } } } private void startReceiving() { if (_messageEventQueue.Count > 0) { _messageEventQueue.Clear(); } _pongReceived = new ManualResetEvent(initialState: false); _receivingExited = new ManualResetEvent(initialState: false); Action receive = null; receive = delegate { WebSocketFrame.ReadFrameAsync(_stream, unmask: false, delegate(WebSocketFrame frame) { if (!processReceivedFrame(frame) || _readyState == WebSocketState.Closed) { _receivingExited?.Set(); } else { receive(); if (!_inMessage && HasMessage && _readyState == WebSocketState.Open) { message(); } } }, delegate(Exception ex) { _logger.Fatal(ex.ToString()); fatal("An exception has occurred while receiving.", ex); }); }; receive(); } private bool validateSecWebSocketAcceptHeader(string value) { if (value != null) { return value == CreateResponseKey(_base64Key); } return false; } private bool validateSecWebSocketExtensionsClientHeader(string value) { if (value != null) { return value.Length > 0; } return true; } private bool validateSecWebSocketExtensionsServerHeader(string value) { if (value == null) { return true; } if (value.Length == 0) { return false; } if (!_extensionsRequested) { return false; } bool flag = _compression != CompressionMethod.None; foreach (string item in value.SplitHeaderValue(',')) { string text = item.Trim(); if (flag && text.IsCompressionExtension(_compression)) { if (!text.Contains("server_no_context_takeover")) { _logger.Error("The server hasn't sent back 'server_no_context_takeover'."); return false; } if (!text.Contains("client_no_context_takeover")) { _logger.Warn("The server hasn't sent back 'client_no_context_takeover'."); } string method = _compression.ToExtensionString(); if (text.SplitHeaderValue(';').Contains(delegate(string t) { t = t.Trim(); return t != method && t != "server_no_context_takeover" && t != "client_no_context_takeover"; })) { return false; } continue; } return false; } return true; } private bool validateSecWebSocketKeyHeader(string value) { if (value != null) { return value.Length > 0; } return false; } private bool validateSecWebSo
BepInEx/plugins/SimHaptics/H3VR.SimHaptics.dll
Decompiled 12 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using Bhaptics.Tact; using FistVR; using FistVR.BSkee; using HarmonyLib; using Sodalite.ModPanel; using Sodalite.ModPanel.Components; using Sodalite.ModPanel.Pages; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: CompilationRelaxations(8)] [assembly: AssemblyVersion("0.0.0.0")] namespace SimHaptics; public static class HealingModel { public const float Duration = 1.05f; public static bool Confirmed(bool doneBefore, bool doneAfter, bool powerup, float before, float after) { if (!doneBefore && doneAfter && !powerup) { return after > before + 1E-05f; } return false; } public static float Scale(float restored) { if (!(restored <= 0f)) { return 0.45f + 0.55f * (float)Math.Sqrt(RecoilModel.Clamp(restored, 0f, 1f)); } return 0f; } public static float Envelope(float age) { if (!(age < 0f) && !(age >= 1.05f)) { return (float)Math.Pow(Math.Sin(Math.PI * (double)age / 1.0499999523162842), 0.8); } return 0f; } public static float[] Pattern(int channel, float age, float strength) { float[] array = new float[(channel < 2) ? 6 : 20]; if (age < 0f || age >= 1.05f) { return array; } float num = age / 1.05f; if (channel < 2) { float num2 = 5f * RecoilModel.Clamp((num - 0.18f) / 0.7f, 0f, 1f); for (int i = 0; i < 6; i++) { array[i] = strength * 0.35f * (float)Math.Exp(-0.5 * Math.Pow(((float)i - num2) / 1.25f, 2.0)); } return array; } return VestPattern.Broad(0.5f, 0.78f - 0.62f * num, strength * ((channel == 3) ? 0.65f : 1f)); } } [BepInPlugin("local.h3vr.simhaptics", "H3VR SimHaptics", "0.26.0")] [BepInDependency("nrgill28.Sodalite", "1.5.1")] [BepInIncompatibility("org.bepinex.plugins.H3VR_bhaptics")] public class Plugin : BaseUnityPlugin { private sealed class HealingPickupState { public FVRPlayerBody Body; public float Before; public bool Done; public bool Powerup; } [HarmonyPatch(typeof(HealthPickUp), "Boom", new Type[] { })] private static class HealingPickupPatch { private static void Prefix(HealthPickUp __instance, bool ___m_hasDone, out object __state) { __state = null; if ((Object)(object)Instance == (Object)null || !Instance.healingEnabled.Value || ___m_hasDone) { return; } try { FVRPlayerBody currentPlayerBody = GM.CurrentPlayerBody; if (!((Object)(object)currentPlayerBody == (Object)null)) { __state = new HealingPickupState { Body = currentPlayerBody, Before = currentPlayerBody.GetPlayerHealth(), Done = ___m_hasDone, Powerup = __instance.IsPowerUp }; } } catch (Exception e) { Instance.Error(e); } } private static void Postfix(bool ___m_hasDone, object __state) { if ((Object)(object)Instance == (Object)null || __state == null) { return; } try { HealingPickupState healingPickupState = (HealingPickupState)__state; if (!((Object)(object)healingPickupState.Body == (Object)null) && !((Object)(object)healingPickupState.Body != (Object)(object)GM.CurrentPlayerBody)) { float playerHealth = healingPickupState.Body.GetPlayerHealth(); if (HealingModel.Confirmed(healingPickupState.Done, ___m_hasDone, healingPickupState.Powerup, healingPickupState.Before, playerHealth)) { Instance.QueuePower(0, inverted: false, 3, HealingModel.Scale(playerHealth - healingPickupState.Before)); } } } catch (Exception e) { Instance.Error(e); } } } private sealed class PanelRange : AcceptableValueFloatRangeStep { public PanelRange(float min, float max, float step) : base(min, max, step) { } public override object Clamp(object value) { float num = (float)value; return (float.IsNaN(num) || float.IsInfinity(num)) ? ((AcceptableValueRange<float>)(object)this).MinValue : Math.Max(((AcceptableValueRange<float>)(object)this).MinValue, Math.Min(((AcceptableValueRange<float>)(object)this).MaxValue, num)); } public override bool IsValid(object value) { float num = (float)value; if (!float.IsNaN(num) && !float.IsInfinity(num) && num >= ((AcceptableValueRange<float>)(object)this).MinValue) { return num <= ((AcceptableValueRange<float>)(object)this).MaxValue; } return false; } } [HarmonyPatch(typeof(ModPanelConfigPage), "NavigateHere", new Type[] { typeof(PluginInfo) })] private static class SettingsPagePatch { private static void Postfix(ModPanelConfigPage __instance, PluginInfo plugin) { //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Expected O, but got Unknown //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_028f: Expected O, but got Unknown //IL_030b: Unknown result type (might be due to invalid IL or missing references) //IL_0315: 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 if ((Object)(object)Instance == (Object)null || plugin.Metadata.GUID != "local.h3vr.simhaptics") { return; } try { ConfigFieldList[] componentsInChildren = ((Component)__instance.ContentGameObject).GetComponentsInChildren<ConfigFieldList>(); foreach (ConfigFieldList val in componentsInChildren) { SettingsPresetControl settingsPresetControl = ((Component)val).gameObject.AddComponent<SettingsPresetControl>(); settingsPresetControl.Configure(val, Instance.strengthPreset); } ConfigFieldInteger[] componentsInChildren2 = ((Component)__instance.ContentGameObject).GetComponentsInChildren<ConfigFieldInteger>(); foreach (ConfigFieldInteger val2 in componentsInChildren2) { ConfigEntryBase val3 = (ConfigEntryBase)AccessTools.Field(typeof(ConfigFieldBase), "ConfigEntry").GetValue(val2); if ((object)val3.SettingType == typeof(float)) { AcceptableValueBase acceptableValues = val3.Description.AcceptableValues; AcceptableValueFloatRangeStep val4 = (AcceptableValueFloatRangeStep)(object)((acceptableValues is AcceptableValueFloatRangeStep) ? acceptableValues : null); if (val4 != null) { SettingsSliderField.Replace(val2, val3, val4); } } } ConfigFieldBase[] componentsInChildren3 = ((Component)__instance.ContentGameObject).GetComponentsInChildren<ConfigFieldBase>(); foreach (ConfigFieldBase val5 in componentsInChildren3) { val5.Name.text = Regex.Replace(val5.Name.text, "([a-z])([A-Z])", "$1 $2"); } Text[] componentsInChildren4 = ((Component)__instance.ContentGameObject).GetComponentsInChildren<Text>(); foreach (Text val6 in componentsInChildren4) { if (val6.text == "BodyResponse") { val6.text = "Opposite-side response (advanced)"; } else if (val6.text == "Boomskee") { val6.text = "Boomskee range (advanced)"; } else if (val6.text == "Calibration") { val6.text = "Vest orientation (advanced)"; } else if (val6.text == "MuzzleBlast") { val6.text = "Muzzle pressure"; } else if (val6.text == "ContinuousWeapons") { val6.text = "Continuous weapons"; } else if (val6.text == "SpecialWeapons") { val6.text = "Special weapons"; } } ConfigFieldBase val7 = Object.Instantiate<ConfigFieldBase>(__instance.BoolField, (Transform)(object)__instance.ContentGameObject); ((Component)val7).transform.SetAsFirstSibling(); val7.Name.text = "Reset to defaults"; val7.Description.text = "Restore all SimHaptics settings to our tested tuning. Click twice to confirm. Changes are saved automatically."; Button componentInChildren = ((Component)val7).GetComponentInChildren<Button>(); if ((Object)(object)componentInChildren == (Object)null) { throw new InvalidOperationException("Sodalite reset button template is unavailable."); } componentInChildren.onClick = new ButtonClickedEvent(); Text buttonText = ((Component)val7).GetComponent<ConfigFieldBool>().ButtonText; SettingsResetButton settingsResetButton = ((Component)val7).gameObject.AddComponent<SettingsResetButton>(); settingsResetButton.Configure(componentInChildren, buttonText, __instance); Object.Destroy((Object)(object)val7); ConfigFieldBase val8 = Object.Instantiate<ConfigFieldBase>(__instance.BoolField, (Transform)(object)__instance.ContentGameObject); ((Component)val8).transform.SetSiblingIndex(1); val8.Name.text = "Apply selected preset"; val8.Description.text = "Reapply the selected Strength Preset to the strength sliders. Timing, range, enabled switches and orientation are preserved."; Button componentInChildren2 = ((Component)val8).GetComponentInChildren<Button>(); componentInChildren2.onClick = new ButtonClickedEvent(); ((Component)val8).GetComponent<ConfigFieldBool>().ButtonText.text = "Apply preset"; ((UnityEvent)componentInChildren2.onClick).AddListener((UnityAction)delegate { if ((Object)(object)Instance != (Object)null) { Instance.ApplySelectedPreset(); } }); Object.Destroy((Object)(object)val8); } catch (Exception e) { Instance.Error(e); } } } [HarmonyPatch(typeof(Explosion), "Explode", new Type[] { })] private static class ExplosionPatch { private static void Prefix(bool ___m_hasExploded, out bool __state) { __state = !___m_hasExploded; } private static void Postfix(Explosion __instance, bool __state) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Instance == (Object)null || !__state || Instance.BoomskeeOwnsExplosion((Component)(object)__instance)) { return; } try { Instance.CaptureExplosion((Component)(object)__instance, __instance.ExplosionRadius, __instance.ExplosiveForce, __instance.UsesBlockers, LayerMask.op_Implicit(__instance.Mask_Blockers), ignoreTriggers: false); } catch (Exception e) { Instance.Error(e); } } } [HarmonyPatch(typeof(GrenadeExplosion), "Explode", new Type[] { })] private static class GrenadeExplosionPatch { private static void Prefix(bool ___m_hasExploded, out bool __state) { __state = !___m_hasExploded; } private static void Postfix(GrenadeExplosion __instance, bool __state) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Instance == (Object)null || !__state || Instance.BoomskeeOwnsExplosion((Component)(object)__instance) || (Object)(object)((Component)__instance).GetComponent<Explosion>() != (Object)null) { return; } try { Instance.CaptureExplosion((Component)(object)__instance, Math.Max(__instance.ExplosionRadius, __instance.ForceRadius), __instance.ExplosiveForce, __instance.UsesBlockers, LayerMask.op_Implicit(__instance.Mask_Blockers), ignoreTriggers: true); } catch (Exception e) { Instance.Error(e); } } } [HarmonyPatch(typeof(ForceExplosion), "Start", new Type[] { })] private static class ForceExplosionPatch { private static void Postfix(ForceExplosion __instance) { if ((Object)(object)Instance == (Object)null || Instance.BoomskeeOwnsExplosion((Component)(object)__instance) || (Object)(object)((Component)__instance).GetComponent<Explosion>() != (Object)null || (Object)(object)((Component)__instance).GetComponent<GrenadeExplosion>() != (Object)null) { return; } try { Instance.CaptureExplosion((Component)(object)__instance, __instance.ExplosionRadius, __instance.ExplosiveForce, usesBlockers: false, 0, ignoreTriggers: true); } catch (Exception e) { Instance.Error(e); } } } [HarmonyPatch(typeof(BoomskeeExplosion), "Splode", new Type[] { })] private static class BoomskeeDetonationPatch { private static void Postfix(BoomskeeExplosion __instance) { if ((Object)(object)Instance == (Object)null) { return; } try { Instance.CaptureExplosion((Component)(object)__instance, __instance.ForceRadius, __instance.ForcePower, usesBlockers: false, 0, ignoreTriggers: true, boomskee: true); } catch (Exception e) { Instance.Error(e); } } } [HarmonyPatch(typeof(AttachableFirearm), "Fire", new Type[] { typeof(FVRFireArmChamber), typeof(Transform), typeof(bool), typeof(FVRFireArm), typeof(float) })] private static class AttachedLaunchPatch { private static void Prefix(AttachableFirearm __instance, FVRFireArmChamber chamber, Transform muzzle, FVRFireArm fa, out object __state) { __state = null; if ((Object)(object)Instance == (Object)null) { return; } try { __state = Instance.BeginAttachment(__instance, fa, chamber, muzzle); } catch (Exception e) { Instance.Error(e); } } private static Exception Finalizer(Exception __exception, object __state) { if ((Object)(object)Instance != (Object)null) { Instance.EndFire(__state as Shot, __exception == null); } return __exception; } } [HarmonyPatch(typeof(StingerLauncher), "Fire", new Type[] { })] private static class StingerLaunchPatch { private static void Prefix(StingerLauncher __instance, bool ___m_hasMissile, out object __state) { __state = null; if ((Object)(object)Instance == (Object)null || !___m_hasMissile) { return; } try { __state = Instance.BeginFire((FVRFireArm)(object)__instance, null, ((FVRFireArm)__instance).GetMuzzle()); } catch (Exception e) { Instance.Error(e); } } private static Exception Finalizer(Exception __exception, object __state) { if ((Object)(object)Instance != (Object)null) { Instance.EndFire(__state as Shot, __exception == null); } return __exception; } } [HarmonyPatch(typeof(StingerMissile), "Fire", new Type[] { typeof(AIEntity) })] private static class StingerDischargePatch { private static void Postfix() { if (!((Object)(object)Instance == (Object)null) && Instance.firing.Count != 0) { Shot shot = Instance.firing.Peek(); if (shot.Weapon is StingerLauncher) { shot.Projectiles++; } } } } [HarmonyPatch(typeof(RemoteMissile), "SetLauncher", new Type[] { typeof(RemoteMissileLauncher) })] private static class RemoteDischargePatch { private static void Postfix(RemoteMissileLauncher __0) { if ((Object)(object)Instance == (Object)null || (Object)(object)__0 == (Object)null) { return; } for (int num = Instance.shots.Count - 1; num >= 0; num--) { Shot shot = Instance.shots[num]; if ((Object)(object)shot.Weapon == (Object)(object)__0 && shot.Frame == Time.frameCount && shot.Launch == LaunchKind.Guided) { if (shot.Projectiles == 0) { shot.Projectiles = 1; } break; } } } } [HarmonyPatch(typeof(BallisticProjectile), "FireSubmunitions", new Type[] { typeof(Vector3), typeof(Vector3), typeof(Vector3), typeof(float) })] private static class LaunchSecondaryScopePatch { private static void Prefix(out object __state) { Plugin plugin = (Plugin)(__state = Instance); if ((Object)(object)plugin != (Object)null) { plugin.launchSubmunitionDepth++; } } private static Exception Finalizer(object __state, Exception __exception) { Plugin plugin = __state as Plugin; if ((Object)(object)plugin != (Object)null) { plugin.launchSubmunitionDepth = Math.Max(0, plugin.launchSubmunitionDepth - 1); } return __exception; } } [HarmonyPatch(typeof(sblp), "FVRUpdate", new Type[] { })] private static class LaunchLaserStatePatch { private static void Postfix(sblp __instance, bool ___m_isShotEngaged) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Instance == (Object)null) { return; } try { FVRFireArmMagazine magazine = ((FVRFireArm)__instance).Magazine; sblpCell val = (sblpCell)(object)((magazine is sblpCell) ? magazine : null); Instance.SampleTool((FVRPhysicalObject)(object)__instance, 4, Instance.specialEnabled.Value && ___m_isShotEngaged && (Object)(object)val != (Object)null && ((FVRFireArmMagazine)val).FuelAmountLeft > 0f, ((Object)(object)val != (Object)null) ? ((float)val.PL / 2f) : 0f, contact: false, ((FVRPhysicalObject)__instance).AltGrip); } catch (Exception e) { Instance.Error(e); } } } [HarmonyPatch] private static class MeleeCollisionPatch { private static IEnumerable<MethodBase> TargetMethods() { try { Type[] types = typeof(FVRPhysicalObject).Assembly.GetTypes(); foreach (Type type in types) { if (typeof(FVRPhysicalObject).IsAssignableFrom(type)) { MethodInfo method = type.GetMethod("OnCollisionEnter", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(Collision) }, null); if ((object)method != null) { yield return method; } } } } finally { } } private static void Postfix(FVRPhysicalObject __instance, Collision __0) { if ((Object)(object)Instance == (Object)null) { return; } try { Instance.MeleeCollision(__instance, __0); } catch (Exception e) { Instance.Error(e); } } } [HarmonyPatch(typeof(MeleeParams), "FixedUpdate", new Type[] { typeof(float) })] private static class MeleeSweepPatch { private sealed class SweepState { public float Timer; public float Speed; } private static void Prefix(float ___m_timeSinceLastDamageDone, Vector3 ___endPointVelocity, out object __state) { __state = new SweepState { Timer = ___m_timeSinceLastDamageDone, Speed = ((Vector3)(ref ___endPointVelocity)).magnitude }; } private static void Postfix(MeleeParams __instance, FVRPhysicalObject ___m_obj, float ___m_timeSinceLastDamageDone, Vector3 ___endPointVelocity, RaycastHit ___m_dhit, object __state) { SweepState sweepState = __state as SweepState; if ((Object)(object)Instance == (Object)null || sweepState == null || !__instance.UsesSweepTesting || ___m_timeSinceLastDamageDone >= sweepState.Timer || (Object)(object)((RaycastHit)(ref ___m_dhit)).collider == (Object)null) { return; } try { float num = Math.Max(sweepState.Speed, ((Vector3)(ref ___endPointVelocity)).magnitude); Instance.EmitMelee(___m_obj, ((RaycastHit)(ref ___m_dhit)).collider, num, num, "sweep"); } catch (Exception e) { Instance.Error(e); } } } [HarmonyPatch(typeof(GM), "KillPlayer", new Type[] { typeof(bool), typeof(int) })] private static class DeathPatch { private static void Prefix(bool ___m_isDead, out bool __state) { __state = ___m_isDead; } private static void Postfix(bool ___m_isDead, bool __state) { if ((Object)(object)Instance == (Object)null || !DeathModel.Confirmed(__state, ___m_isDead)) { return; } try { Instance.BeginDeath(); } catch (Exception e) { Instance.Error(e); } } } [HarmonyPatch(typeof(GM), "ResetPlayer", new Type[] { })] private static class RespawnPatch { private static void Prefix() { if ((Object)(object)Instance == (Object)null) { return; } try { Instance.StopOwnEffects(); Instance.shots.Clear(); } catch (Exception e) { Instance.Error(e); } } } private sealed class EatContext { public AudioEvent Sound; public bool Emitted; public string Item; } [HarmonyPatch] private static class EatScopePatch { private static IEnumerable<MethodBase> TargetMethods() { yield return AccessTools.Method(typeof(EdibleThing), "UpdateInteraction", new Type[1] { typeof(FVRViveHand) }, (Type[])null); yield return AccessTools.Method(typeof(RotrwMeatCore), "UpdateInteraction", new Type[1] { typeof(FVRViveHand) }, (Type[])null); yield return AccessTools.Method(typeof(RotrwHerb), "UpdateInteraction", new Type[1] { typeof(FVRViveHand) }, (Type[])null); yield return AccessTools.Method(typeof(RotrwBangerJunk), "Update", Type.EmptyTypes, (Type[])null); yield return AccessTools.Method(typeof(BBQRapierSausage), "Update", Type.EmptyTypes, (Type[])null); yield return AccessTools.Method(typeof(RW_Powerup), "PowerUp", new Type[1] { typeof(FVRViveHand) }, (Type[])null); } private static void Prefix(Component __instance, out object __state) { __state = null; if ((Object)(object)Instance == (Object)null) { return; } try { __state = BeginEating(__instance); } catch (Exception e) { Instance.Error(e); } } private static Exception Finalizer(Exception __exception, object __state) { if ((Object)(object)Instance != (Object)null && __state != null) { if (Instance.eating.Count > 0 && Instance.eating.Peek() == __state) { Instance.eating.Pop(); } else { Instance.eating.Clear(); } } return __exception; } } [HarmonyPatch(typeof(SM), "PlayGenericSound", new Type[] { typeof(AudioEvent), typeof(Vector3) })] private static class EatGenericSoundPatch { private static void Postfix(AudioEvent __0) { ConfirmEating(__0); } } [HarmonyPatch(typeof(SM), "PlayCoreSound", new Type[] { typeof(FVRPooledAudioType), typeof(AudioEvent), typeof(Vector3) })] private static class EatCoreSoundPatch { private static void Postfix(AudioEvent __1) { ConfirmEating(__1); } } [HarmonyPatch(typeof(ZosigGameManager), "VomitObject", new Type[] { typeof(FVRObject) })] private static class VomitPatch { private static void Postfix(bool __result) { if ((Object)(object)Instance == (Object)null || !__result) { return; } try { Instance.EmitGut(GutKind.Vomit, "expelled item"); } catch (Exception e) { Instance.Error(e); } } } private sealed class PowerSnapshot { public bool[] Active = new bool[20]; public float[] Time = new float[20]; public float Health; } private sealed class PowerCue { public int Type; public int Mode; public bool Inverted; public float Expires; public float Scale; } [HarmonyPatch(typeof(FVRPlayerBody), "ActivatePower", new Type[] { typeof(PowerupType), typeof(PowerUpIntensity), typeof(PowerUpDuration), typeof(bool), typeof(bool), typeof(float) })] private static class PowerActivatePatch { private static void Prefix(FVRPlayerBody __instance, out object __state) { __state = null; if ((Object)(object)Instance == (Object)null) { return; } try { __state = Instance.ReadPower(__instance); } catch (Exception e) { Instance.Error(e); } } private static void Postfix(FVRPlayerBody __instance, PowerupType type, bool isInverted, object __state) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected I4, but got Unknown if ((Object)(object)Instance == (Object)null || __state == null) { return; } try { Instance.ActivatedPower(__instance, (int)type, isInverted, (PowerSnapshot)__state); } catch (Exception e) { Instance.Error(e); } } } private sealed class ToolState { public FVRPhysicalObject Object; public FVRAlternateGrip Support; public int Kind; public bool Contact; public float Throttle; public float Seen; public float Started; public float ContactAt = -100f; } [HarmonyPatch(typeof(Cannon), "FireShot", new Type[] { })] private static class CannonFirePatch { private static void Prefix(float ___fireDelay, out float __state) { __state = ___fireDelay; } private static Exception Finalizer(Cannon __instance, float ___fireDelay, float __state, Exception __exception) { if ((Object)(object)Instance != (Object)null && EdgeModel.CannonConfirmed(__state, ___fireDelay, __exception == null)) { try { Instance.EmitCannon(__instance); } catch (Exception e) { Instance.Error(e); } } return __exception; } } [HarmonyPatch(typeof(FlameThrower), "UpdateFire", new Type[] { })] private static class FlameStatePatch { private static void Postfix(FlameThrower __instance, bool ___m_isFiring) { if ((Object)(object)Instance == (Object)null) { return; } try { bool flag = (Object)(object)((FVRFireArm)__instance).Magazine != (Object)null && ((FVRFireArm)__instance).Magazine.FuelAmountLeft > 0f; float throttle = ((__instance.UsesValve && (Object)(object)__instance.Valve != (Object)null) ? __instance.Valve.ValvePos : ((__instance.UsesMF2Valve && (Object)(object)__instance.MF2Valve != (Object)null) ? __instance.MF2Valve.Lerp : 0.5f)); Instance.SampleTool((FVRPhysicalObject)(object)__instance, 1, ___m_isFiring && flag, throttle, contact: false, ((FVRPhysicalObject)__instance).AltGrip); } catch (Exception e) { Instance.Error(e); } } } [HarmonyPatch(typeof(Chainsaw), "FVRUpdate", new Type[] { })] private static class SawStatePatch { private static void Postfix(Chainsaw __instance, bool ___m_isRunning, float ___m_sawingIntesity) { if ((Object)(object)Instance == (Object)null) { return; } try { Instance.SampleTool((FVRPhysicalObject)(object)__instance, 2, ___m_isRunning, ___m_sawingIntesity, contact: false, __instance.Foregrip); } catch (Exception e) { Instance.Error(e); } } } [HarmonyPatch(typeof(Chainsaw), "OnCollisionStay", new Type[] { typeof(Collision) })] private static class SawContactPatch { private static void Postfix(Chainsaw __instance, Collision __0, bool ___m_isRunning, float ___m_sawingIntesity) { if ((Object)(object)Instance == (Object)null) { return; } try { Instance.SawBladeContact(__instance, __0, ___m_isRunning, ___m_sawingIntesity); } catch (Exception e) { Instance.Error(e); } } } private sealed class GrappleState { public GrappleGun Gun; public GrappleGunBolt First; public GrappleGunBolt Second; public bool FirstHit; public bool SecondHit; } private sealed class PullScope { public GrappleGun Gun; public bool Confirmed; public Vector3 Direction; } private sealed class BeamerSnapshot { public bool Object; public float Charge; } [HarmonyPatch(typeof(GBeamer), "FVRFixedUpdate", new Type[] { })] private static class SpecialBeamerStatePatch { private static void Prefix(bool ___m_hasObject, float ___m_capacitorCharge, out object __state) { __state = new BeamerSnapshot { Object = ___m_hasObject, Charge = ___m_capacitorCharge }; } private static void Postfix(GBeamer __instance, bool ___m_hasObject, bool ___m_isManipulating, float ___m_capacitorCharge, FVRPhysicalObject ___m_obj, bool ___m_isBatterySwitchedOn, bool ___m_isCapacitorSwitchedOn, bool ___m_isMotorSwitchedOn, object __state) { if ((Object)(object)Instance == (Object)null) { return; } try { BeamerSnapshot beamerSnapshot = (BeamerSnapshot)__state; bool active = Instance.specialEnabled.Value && ___m_hasObject && ___m_isManipulating && (Object)(object)___m_obj != (Object)null; float mass = (((Object)(object)___m_obj != (Object)null && (Object)(object)___m_obj.RootRigidbody != (Object)null) ? ___m_obj.RootRigidbody.mass : 0f); Instance.SampleTool((FVRPhysicalObject)(object)__instance, 3, active, SpecialModel.Load(mass), contact: false, ((FVRPhysicalObject)__instance).AltGrip); if (___m_hasObject && !beamerSnapshot.Object) { Instance.SpecialArms((FVRFireArm)(object)__instance, 4, 0.16f); } if (SpecialModel.ReadyCharge(beamerSnapshot.Charge, ___m_capacitorCharge, ___m_isBatterySwitchedOn && ___m_isCapacitorSwitchedOn && ___m_isMotorSwitchedOn)) { Instance.SpecialArms((FVRFireArm)(object)__instance, 7, 0.07f); } } catch (Exception e) { Instance.Error(e); } } } [HarmonyPatch(typeof(GBeamer), "ShuntHeldObject", new Type[] { })] private static class SpecialHeldShuntPatch { private static void Prefix(bool ___m_hasObject, out bool __state) { __state = ___m_hasObject; } private static void Postfix(GBeamer __instance, bool ___m_hasObject, bool __state) { if ((Object)(object)Instance != (Object)null && SpecialModel.ReleasedObject(__state, ___m_hasObject, succeeded: true)) { try { Instance.SpecialArms((FVRFireArm)(object)__instance, 2, 0.4f); } catch (Exception e) { Instance.Error(e); } } } } [HarmonyPatch(typeof(GBeamer), "WideShunt", new Type[] { })] private static class SpecialWideShuntPatch { private static void Prefix(float ___m_capacitorCharge, out float __state) { __state = ___m_capacitorCharge; } private static void Postfix(GBeamer __instance, float ___m_capacitorCharge, float __state) { if ((Object)(object)Instance != (Object)null && SpecialModel.SpentCharge(__state, ___m_capacitorCharge, succeeded: true)) { try { Instance.SpecialArms((FVRFireArm)(object)__instance, 3, 0.48f); } catch (Exception e) { Instance.Error(e); } } } } [HarmonyPatch(typeof(GrappleGun), "Fire", new Type[] { })] private static class SpecialGrappleFirePatch { private static void Postfix(GrappleGun __instance, bool __result, GrappleGunBolt ___m_firstShotBolt, GrappleGunBolt ___m_secondShotBolt) { if ((Object)(object)Instance != (Object)null && __result) { try { Instance.RegisterGrapple(__instance, ___m_firstShotBolt, ___m_secondShotBolt); } catch (Exception e) { Instance.Error(e); } } } } [HarmonyPatch(typeof(GrappleGun), "LateUpdate", new Type[] { })] private static class SpecialGrappleStatePatch { private static void Postfix(GrappleGun __instance, GrappleGunBolt ___m_firstShotBolt, GrappleGunBolt ___m_secondShotBolt, bool ___m_isRetracting) { if ((Object)(object)Instance != (Object)null) { try { Instance.ObserveGrapple(__instance, ___m_firstShotBolt, ___m_secondShotBolt, ___m_isRetracting); } catch (Exception e) { Instance.Error(e); } } } } [HarmonyPatch(typeof(GrappleGun), "InitiateRetract", new Type[] { })] private static class SpecialRetractPatch { private static void Prefix(bool ___m_isRetracting, out bool __state) { __state = ___m_isRetracting; } private static void Postfix(GrappleGun __instance, bool ___m_isRetracting, bool __state) { if ((Object)(object)Instance != (Object)null && !__state && ___m_isRetracting) { try { Instance.SpecialArms((FVRFireArm)(object)__instance, 5, 0.12f); } catch (Exception e) { Instance.Error(e); } } } } [HarmonyPatch(typeof(GrappleGun), "AttemptRetract", new Type[] { typeof(bool) })] private static class SpecialPullScopePatch { private static void Prefix(GrappleGun __instance, out object __state) { __state = null; if (!((Object)(object)Instance == (Object)null)) { PullScope pullScope = new PullScope(); pullScope.Gun = __instance; PullScope pullScope2 = pullScope; Instance.pulls.Push(pullScope2); __state = pullScope2; } } private static Exception Finalizer(object __state, Exception __exception) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Instance == (Object)null || __state == null) { return __exception; } PullScope pullScope = (PullScope)__state; if (Instance.pulls.Count > 0 && Instance.pulls.Peek() == pullScope) { Instance.pulls.Pop(); } else { Instance.pulls.Clear(); } if (__exception == null && pullScope.Confirmed) { try { Instance.EmitPull(pullScope.Gun, pullScope.Direction); } catch (Exception e) { Instance.Error(e); } } return __exception; } } [HarmonyPatch(typeof(FVRMovementManager), "Blast", new Type[] { typeof(Vector3), typeof(float), typeof(bool) })] private static class SpecialPullConfirmPatch { private static void Postfix(FVRMovementManager __instance, Vector3 __0, MovementMode ___Mode) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected I4, but got Unknown //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Instance != (Object)null && (Object)(object)__instance == (Object)(object)GM.CurrentMovementManager && Instance.pulls.Count > 0 && SpecialModel.AcceptsPull((int)___Mode) && FiniteVector(__0) && ((Vector3)(ref __0)).sqrMagnitude > 0.001f) { PullScope pullScope = Instance.pulls.Peek(); pullScope.Confirmed = true; pullScope.Direction = __0; } } } private sealed class HurtContext { public FVRPlayerHitbox Hitbox; public object Record; } [HarmonyPatch(typeof(FVRPlayerHitbox), "Damage", new Type[] { typeof(Damage) })] private static class DetailedHitboxDamagePatch { private static void Prefix(FVRPlayerHitbox __instance, Damage __0, out object __state) { __state = BeginHurt(__instance, __0); } private static Exception Finalizer(Exception __exception, object __state) { return EndHurt(__exception, __state); } } [HarmonyPatch(typeof(FVRPlayerHitbox), "Damage", new Type[] { typeof(DamageDealt) })] private static class ModernHitboxDamagePatch { private static void Prefix(FVRPlayerHitbox __instance, DamageDealt __0, out object __state) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) __state = BeginHurt(__instance, __0); } private static Exception Finalizer(Exception __exception, object __state) { return EndHurt(__exception, __state); } } [HarmonyPatch(typeof(FVRPlayerHitbox), "Damage", new Type[] { typeof(float) })] private static class ScalarHitboxDamagePatch { private static void Prefix(FVRPlayerHitbox __instance, float __0, out object __state) { __state = BeginHurt(__instance, __0); } private static Exception Finalizer(Exception __exception, object __state) { return EndHurt(__exception, __state); } } [HarmonyPatch(typeof(FVRPlayerBody), "RegisterPlayerHit", new Type[] { typeof(float), typeof(bool), typeof(int) })] private static class RegisteredHitPatch { private static void Prefix(out bool __state) { FVRSceneSettings currentSceneSettings = GM.CurrentSceneSettings; __state = (Object)(object)currentSceneSettings != (Object)null && currentSceneSettings.DoesDamageGetRegistered && (Object)(object)currentSceneSettings.DeathResetPoint != (Object)null && !GM.IsDead(); } private static void Postfix(FVRPlayerBody __instance, float __0, bool __state) { if ((Object)(object)Instance == (Object)null || !__state || __0 <= 0f) { return; } try { Instance.CaptureHurt(__instance, __0, blind: false); } catch (Exception e) { Instance.Error(e); } } } [HarmonyPatch(typeof(FVRPlayerBody), "BlindPlayer", new Type[] { typeof(float) })] private static class BlindPatch { private static void Postfix(FVRPlayerBody __instance, float __0) { if ((Object)(object)Instance == (Object)null || __0 <= 0f) { return; } try { Instance.CaptureHurt(__instance, __0, blind: true); } catch (Exception e) { Instance.Error(e); } } } private sealed class HandlingCue { public FVRFireArm Gun; public FVRViveHand Hand; public HandlingKind Kind; public float At; public int Frame; } private sealed class HandlingMagState { public FVRFireArmMagazine Magazine; public FVRViveHand Hand; public bool Eligible; } [HarmonyPatch(typeof(FVRFireArm), "PlayAudioEvent", new Type[] { typeof(FirearmAudioEventType), typeof(float) })] private static class HandlingAudioPatch { private static void Postfix(FVRFireArm __instance, FirearmAudioEventType __0) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Instance == (Object)null) { return; } try { Instance.CaptureHandlingAudio(__instance, __0); } catch (Exception e) { Instance.Error(e); } } } [HarmonyPatch(typeof(FVRFireArm), "LoadMag", new Type[] { typeof(FVRFireArmMagazine) })] private static class HandlingMagInPatch { private static void Prefix(FVRFireArm __instance, FVRFireArmMagazine __0, out object __state) { __state = null; if ((Object)(object)Instance == (Object)null || !Instance.HandlingPlayable()) { return; } try { __state = HandlingSnapshot(__0, (Object)(object)__instance.Magazine == (Object)null && (Object)(object)__0 != (Object)null, __instance); } catch (Exception e) { if ((Object)(object)Instance != (Object)null) { Instance.Error(e); } } } private static void Postfix(FVRFireArm __instance, object __state) { try { HandlingMagState handlingMagState = __state as HandlingMagState; if ((Object)(object)Instance != (Object)null && handlingMagState != null && handlingMagState.Eligible && (Object)(object)__instance.Magazine == (Object)(object)handlingMagState.Magazine) { Instance.QueueHandling(__instance, handlingMagState.Hand, HandlingKind.MagazineIn); } } catch (Exception e) { if ((Object)(object)Instance != (Object)null) { Instance.Error(e); } } } } [HarmonyPatch(typeof(FVRFireArm), "EjectMag", new Type[] { typeof(bool) })] private static class HandlingMagOutPatch { private static void Prefix(FVRFireArm __instance, out object __state) { __state = null; if ((Object)(object)Instance == (Object)null || !Instance.HandlingPlayable()) { return; } try { __state = HandlingSnapshot(__instance.Magazine, (Object)(object)__instance.Magazine != (Object)null, __instance); } catch (Exception e) { if ((Object)(object)Instance != (Object)null) { Instance.Error(e); } } } private static void Postfix(FVRFireArm __instance, object __state) { try { HandlingMagState handlingMagState = __state as HandlingMagState; if ((Object)(object)Instance != (Object)null && handlingMagState != null && handlingMagState.Eligible && (Object)(object)__instance.Magazine == (Object)null) { Instance.QueueHandling(__instance, handlingMagState.Hand, HandlingKind.MagazineOut); } } catch (Exception e) { if ((Object)(object)Instance != (Object)null) { Instance.Error(e); } } } } [HarmonyPatch(typeof(FVRFireArm), "LoadMagIntoSecondary", new Type[] { typeof(FVRFireArmMagazine), typeof(int) })] private static class HandlingSecondaryInPatch { private static void Prefix(FVRFireArm __instance, FVRFireArmMagazine __0, int __1, out object __state) { __state = null; if ((Object)(object)Instance == (Object)null || !Instance.HandlingPlayable()) { return; } try { bool flag = __instance.SecondaryMagazineSlots != null && __1 >= 0 && __1 < __instance.SecondaryMagazineSlots.Length; __state = HandlingSnapshot(__0, flag && (Object)(object)__0 != (Object)null && (Object)(object)__instance.SecondaryMagazineSlots[__1].Magazine == (Object)null, __instance); } catch (Exception e) { if ((Object)(object)Instance != (Object)null) { Instance.Error(e); } } } private static void Postfix(FVRFireArm __instance, int __1, object __state) { try { HandlingMagState handlingMagState = __state as HandlingMagState; if ((Object)(object)Instance != (Object)null && handlingMagState != null && handlingMagState.Eligible && (Object)(object)__instance.SecondaryMagazineSlots[__1].Magazine == (Object)(object)handlingMagState.Magazine) { Instance.QueueHandling(__instance, handlingMagState.Hand, HandlingKind.MagazineIn); } } catch (Exception e) { if ((Object)(object)Instance != (Object)null) { Instance.Error(e); } } } } [HarmonyPatch(typeof(FVRFireArm), "EjectSecondaryMagFromSlot", new Type[] { typeof(int), typeof(bool) })] private static class HandlingSecondaryOutPatch { private static void Prefix(FVRFireArm __instance, int __0, out object __state) { __state = null; if ((Object)(object)Instance == (Object)null || !Instance.HandlingPlayable()) { return; } try { FVRFireArmMagazine val = ((__instance.SecondaryMagazineSlots != null && __0 >= 0 && __0 < __instance.SecondaryMagazineSlots.Length) ? __instance.SecondaryMagazineSlots[__0].Magazine : null); __state = HandlingSnapshot(val, (Object)(object)val != (Object)null, __instance); } catch (Exception e) { if ((Object)(object)Instance != (Object)null) { Instance.Error(e); } } } private static void Postfix(FVRFireArm __instance, int __0, object __state) { try { HandlingMagState handlingMagState = __state as HandlingMagState; if ((Object)(object)Instance != (Object)null && handlingMagState != null && handlingMagState.Eligible && (Object)(object)__instance.SecondaryMagazineSlots[__0].Magazine == (Object)null) { Instance.QueueHandling(__instance, handlingMagState.Hand, HandlingKind.MagazineOut); } } catch (Exception e) { if ((Object)(object)Instance != (Object)null) { Instance.Error(e); } } } } private sealed class Shot { public FVRFireArm Weapon; public int Frame; public float Momentum; public float Energy; public bool OrdinaryBallistic = true; public int Projectiles; public int PerRoundProjectiles; public bool HasPayload; public bool MixedAmmo; public bool AttachmentShot; public string Round = "unknown"; public string Variant = "unknown"; public bool HasRecoil; public bool TwoHanded; public bool Foregrip; public bool Shouldered; public bool Right; public float Z; public float XY; public float Mass = 2f; public float StockX = 0.5f; public bool StockLocation; public Vector3 MuzzlePosition; public bool HasMuzzle; public bool Suppressed; public bool Braked; public bool IsMinigun; public LaunchKind Launch; public bool HasHand; public Component Emitter; public float LaunchVelocity; public Vector3 LaunchForward; public Vector3 BackblastPosition; public bool HasBackblast; } private sealed class Pulse { public int Channel; public int Source; public float Start; public float Duration; public float Strength; public float X; public float Y; public bool IsBlast; public bool Grid; public bool IsMinigun; public int Phase; public bool IsExplosion; public bool IsDamage; public HurtKind Hurt; public bool Echo; public bool LaunchPressure; public bool IsMelee; public float[] Pattern; public GutKind Gut; public bool IsPower; public bool PowerInverted; public int PowerType; public int PowerMode; public bool IsTool; public bool ToolContact; public int ToolKind; public int SpecialKind; public AmmoKind Ammo; public bool IsHandling; } [HarmonyPatch(typeof(FVRFireArm), "Fire", new Type[] { typeof(FVRFireArmChamber), typeof(Transform), typeof(bool), typeof(float), typeof(float) })] private static class FirePatch { private static void Prefix(FVRFireArm __instance, FVRFireArmChamber chamber, Transform muzzle, out object __state) { __state = null; if ((Object)(object)Instance == (Object)null) { return; } try { __state = Instance.BeginFire(__instance, chamber, muzzle); } catch (Exception e) { Instance.Error(e); } } private static Exception Finalizer(Exception __exception, object __state) { if ((Object)(object)Instance != (Object)null) { try { Instance.EndFire(__state as Shot, __exception == null); } catch (Exception e) { Instance.Error(e); } } return __exception; } } [HarmonyPatch(typeof(Minigun), "Fire", new Type[] { })] private static class MinigunFirePatch { private static void Prefix(Minigun __instance, out object __state) { __state = null; if ((Object)(object)Instance == (Object)null) { return; } try { __state = Instance.BeginMinigunFire(__instance); } catch (Exception e) { Instance.Error(e); } } private static Exception Finalizer(Exception __exception, object __state) { if ((Object)(object)Instance != (Object)null) { try { Instance.EndFire(__state as Shot, __exception == null); } catch (Exception e) { Instance.Error(e); } } return __exception; } } [HarmonyPatch(typeof(BallisticProjectile), "Fire", new Type[] { typeof(float), typeof(Vector3), typeof(FVRFireArm), typeof(bool) })] private static class ProjectilePatch { private static void Prefix(BallisticProjectile __instance, float muzzleVelocity, FVRFireArm firearm) { if ((Object)(object)Instance == (Object)null) { return; } try { Instance.CaptureProjectile(__instance, muzzleVelocity, firearm); } catch (Exception e) { Instance.Error(e); } } } [HarmonyPatch(typeof(FVRFireArm), "Recoil", new Type[] { typeof(bool), typeof(bool), typeof(bool), typeof(FVRFireArmRecoilProfile), typeof(float) })] private static class RecoilPatch { private static void Postfix(FVRFireArm __instance, bool twoHandStabilized, bool foregripStabilized, bool shoulderStabilized, FVRFireArmRecoilProfile overrideprofile) { if ((Object)(object)Instance == (Object)null) { return; } try { Instance.CaptureRecoil(__instance, twoHandStabilized, foregripStabilized, shoulderStabilized, overrideprofile); } catch (Exception e) { Instance.Error(e); } } } public const string Id = "local.h3vr.simhaptics"; private Harmony healingPatches; private ConfigEntry<bool> healingEnabled; private ConfigEntry<float> healingIntensity; private Harmony settingsPatches; private bool settingsDirty; private bool applyingPreset; private ConfigEntry<StrengthPreset> strengthPreset; private bool sliderSavePending; private float sliderSaveAt; private ConfigEntry<bool> explosionEnabled; private ConfigEntry<bool> explosionCover; private ConfigEntry<float> explosionGain; private ConfigEntry<float> explosionCeiling; private int explosionSequence = -100; private Harmony boomskeePatches; private bool boomskeeAvailable; private ConfigEntry<float> boomskeeRange; private ConfigEntry<float> boomskeeDistant; private ConfigEntry<float> recoilEcho; private ConfigEntry<float> blastEcho; private ConfigEntry<float> damageEcho; private ConfigEntry<float> echoDelay; private ConfigEntry<bool> launchEnabled; private ConfigEntry<float> launchArmGain; private ConfigEntry<float> launchPressureGain; private ConfigEntry<float> launchCeiling; private ConfigEntry<float> launchPressureArmGain; private Harmony launchFixPatches; private bool attachmentCaptureAvailable; private int launchSubmunitionDepth; private ConfigEntry<bool> meleeEnabled; private ConfigEntry<float> meleeGain; private ConfigEntry<float> meleeCeiling; private ConfigEntry<float> meleeSupport; private readonly Dictionary<int, float> meleeTimes = new Dictionary<int, float>(); private readonly float[] meleeHandTimes = new float[2] { -100f, -100f }; private ConfigEntry<bool> deathEnabled; private ConfigEntry<float> deathIntensity; private ConfigEntry<float> deathDuration; private bool deathActive; private float deathStart; private float deathLength; private float deathStrength; private FVRPlayerBody deathBody; private ConfigEntry<bool> ingestEnabled; private ConfigEntry<bool> vomitEnabled; private ConfigEntry<float> ingestIntensity; private ConfigEntry<float> vomitIntensity; private readonly Stack<EatContext> eating = new Stack<EatContext>(); private Harmony powerPatches; private ConfigEntry<bool> powerEnabled; private ConfigEntry<bool> powerReminders; private ConfigEntry<float> powerIntensity; private ConfigEntry<float> reminderIntensity; private bool powerAvailable; private float lastNonPower = -100f; private float nextPowerPoll; private float nextReminder; private FVRPlayerBody trackedPowerBody; private PowerSnapshot trackedPower; private readonly List<PowerCue> powerCues = new List<PowerCue>(); private static readonly string[] BuffNames = new string[20] { "Healing", "DamResist", "DamPowerUp", "InfiniteAmmo", "Ghosted", "MuscleMeat", "Cyclops", "SnakeEyes", "Blort", "FarOutMan", "Hurting", "DamMult", "DamPowerDown", "AmmoDrain", "SuperVisible", "WeakMeat", "BiClops", "MoleEye", "Dlort", "BadTrip" }; private static readonly string[] TimerNames = new string[20] { "Heal", "DamResist", "DamPowerUp", "InfiniteAmmo", "Ghosted", "MuscleMeat", "Cyclops", "SnakeEyes", "Blort", "FarOutMan", "Hurt", "DamMult", "DamPowerDown", "AmmoDrain", "SuperVisible", "WeakMeat", "BiClops", "MoleEye", "Dlort", "BadTrip" }; private readonly FieldInfo[] powerFlags = new FieldInfo[20]; private readonly FieldInfo[] powerTimers = new FieldInfo[20]; private Harmony edgePatches; private ConfigEntry<bool> cannonEnabled; private ConfigEntry<bool> toolsEnabled; private ConfigEntry<float> cannonGain; private ConfigEntry<float> toolGain; private ConfigEntry<float> toolLimit; private readonly Dictionary<int, ToolState> tools = new Dictionary<int, ToolState>(); private readonly List<int> staleTools = new List<int>(); private Harmony specialPatches; private ConfigEntry<bool> specialEnabled; private ConfigEntry<float> specialGain; private ConfigEntry<float> specialLimit; private readonly Dictionary<int, GrappleState> grapples = new Dictionary<int, GrappleState>(); private readonly Stack<PullScope> pulls = new Stack<PullScope>(); private ConfigEntry<bool> damageEnabled; private ConfigEntry<float> damageGain; private ConfigEntry<float> damageCeiling; private readonly Stack<HurtContext> hurtContexts = new Stack<HurtContext>(); private int hurtSequence = -2000000; private ConfigEntry<bool> ammoEnabled; private ConfigEntry<bool> handlingEnabled; private ConfigEntry<float> handlingGain; private ConfigEntry<float> handlingLimit; private Harmony handlingPatches; private float lastHandlingCombat = -100f; private readonly float[] lastHandlingArm = new float[2] { -100f, -100f }; private readonly List<HandlingCue> handlingCues = new List<HandlingCue>(); internal static Plugin Instance; private Harmony patches; private Harmony meleePatches; private Harmony consumptionPatches; private HapticPlayer player; private volatile bool connected; private bool previousConnected; private float nextTick; private float nextError; private ConfigEntry<bool> enabledSetting; private ConfigEntry<bool> mirrorFront; private ConfigEntry<bool> blastEnabled; private ConfigEntry<float> blastGain; private ConfigEntry<float> blastShoulderRatio; private ConfigEntry<float> blastCeiling; private ConfigEntry<float> armGain; private ConfigEntry<float> vestGain; private ConfigEntry<float> limit; private ConfigEntry<float> minigunArmGain; private ConfigEntry<float> minigunBodyGain; private ConfigEntry<float> minigunCeiling; private int minigunPhase; private readonly List<Shot> shots = new List<Shot>(); private readonly List<Pulse> pulses = new List<Pulse>(); private readonly Stack<Shot> firing = new Stack<Shot>(); private readonly bool[] activeChannel = new bool[4]; private static readonly PositionType[] Positions = (PositionType[])(object)new PositionType[4] { (PositionType)10, (PositionType)11, (PositionType)201, (PositionType)202 }; private void ConfigureHealing() { healingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Powerups", "HealthPickupEnabled", true, "Healing wave for full/partial health cubes when they actually restore player health. Powerups Enabled also applies; combat takes priority."); healingIntensity = ((BaseUnityPlugin)this).Config.Bind<float>("Powerups", "HealingIntensity", 28f, Range("Health-cube wave intensity percent, scaled by health restored. Back and sleeves are softer; global ceiling applies.", 0f, 50f, 1f)); } private void InstallHealingHooks() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown try { healingPatches = new Harmony("local.h3vr.simhaptics.healing"); healingPatches.CreateClassProcessor(typeof(HealingPickupPatch)).Patch(); } catch (Exception ex) { if (healingPatches != null) { healingPatches.UnpatchSelf(); } ((BaseUnityPlugin)this).Logger.LogWarning((object)("Health pickup hook unavailable; other feedback remains active: " + ex.Message)); } } private static ConfigDescription Range(string description, float min, float max, float step) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown return new ConfigDescription(description, (AcceptableValueBase)(object)new PanelRange(min, max, step), new object[0]); } private void ConfigureSettingsPanel() { //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Expected O, but got Unknown strengthPreset = ((BaseUnityPlugin)this).Config.Bind<StrengthPreset>("General", "StrengthPreset", StrengthPreset.Standard, "Shortcut that fills the strength sliders: Weakest, Weaker, Standard (tested tuning), Stronger or Strongest. Changes gains and intensity limits together; restores tuned strength ratios. Preserves timing, distance ranges, enabled switches and orientation. You can edit sliders afterwards and use Apply preset to reapply this selection."); ((BaseUnityPlugin)this).Config.SaveOnConfigSet = true; ((BaseUnityPlugin)this).Config.SettingChanged += SettingsChanged; List<ConfigEntryBase> list = new List<ConfigEntryBase>(); string[] array = new string[19] { "General", "Recoil", "MuzzleBlast", "Explosions", "Launchers", "Damage", "Death", "Melee", "Handling", "Ammunition", "Minigun", "ContinuousWeapons", "Cannon", "SpecialWeapons", "Consumption", "Powerups", "BodyResponse", "Boomskee", "Calibration" }; string[] array2 = array; foreach (string text in array2) { foreach (ConfigDefinition key in ((BaseUnityPlugin)this).Config.Keys) { if (key.Section == text) { list.Add(((BaseUnityPlugin)this).Config[key]); } } } UniversalModPanel.RegisterPluginSettings(((BaseUnityPlugin)this).Info, list.ToArray()); settingsPatches = new Harmony("local.h3vr.simhaptics.settings"); settingsPatches.CreateClassProcessor(typeof(SettingsPagePatch)).Patch(); } private void SettingsChanged(object sender, SettingChangedEventArgs args) { if ((object)args.ChangedSetting == strengthPreset && !applyingPreset) { ApplySelectedPreset(); } settingsDirty = true; } private void ApplySettingsChanges() { if (sliderSavePending && Time.realtimeSinceStartup >= sliderSaveAt) { SaveSliderSettings(); } if (!settingsDirty) { return; } settingsDirty = false; shots.Clear(); pulses.Clear(); ClearHandling(); powerCues.Clear(); if (!deathEnabled.Value || !enabledSetting.Value) { deathActive = false; deathBody = null; } else if (deathActive) { deathStrength = DeathModel.Strength(deathIntensity.Value, limit.Value); } for (int i = 0; i < 4; i++) { if (player != null && connected && activeChannel[i]) { player.TurnOff(Key(i)); } activeChannel[i] = false; } nextTick = 0f; } internal void ResetSettings() { applyingPreset = true; try { SettingsStorage.Reset(((BaseUnityPlugin)this).Config); } finally { applyingPreset = false; } sliderSavePending = false; settingsDirty = true; } internal void ApplySelectedPreset() { applyingPreset = true; try { SettingsStorage.ApplyPreset(((BaseUnityPlugin)this).Config, strengthPreset.Value); } finally { applyingPreset = false; } sliderSavePending = false; settingsDirty = true; } internal void SetSliderValue(ConfigEntryBase entry, float value) { if (!(Math.Abs((float)entry.BoxedValue - value) < 1E-05f)) { bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; try { entry.BoxedValue = value; } finally { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } sliderSavePending = true; sliderSaveAt = Time.realtimeSinceStartup + 0.35f; } } internal void SaveSliderSettings() { if (!sliderSavePending) { return; } try { ((BaseUnityPlugin)this).Config.Save(); sliderSavePending = false; } catch (Exception e) { sliderSaveAt = Time.realtimeSinceStartup + 30f; Error(e); } } private void ConfigureExplosions() { explosionEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Explosions", "Enabled", true, "Directional detonation pressure, independent of muzzle blast."); explosionCover = ((BaseUnityPlugin)this).Config.Bind<bool>("Explosions", "RespectCover", true, "Use each blast's blocker settings; obstruction reduces pressure to 25 percent."); explosionGain = ((BaseUnityPlugin)this).Config.Bind<float>("Explosions", "Gain", 1f, Range("Explosion multiplier, clamped to 0..2.", 0f, 2f, 0.05f)); explosionCeiling = ((BaseUnityPlugin)this).Config.Bind<float>("Explosions", "MaximumIntensity", 65f, Range("Explosion percent ceiling; general ceiling also applies.", 0f, 100f, 1f)); } private void CaptureExplosion(Component component, float radius, float force, bool usesBlockers, int mask, bool ignoreTriggers, bool boomskee = false) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_007f: 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) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) if (!enabledSetting.Value || !explosionEnabled.Value || !connected || Time.timeScale <= 0f) { return; } FVRPlayerBody currentPlayerBody = GM.CurrentPlayerBody; if ((Object)(object)currentPlayerBody == (Object)null || (Object)(object)currentPlayerBody.TorsoTransform == (Object)null) { return; } Transform torsoTransform = currentPlayerBody.TorsoTransform; Vector3 val = torsoTransform.position - torsoTransform.up * 0.22f; Vector3 position = component.transform.position; Vector3 val2 = val - position; float magnitude = ((Vector3)(ref val2)).magnitude; bool covered = false; RaycastHit val3 = default(RaycastHit); if (explosionCover.Value && usesBlockers && mask != 0 && magnitude > 0.01f && Physics.Raycast(position, ((Vector3)(ref val2)).normalized, ref val3, magnitude, mask, (QueryTriggerInteraction)(ignoreTriggers ? 1 : 0))) { covered = (Object)(object)((RaycastHit)(ref val3)).collider != (Object)null && !((Component)((RaycastHit)(ref val3)).collider).CompareTag("noOcclude") && (Object)(object)((Component)((RaycastHit)(ref val3)).collider).transform.root != (Object)(object)((Component)currentPlayerBody).transform.root; } float num = (boomskee ? ExplosionModel.BoomskeeStrength(radius, force, magnitude, boomskeeRange.Value, boomskeeDistant.Value / 100f, explosionGain.Value, explosionCeiling.Value / 100f) : ExplosionModel.Strength(radius, force, magnitude, covered, explosionGain.Value, explosionCeiling.Value / 100f)); if (num < 0.005f) { return; } Vector3 val4 = torsoTransform.InverseTransformDirection(position - val); Vector3 normalized = ((Vector3)(ref val4)).normalized; float num2 = RecoilModel.Clamp(0.5f - normalized.y * 0.45f, 0f, 1f); float num3 = ExplosionModel.Duration(radius); if (boomskee) { num3 = Math.Max(0.3f, num3); } float realtimeSinceStartup = Time.realtimeSinceStartup; int source = explosionSequence--; if (explosionSequence < -1000000) { explosionSequence = -100; } for (int i = 0; i < 4; i++) { float strength = ((i < 2) ? (num * 0.55f * ExplosionModel.Facing((i == 1) ? normalized.x : (0f - normalized.x))) : num); AddPulse(i, source, realtimeSinceStartup, num3, strength, 0.5f, num2, blast: false, i >= 2); Pulse pulse = pulses[pulses.Count - 1]; pulse.IsExplosion = true; if (i >= 2) { pulse.Pattern = ExplosionModel.Panel(i == 3, normalized.x, normalized.z, num2); } } } private void ConfigureBoomskee() { boomskeeRange = ((BaseUnityPlugin)this).Config.Bind<float>("Boomskee", "RangeMeters", 150f, Range("Expanded haptic reach for Boomskee detonations, 20..300 metres. Does not change game explosions.", 20f, 300f, 5f)); boomskeeDistant = ((BaseUnityPlugin)this).Config.Bind<float>("Boomskee", "DistantIntensity", 12f, Range("Faint distant blast presence percent, 0..25, fading at the range boundary. Explosion/general ceilings still apply.", 0f, 25f, 1f)); } private void InstallBoomskeeHooks() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown try { boomskeePatches = new Harmony("local.h3vr.simhaptics.boomskee"); boomskeePatches.CreateClassProcessor(typeof(BoomskeeDetonationPatch)).Patch(); boomskeeAvailable = true; } catch (Exception ex) { if (boomskeePatches != null) { boomskeePatches.UnpatchSelf(); } boomskeeAvailable = false; ((BaseUnityPlugin)this).Logger.LogWarning((object)("Boomskee hooks unavailable; other haptics remain active: " + ex)); } } private bool BoomskeeOwnsExplosion(Component component) { if (boomskeeAvailable) { return (Object)(object)component.GetComponent<BoomskeeExplosion>() != (Object)null; } return false; } private void ConfigureEcho() { recoilEcho = ((BaseUnityPlugin)this).Config.Bind<float>("BodyResponse", "RecoilRatio", 0.12f, Range("Opposite shoulder response as fraction of capped shouldered recoil; 0..0.3, zero disables.", 0f, 0.3f, 0.01f)); blastEcho = ((BaseUnityPlugin)this).Config.Bind<float>("BodyResponse", "MuzzleBlastRatio", 0.08f, Range("Soft back pressure as fraction of capped muzzle blast; 0..0.3, zero disables. The MuzzleBlast setting controls both.", 0f, 0.3f, 0.01f)); damageEcho = ((BaseUnityPlugin)this).Config.Bind<float>("BodyResponse", "DamageRatio", 0.15f, Range("Opposite vest response for blunt/explosive torso or head impacts only; 0..0.3, zero disables.", 0f, 0.3f, 0.01f)); echoDelay = ((BaseUnityPlugin)this).Config.Bind<float>("BodyResponse", "DelayMilliseconds", 30f, Range("Response delay, clamped to 20..40 milliseconds.", 20f, 40f, 1f)); } private void AddEcho(int primaryChannel, int source, float now, float duration, float primary, float ratio, float x, float y, bool blast, bool damage, HurtKind kind, AmmoKind ammo = AmmoKind.None) { float num = EchoModel.Strength(primary, ratio, limit.Value / 100f); if (!(num <= 0f)) { AddPulse((primaryChannel == 2) ? 3 : 2, source, now + RecoilModel.Clamp(echoDelay.Value, 20f, 40f) / 1000f, duration, num, EchoModel.OppositeX(x), y, blast, grid: true, minigun: false, 0, echo: true); Pulse pulse = pulses[pulses.Count - 1]; pulse.IsDamage = damage; pulse.Hurt = kind; pulse.Ammo = ammo; } } private void ConfigureLaunch() { launchEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Launchers", "Enabled", true, "Distinct grenade/rocket/mortar launches, independent of rifle muzzle blast."); launchArmGain = ((BaseUnityPlugin)this).Config.Bind<float>("Launchers", "SleeveGain", 1f, Range("Held launch contact gain, 0..2. Mortars never fabricate hand recoil.", 0f, 2f, 0.05f)); launchPressureGain = ((BaseUnityPlugin)this).Config.Bind<float>("Launchers", "PressureGain", 1f, Range("Nearby launch pressure gain, 0..2.", 0f, 2f, 0.05f)); launchPressureArmGain = ((BaseUnityPlugin)this).Config.Bind<float>("Launchers", "PressureSleeveGain", 1f, Range("Both-sleeve atmospheric launch pressure gain, 0..2, independent of held contact. Zero disables; includes unheld mortars.", 0f, 2f, 0.05f)); launchCeiling = ((BaseUnityPlugin)this).Config.Bind<float>("Launchers", "MaximumIntensity", 65f, Range("Launch intensity percent ceiling; general ceiling also applies.", 0f, 100f, 1f)); } private void EmitLaunch(Shot s, float now) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0149: 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_02bc: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Unknown result type (might be due to invalid IL or missing references) //IL_02c3: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_02d1: Unknown result type (might be due to invalid IL or missing references) if (!launchEnabled.Value || s.Projectiles <= 0 || !s.HasMuzzle) { return; } FVRPlayerBody currentPlayerBody = GM.CurrentPlayerBody; if ((Object)(object)currentPlayerBody == (Object)null || (Object)(object)currentPlayerBody.TorsoTransform == (Object)null) { return; } Transform torsoTransform = currentPlayerBody.TorsoTransform; Vector3 val = torsoTransform.position - torsoTransform.up * 0.22f; Vector3 val2 = (s.HasBackblast ? s.BackblastPosition : s.MuzzlePosition); float distance = Vector3.Distance(val2, val); float num = RecoilModel.Clamp(launchCeiling.Value / 100f, 0f, 1f); float num2 = RecoilModel.Clamp(LaunchModel.Arm(s.Launch, s.Momentum) * RecoilModel.Clamp(launchArmGain.Value, 0f, 2f), 0f, num); float num3 = RecoilModel.Clamp(LaunchModel.Pressure(s.Launch, distance) * LaunchModel.ChargeFactor(s.Launch, s.LaunchVelocity) * RecoilModel.Clamp(launchPressureGain.Value, 0f, 2f), 0f, num); if (s.HasBackblast) { float num4 = num3; Vector3 val3 = val - val2; num3 = num4 * (0.65f + 0.35f * RecoilModel.Clamp(Vector3.Dot(((Vector3)(ref val3)).normalized, -s.LaunchForward), 0f, 1f)); } int source = (((Object)(object)s.Emitter != (Object)null) ? ((Object)s.Emitter).GetInstanceID() : ((Object)s.Weapon).GetInstanceID()); float duration = LaunchModel.Duration(s.Launch); if (s.HasHand && num2 > 0f) { AddPulse(s.Right ? 1 : 0, source, now, duration, num2, 0.5f, 0.5f); if (s.TwoHanded) { AddPulse((!s.Right) ? 1 : 0, source, now, duration, num2 * 0.55f, 0.5f, 0.5f); } if (s.Shouldered && (s.Launch == LaunchKind.GrenadeLow || s.Launch == LaunchKind.GrenadeHigh || s.Launch == LaunchKind.Spigot)) { float x = RecoilModel.ShoulderContactX(s.Right, s.StockLocation, s.StockX); float num5 = LaunchModel.Shoulder(num2, vestGain.Value, num); AddPulse(2, source, now, duration, num5, x, 0.1f, blast: false, grid: true); AddEcho(2, source, now, duration, num5, recoilEcho.Value, x, 0.1f, blast: false, damage: false, HurtKind.Generic); } } Vector3 val4 = torsoTransform.InverseTransformDirection(val2 - val); Vector3 normalized = ((Vector3)(ref val4)).normalized; float num6 = RecoilModel.Clamp(0.5f - normalized.y * 0.4f, 0f, 1f); for (int i = 0; i < 4; i++) { if (!(num3 > 0f)) { break; } float strength = ((i < 2) ? LaunchModel.SleevePressure(num3, (i == 1) ? normalized.x : (0f - normalized.x), launchPressureArmGain.Value, num) : num3); AddPulse(i, source, now, duration, strength, 0.5f, num6, blast: false, i >= 2, minigun: false, 0, echo: true); Pulse pulse = pulses[pulses.Count - 1]; pulse.IsExplosion = true; pulse.LaunchPressure = true; if (i >= 2) { pulse.Pattern = ExplosionModel.Panel(i == 3, normalized.x, normalized.z, num6); } } } private Shot BeginAttachment(AttachableFirearm attachment, FVRFireArm gun, FVRFireArmChamber chamber, Transform muzzle) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) LaunchKind launchKind = LaunchModel.Kind(((object)chamber.RoundType).ToString(), ((object)attachment).GetType().Name); if (launchKind == LaunchKind.None) { return null; } FVRFireArm val = gun; if ((Object)(object)val == (Object)null && (Object)(object)attachment.Attachment != (Object)null && (Object)(object)((FVRFireArmAttachment)attachment.Attachment).curMount != (Object)null) { FVRPhysicalObject rootObject = ((FVRFireArmAttachment)attachment.Attachment).GetRootObject(); val = (FVRFireArm)(object)((rootObject is FVRFireArm) ? rootObject : null); } Shot shot = new Shot(); shot.Weapon = val; shot.AttachmentShot = true; shot.Emitter = (Component)(object)attachment; shot.Launch = launchKind; shot.Frame = Time.frameCount; shot.Round = ((object)chamber.RoundType).ToString(); FVRViveHand val2 = (((Object)(object)attachment.Interface != (Object)null) ? ((FVRInteractiveObject)attachment.Interface).m_hand : null); if ((Object)(object)val2 == (Object)null && (Object)(object)attachment.Attachment != (Object)null) { val2 = ((FVRInteractiveObject)attachment.Attachment).m_hand; } if ((Object)(object)val2 == (Object)null && (Object)(object)val != (Object)null) { val2 = ((FVRInteractiveObject)val).m_hand; } shot.HasHand = (Object)(object)val2 != (Object)null; shot.Right = (Object)(object)val2 != (Object)null && val2.IsThisTheRightHand; shot.TwoHanded = (Object)(object)val != (Object)null && val.IsTwoHandStabilized(); shot.Shouldered = (Object)(object)val != (Object)null && val.IsShoulderStabilized(); shot.Foregrip = (Object)(object)val != (Object)null && val.IsForegripStabilized(); shot.TwoHanded |= shot.Foregrip || ((Object)(object)val != (Object)null && (Object)(object)((FVRInteractiveObject)val).m_hand != (Object)null && (Object)(object)val2 != (Object)null && (Object)(object)((FVRInteractiveObject)val).m_hand != (Object)(object)val2); CaptureStockLocation(shot, val); if ((Object)(object)muzzle != (Object)null) { shot.MuzzlePosition = muzzle.position; shot.HasMuzzle = true; } shots.Add(shot); firing.Push(shot); return shot; } private void InstallLaunchCaptureFix() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown try { launchFixPatches = new Harmony("local.h3vr.simhaptics.launchcapture"); launchFixPatches.CreateClassProcessor(typeof(LaunchSecondaryScopePatch)).Patch(); launchFixPatches.CreateClassProcessor(typeof(LaunchLaserStatePatch)).Patch(); attachmentCaptureAvailable = true; } catch (Exception ex) { if (launchFixPatches != null) { launchFixPatches.UnpatchSelf(); } ((BaseUnityPlugin)this).Logger.LogWarning((object)("Attachment capture fix unavailable; other haptics remain active: " + ex)); } } private void ConfigureMelee() { meleeEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Melee", "Enabled", true, "Outgoing held-weapon contact feedback, independent of incoming damage."); meleeGain = ((BaseUnityPlugin)this).Config.Bind<float>("Melee", "SleeveGain", 1f, Range("Strike feedback multiplier, 0..2.", 0f, 2f, 0.05f)); meleeCeiling = ((BaseUnityPlugin)this).Config.Bind<float>("Melee", "MaximumIntensity", 45f, Range("Melee percent ceiling; general ceiling also applies.", 0f, 100f, 1f)); meleeSupport = ((BaseUnityPlugin)this).Config.Bind<float>("Melee", "SupportRatio", 0.35f, Range("Support grip fraction when actually held, 0..0.6.", 0f, 0.6f, 0.01f)); } private void MeleeCollision(FVRPhysicalObject obj, Collision col) { //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) FVRPhysicalObject val = MeleeOwner(obj); if ((Object)(object)val == (Object)null || col == null || col.contacts.Length == 0 || (!(obj is FVRFireArm) && !(obj is FVRKnife) && (obj.MP == null || !obj.MP.IsMeleeWeapon))) { return; } Collider collider = col.collider; if ((Object)(object)collider == (Object)null || (Object)(object)collider.attachedRigidbody == (Object)(object)val.RootRigidbody || ((Component)collider).transform.IsChildOf(((Component)val).transform) || (obj.MP != null && obj.MP.IgnoreRBs != null && (Object)(object)collider.attachedRigidbody != (Object)null && obj.MP.IgnoreRBs.Contains(collider.attachedRigidbody))) { return; } float num = 0f; float num2 = 0f; ContactPoint[] contacts = col.contacts; for (int i = 0; i < contacts.Length; i++) { ContactPoint val2 = contacts[i]; num = Math.Max(num, Math.Abs(Vector3.Dot(col.relativeVelocity, ((ContactPoint)(ref val2)).normal))); if ((Object)(object)val.RootRigidbody != (Object)null) { float val3 = num2; Vector3 pointVelocity = val.RootRigidbody.GetPointVelocity(((ContactPoint)(ref val2)).point); num2 = Math.Max(val3, ((Vector3)(ref pointVelocity)).magnitude); } } num2 = Math.Max(num2, ((Vector3)(ref ((FVRInteractiveObject)val).m_hand.Input.VelLinearWorld)).magnitude); EmitMelee(obj, collider, num2, num, "collision"); } private void EmitMelee(FVRPhysicalObject obj, Collider target, float own, float speed, string path) { //IL_0134: Unknown result type (might be due to invalid IL or missing references) obj = MeleeOwner(obj); if ((Object)(object)obj == (Object)null || (Object)(object)target == (Object)null || !meleeEnabled.Value || !enabledSetting.Value || !connected || deathActive || GM.IsDead() || Time.timeScale <= 0f || ((Component)target).transform.IsChildOf(((Component)obj).transform) || (Object)(object)target.attachedRigidbody == (Object)(object)obj.RootRigidbody || (Object)(object)((Component)target).GetComponentInParent<FVRPlayerHitbox>() != (Object)null || (Object)(object)((Component)target).GetComponentInParent<FVRViveHand>() != (Object)null) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; float value = -100f; int instanceID = ((Object)obj).GetInstanceID(); int num = (((FVRInteractiveObject)obj).m_hand.IsThisTheRightHand ? 1 : 0); meleeTimes.TryGetValue(instanceID, out value); if (!meleeTimes.ContainsKey(instanceID)) { value = -100f; } if (!MeleeModel.Eligible(own, speed, realtimeSinceStartup - value) || realtimeSinceStartup - meleeHandTimes[num] < 0.065f) { return; } PMat componentInParent = ((Component)target).GetComponentInParent<PMat>(); string text = (((Object)(object)componentInParent != (Object)null && (Object)(object)componentInParent.Def != (Object)null) ? ((object)componentInParent.Def.material).ToString() : "Unknown"); int num2; switch (text) { default: num2 = ((text == "Styrofoam") ? 1 : 0); break; case "Flesh": case "BotMeat": case "RotMeat": case "Rubber": case "Cloth": case "Water": num2 = 1; break; } bool soft = (byte)num2 != 0; if (text == "Unknown" && (Object)(object)((Component)target).GetComponentInParent<SosigLink>() != (Object)null) { soft = true; } float mass = (((Object)(object)obj.RootRigidbody != (Object)null) ? obj.RootRigidbody.mass : 1f); float num3 = MeleeModel.Strength(speed, mass, soft, meleeGain.Value, meleeCeiling.Value / 100f); if (!(num3 <= 0f)) { if (meleeTimes.Count >= 256) { meleeTimes.Clear(); } meleeTimes[instanceID] = realtimeSinceStartup; meleeHandTimes[num] = realtimeSinceStartup; float duration = MeleeModel.Duration(soft, num3); AddPulse(num, instanceID, realtimeSinceStartup, duration, num3, 0.5f, 0.5f, blast: false, grid: false, minigun: false, 0, echo: false, melee: true); if ((Object)(object)obj.AltGrip != (Object)null && (Object)(object)((FVRInteractiveObject)obj.AltGrip).m_hand != (Object)null && (Object)(object)((FVRInteractiveObject)obj.AltGrip).m_hand != (Object)(object)((FVRInteractiveObject)obj).m_hand) { int num4 = (((FVRInteractiveObject)obj.AltGrip).m_hand.IsThisTheRightHand ? 1 : 0); AddPulse(num4, instanceID, realtimeSinceStartup, duration, num3 * RecoilModel.Clamp(meleeSupport.Value, 0f, 0.6f), 0.5f, 0.5f, blast: false, grid: false, minigun: false, 0, echo: false, melee: true); meleeHandTimes[num4] = realtimeSinceStartup; } } } private static FVRPhysicalObject MeleeOwner(FVRPhysicalObject obj) { if ((Object)(object)obj == (Object)null) { return null; } if ((Object)(object)((FVRInteractiveObject)obj).m_hand != (Object)null) { return obj; } FVRFireArmAttachment val = (FVRFireArmAttachment)(object)((obj is FVRFireArmAttachment) ? obj : null); if ((Object)(object)val == (Object)null || (Object)(object)val.curMount == (Object)null) { return null; } FVRPhysicalObject rootObject = val.GetRootObject(); if (!((Object)(object)rootObject != (Object)null) || !((Object)(object)((FVRInteractiveObject)rootObject).m_hand != (Object)null)) { return null; } return rootObject; } private void ConfigureDeath() { deathEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Death", "Enabled", true, "Confirmed death: torso impact, descending rumble and fading sleeves. Takes priority over other effects."); deathIntensity = ((BaseUnityPlugin)this).Config.Bind<float>("Death", "MaximumIntensity", 65f, Range("Death intensity percent; general ceiling applies.", 0f, 100f, 1f)); deathDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Death", "DurationSeconds", 0.9f, Range("Sequence duration, clamped to 0.5..1.5 seconds; respawn cancels immediately.", 0.5f, 1.5f, 0.05f)); } private void BeginDeath() { if (deathEnabled.Value && enabledSetting.Value && connected && !(Time.timeScale <= 0f)) { StopOwnEffects(); shots.Clear(); deathBody = GM.CurrentPlayerBody; deathStart = Time.realtimeSinceStartup; deathLength = DeathModel.Duration(deathDuration.Value); deathStrength = DeathModel.Strength(deathIntensity.Value, limit.Value); deathActive = (Object)(object)deathBody != (Object)null && deathStrength > 0f; nextTick = deathStart; } } private void TickDeath(float now) { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown if (!GM.IsDead() || (Object)(object)deathBody != (Object)(object)GM.CurrentPlayerBody || now >= deathStart + deathLength) { StopOwnEffects(); return; } for (int i = 0; i < 4; i++) { float[] array = DeathModel.Frame(i, now - deathStart, deathLength, deathStrength); List<DotPoint> list = new List<DotPoint>(); for (int j = 0; j < array.Length; j++) { int num = (int)Math.Round(RecoilModel.Clamp(array[j] * 100f, 0f, RecoilModel.Clamp(limit.Value, 0f, 100f))); if (num > 0) { list.Add(new DotPoint(j, num)); } } if (list.Count > 0) { player.Submit(Key(i), Positions[i], list, 35); activeChannel[i] = true; } else if (activeChannel[i]) { player.TurnOff(Key(i)); activeChannel[i] = false; } } } private void ConfigureGut() { ingestEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Consumption", "IngestEnabled", true, "Physical ingestion only, independent of power-up status."); vomitEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Consumption", "VomitEnabled", true, "Successful player item vomiting only; excludes dry heaving/NPCs."); ingestIntensity = ((BaseUnityPlugin)this).Config.Bind<float>("Consumption", "IngestIntensity", 18f, Range("Downward front-vest wave percent; general ceiling applies.", 0f, 100f, 1f)); vomitIntensity = ((BaseUnityPlugin)this).Config.Bind<float>("Consumption", "VomitIntensity", 40f, Range("Two upward contractions percent; general ceiling applies.", 0f, 100f, 1f)); } private void EmitGut(GutKind kind, string item) { if (!enabledSetting.Value || !connected || Time.timeScale <= 0f || deathActive || GM.IsDead() || (Object)(object)GM.CurrentPlayerBody == (Object)null || ((kind == GutKind.Ingest) ? (!ingestEnabled.Value) : (!vomitEnabled.Value))) { return; } float num = DeathModel.Strength((kind == GutKind.Ingest) ? ingestIntensity.Value : vomitIntensity.Value, limit.Value); if (num <= 0f) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (kind == GutKind.Vomit) { pulses.RemoveAll((Pulse p) => p.Gut == GutKind.Ingest); } for (int num2 = 2; num2 < 4; num2++) { AddPulse(num2, (kind == GutKind.Ingest) ? (-300001) : (-300002), realtimeSinceStartup, GutModel.Duration(kind), num * ((num2 == 3) ? ((kind == GutKind.Ingest) ? 0.12f : 0.25f) : 1f), 0.5f, 0.5f, blast: false, grid: true); pulses[pulses.Count - 1].Gut = kind; } } private static EatContext BeginEating(Component obj) { //IL_001a: 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_0046: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Instance == (Object)null) { return null; } AudioEvent sound = null; if (obj is EdibleThing) { sound = ((EdibleThing)obj).EatSound; } else if (obj is RotrwMeatCore) { sound = ((RotrwMeatCore)obj).AudEvent_Eat; } else if (obj is RotrwHerb) { sound = ((RotrwHerb)obj).AudEvent_Eat; } else if (obj is RotrwBangerJunk) { sound = ((RotrwBangerJunk)obj).AudEvent_Eat; } else if (obj is BBQRapierSausage) { sound = ((BBQRapierSausage)obj).AudEvent_Nom; } else if (obj is RW_Powerup) { sound = ((RW_Powerup)obj).AudEvent_DIng; } EatContext eatContext = new EatContext(); eatContext.Sound = sound; eatContext.Item = ((Object)obj).name; EatContext eatContext2 = eatContext; Instance.eating.Push(eatContext2); return eatContext2; } private static void ConfirmEating(AudioEvent sound) { if ((Object)(object)Instance == (Object)null || Instance.eating.Count == 0) { return; } EatContext eatContext = Instance.eating.Peek(); if (eatContext.Emitted || eatContext.Sound == null || !object.ReferenceEquals(eatContext.Sound, sound)) { return; } eatContext.Emitted = true; try { Instance.EmitGut(GutKind.Ingest, eatContext.Item); } catch (Exception e) { Instance.Error(e); } } private void ConfigurePower() { powerEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Powerups", "Enabled", true, "Brief signatures, with absolute combat priority. No continuous sparkle."); powerReminders = ((BaseUnityPlugin)this).Config.Bind<bool>("Powerups", "RemindersEnabled", true, "Only regen and resistance: faint reminders at least 10 seconds apart globally, only during quiet moments."); powerIntensity = ((BaseUnityPlugin)this).Config.Bind<float>("Powerups", "ActivationIntensity", 18f, Range("Activation percent ceiling, clamped to 0..25; general ceiling applies.", 0f, 25f, 1f)); reminderIntensity = ((BaseUnityPlugin)this).Config.Bind<float>("Powerups", "ReminderIntensity", 4f, Range("Reminder percent ceiling, clamped to 0..6; general ceiling applies.", 0f, 6f, 1f)); for (int i = 0; i < 20; i++) { powerFlags[i] = AccessTools.Field(typeof(FVRPlayerBody), "m_is" + BuffNames[i]); powerTimers[i] = AccessTools.Field(typeof(FVRPlayerBody), ((i < 10) ? "m_buffTime_" : "m_debuffTime_") + TimerNames[i]); } } private PowerSnapshot ReadPower(FVRPlayerBody body) { PowerSnapshot powerSnapshot = new PowerSnapshot(); powerSnapshot.Health = body.Health; for (int i = 0; i < 20; i++) { if ((object)powerFlags[i] != null && (object)powerTimers[i] != null) { powerSnapshot.Time[i] = (float)powerTimers[i].GetValue(body); powerSnapshot.Active[i] = (bool)powerFlags[i].GetValue(body) && powerSnapshot.Time[i] > 0f; } } return powerSnapshot; } private void QueuePower(int type, bool inverted, int mode, float scale) { if (powerAvailable && powerEnabled.Value && connected && !deathActive && !GM.IsDead() && !(Time.timeScale <= 0f)) { powerCues.RemoveAll((PowerCue c) => c.Type == type); if (powerCues.Count >= 8) { powerCues.RemoveAt(0); } powerCues.Add(new PowerCue { Type = type, Inverted = inverted, Mode = mode, Scale = scale, Expires = Time.realtimeSinceStartup + 1.5f }); } } private void ActivatedPower(FVRPlayerBody body, int type, bool inverted, PowerSnapshot before) { if ((Object)(object)body != (Object)(object)GM.CurrentPlayerBody || !PowerModel.Supported(type)) { return; } PowerSnapshot powerSnapshot = ReadPower(body); int num = PowerModel.Pair(type); if (num >= 0) { int num2 = num + (inverted ? 10 : 0); if (powerSnapshot.Active[num2] && (!before.Active[num2] || powerSnapshot.Time[num2] > before.Time[num2] + 0.05f)) { QueuePower(type, inverted, 0, before.Active[num2] ? 0.55f : 1f); } } else if (type == 0) { if (Math.Abs(powerSnapshot.Health - before.Health) > 0.01f) { QueuePower(type, inverted, 0, 1f); } } else { QueuePower(type, inverted, 0, 1f); } trackedPowerBody = body; trackedPower = powerSnapshot; } private void PollPower(bool playable) { float realtimeSinceStartup = Time.realtimeSinceStartup; FVRPlayerBody currentPlayerBody = GM.CurrentPlayerBody; if ((Object)(object)currentPlayerBody == (Object)null) { trackedPowerBody = null; trackedPower = null; } else { if (realtimeSinceStartup < nextPowerPoll) { return; } nextPowerPoll = realtimeSinceStartup + 0.2f; PowerSnapshot powerSnapshot = ReadPower(currentPlayerBody); if ((Object)(object)trackedPowerBody == (Object)(object)currentPlayerBody && trackedPower != null && playable && !deathActive && !GM.IsDead()) { for (int i = 0; i < 20; i++) { if (PowerModel.ExpiredNaturally(trackedPower.Active[i], powerSnapshot.Active[i], trackedPower.Time[i], powerSnapshot.Time[i], powerSnapshot.Active[(i + 10) % 20])) { QueuePower(PowerModel.TypeForPair(i % 10), i >= 10, 2, 0.35f); } } } trackedPowerBody = currentPlayerBody; trackedPower = powerSnapshot; } } private void TickPower(float now) { if (!powerAvailable || !powerEnabled.Value || deathActive || GM.IsDead()) { powerCues.Clear(); return; } bool flag = false; for (int i = 0; i < pulses.Count; i++) { if (!pulses[i].IsPower) { flag = true; break; } } powerCues.RemoveAll(delegate(PowerCue c) { if (c.Mode == 3 && !healingEnabled.Value) { return true; } if (now > c.Expires) { return true; } int num4 = PowerModel.Pair(c.Type); if (num4 < 0 || trackedPower == null) { return false; } bool flag2 = trackedPower.Active[num4 + (c.Inverted ? 10 : 0)]; return (c.Mode != 2) ? (!flag2) : flag2; }); if (flag || now - lastNonPower < 0.35f) { pulses.RemoveAll((Pulse p) => p.IsPower); return; } for (int num = 0; num < pulses.Count; num++) { if (pulses[num].IsPower) { return; } } if (powerCues.Count == 0 && powerReminders.Value && now >= nextReminder && trackedPower != null) { if (trackedPower.Active[0]) { QueuePower(10, inverted: false, 1, 1f); } else if (trackedPower.Active[1]) { QueuePower(3, inverted: false, 1, 1f); } nextReminder = now + 10f; } if (powerCues.Count == 0) { return; } PowerCue powerCue = powerCues[0]; powerCues.RemoveAt(0); if (!PowerModel.MayPlay(now, powerCue.Expires, lastNonPower, flag)) { return; } float percent = ((powerCue.Mode == 3) ? (RecoilModel.Clamp(healingIntensity.Value, 0f, 50f) * powerCue.Scale) : ((powerCue.Mode == 1) ? RecoilModel.Clamp(reminderIntensity.Value, 0f, 6f) : (RecoilModel.Clamp(powerIntensity.Value, 0f, 25f) * powerCue.Scale))); float num2 = DeathModel.Strength(percent, limit.Value); if (!(num2 <= 0f)) { nextReminder = now + 10f; for (int num3 = 0; num3 < 4; num3++) { AddPulse(num3, -400001, now, PowerModel.Duration(powerCue.Type, powerCue.Mode), num2, 0.5f, 0.5f, blast: false, num3 >= 2, minigun: false, 0, echo: false, melee: false, power: true); Pulse pulse = pulses[pulses.Count - 1]; pulse.PowerType = powerCue.Type; pulse.PowerMode = powerCue.Mode; pulse.PowerInverted = powerCue.Inverted; } } } private bool EdgePlayable() { if (enabledSetting.Value && connected && !deathActive && !GM.IsDead()) { return Time.timeScale > 0f; } return false; } private void ConfigureEdges() { cannonEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Cannon", "Enabled", true, "Confirmed cannon firing gives nearby directional pressure without held recoil."); cannonGain = ((BaseUnityPlugin)this).Config.Bind<float>("Cannon", "Gain", 1f, Range("Cannon pressure multiplier, 0..2, within launcher/general ceilings.", 0f, 2f, 0.05f)); toolsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("ContinuousWeapons", "Enabled", true, "Held flamethrower and chainsaw state feedback; impacts take priority."); toolGain = ((BaseUnityPlugin)this).Config.Bind<float>("ContinuousWeapons", "SleeveGain", 1f, Range("Continuous sleeve gain, 0..2.", 0f, 2f, 0.05f)); toolLimit = ((BaseUnityPlugin)this).Config.Bind<float>("ContinuousWeapons", "MaximumIntensity", 45f, Range("Continuous sleeve percent ceiling; general ceiling applies.", 0f, 100f, 1f)); } private void EmitCannon(Cannon cannon) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)cannon == (Object)null || (Object)(object)cannon.Muzzle == (Object)null || !cannonEnabled.Value || !EdgePlayable()) { return; } FVRPlayerBody currentPlayerBody = GM.CurrentPlayerBody; if ((Object)(object)currentPlayerBody == (Object)null || (Object)(object)currentPlayerBody.TorsoTransform == (Object)null) { return; } Transform torsoTransform = currentPlayerBody.TorsoTransform; Vector3 val = torsoTransform.position - torsoTransform.up * 0.22f; Vector3 position = cannon.Muzzle.position; float distance = Vector3.Distance(position, val); float num = RecoilModel.Clamp(EdgeModel.Nearby(distance, 0.72f, 18f) * RecoilModel.Clamp(cannonGain.Value, 0f, 2f), 0f, RecoilModel.Clamp(launchCeiling.Value / 100f, 0f, 1f)); if (num < 0.005f) { return; } Vector3 val2 = torsoTransform.InverseTransformDirection(position - val); Vector3 normalized = ((Vector3)(ref val2)).normalized; float num2 = RecoilModel.Clamp(0.5f - normalized.y * 0.4f, 0f, 1f); float realtimeSinceStartup = Time.realtimeSinceStartup; for (int i = 0; i < 4; i++) { float strength = ((i < 2) ? (num * 0.4f * ExplosionModel.Facing((i == 1) ? normalized.x : (0f - normalized.x))) : num); AddPulse(i, ((Object)cannon).GetInstanceID(), realtimeSinceStartup, 0.3f, strength, 0.5f, num2, blast: false, i >= 2); Pulse pulse = pulses[pulses.Count - 1]; pulse.IsExplosion = true; pulse.LaunchPressure = true; if (i >= 2) { pulse.Pattern = ExplosionModel.Panel(i == 3, normalized.x, normalized.z, num2); } } } private void SampleTool(FVRPhysicalObject obj, int kind, bool active, float throttle, bool contact, FVRAlternateGrip support) { if ((Object)(object)obj == (Object)null) { return; } int source = ((Object)obj).GetInstanceID(); ToolState value; bool flag = tools.TryGetValue(source, out value); if (!EdgePlayable() || !toolsEnabled.Value || (Object)(object)((FVRInteractiveObject)obj).m_hand == (Object)null || !active) { tools.Remove(source); pulses.RemoveAll((Pulse p) => p.IsTool && p.Source == source); if (flag && !active && (Object)(object)((FVRInteractiveObject)obj).m_hand != (Object)null && EdgePlayable() && toolsEnabled.Value && ((kind != 3 && kind != 4) || specialEnabled.Value)) { AddPulse(((FVRInteractiveObject)obj).m_hand.IsThisTheRightHand ? 1 : 0, source, Time.realtimeSinceStartup, 0.08f, 0.07f * RecoilModel.Clamp(toolGain.Value, 0f, 2f), 0.5f, 0.5f); } return; } if (!flag) { if (tools.Count >= 64) { return; } ToolState toolState = new ToolState(); toolState.Object = obj; toolState.Kind = kind; toolState.Started = Time.realtimeSinceStartup; value = toolState; tools.Add(source, value); } if (kind == 2) { contact = throttle > 0.1f && Time.realtimeSinceStartup - value.ContactAt < 0.12f; } value.Throttle = throttle; value.Contact = contact; value.Support = support; value.Seen = Time.realtimeSinceStartup; } private void SawBladeContact(Chainsaw saw, Collision col, bool running, float throttle) { //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) if (!running || throttle <= 0.1f || col == null || (Object)(object)col.collider == (Object)null || saw.BladeCols == null || !EdgePlayable() || ((Component)col.collider).transform.IsChildOf(((Component)saw).transform) || (Object)(object)((Component)col.collider).GetComponentInParent<FVRPlayerHitbox>() != (Object)null || (Object)(object)((Component)col.collider).GetComponentInParent<FVRViveHand>() != (Object)null || !tools.TryGetValue(((Object)saw).GetInstanceID(), out var value)) { return; } ContactPoint[] contacts = col.contacts; for (int i = 0; i < contacts.Length; i++) { ContactPoint val = contacts[i]; Collider[] bladeCols = saw.BladeCols; foreach (Collider val2 in bladeCols) { if ((Object)(object)val2 == (Object)(object)((ContactPoint)(ref val)).thisCollider) { value.ContactAt = Time.realtimeSinceStartup; return; } } } } private void TickTools(float now) { staleTools.Clear(); foreach (KeyValuePair<int, ToolState> tool in tools) { ToolState value = tool.Value; if (!toolsEnabled.Value || (Object)(object)value.Object == (Object)null || (Object)(object)((FVRInteractiveObject)value.Object).m_hand == (Object)null || now - value.Seen > 0.15f) { staleTools.Add(tool.Key); continue; } int source = tool.Key; pulses.RemoveAll((Pulse p) => p.IsTool && p.Source == source); float num = RecoilModel.Clamp(EdgeModel.ToolStrength(value.Kind, active: true, value.Throttle, value.Contact, now - value.Started) * RecoilModel.Clamp(toolGain.Value, 0f, 2f), 0f, RecoilModel.Clamp(toolLimit.Value / 100f, 0f, 1f)); int channel = (((FVRInteractiveObject)value.Object).m_hand.IsThisTheRightHand ? 1 : 0); AddToolPulse(channel, source, now, num, value); if ((Object)(object)value.Support != (Object)null && (Object)(object)((FVRInteractiveObject)value.Support).m_hand != (Object)null && (Object)(object)((FVRInteractiveObject)value.Support).m_hand != (Object)(object)((FVRInteractiveObject)value.Object).m_hand) { AddToolPulse(((FVRInteractiveObject)value.Support).m_hand.IsThisTheRightHand ? 1 : 0, source, now, num * 0.65f, value); } } foreach (int source2 in staleTools) { tools.Remove(source2); List<Pulse> list = pulses; Predicate<Pulse> match = (Pulse p) => p.IsTool && p.Source == source2; list.RemoveAll(match); } } private void AddToolPulse(int channel, int source, float now, float strength, ToolState state) { if (!(strength <= 0f)) { AddPulse(channel, source, now, 0.09f, strength, 0.5f, 0.5f, blast: false, grid: false, minigun: false, 0, echo: false, melee: false, power: false, tool: true); Pulse pulse = pulses[pulses.Count - 1]; pulse.ToolKind = state.Kind; pulse.ToolContact = state.Contact; } } private bool ToolCompetes(int channel, float now) { foreach (Pulse pulse in pulses) { if (!pulse.IsTool && !pulse.IsPower && now >= pulse.Start && now < pulse.Start + pulse.Duration && (pulse.Channel == channel || pulse.IsDamage || pulse.IsExplosion)) { return true; } } return false; } private void ConfigureSpecial() { specialEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("SpecialWeapons", "Enabled", true, "GBeamer actions, RailTater discharge and grapple attachment/retract/player pull."); specialGain = ((BaseUnityPlugin)this).Config.Bind<float>("SpecialWeapons", "Gain", 1f, Range("Special action multiplier, 0..2.", 0f, 2f, 0.05f)); specialLimit = ((BaseUnityPlugin)this).Config.Bind<float>("SpecialWeapons", "MaximumIntensity", 60f, Range("Special action percent ceiling; general ceiling applies.", 0f, 100f, 1f)); } private void InstallSpecialHooks() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown try { specialPatches = new Harmony("local.h3vr.simhaptics.special"); Type[] array = new Type[8] { typeof(SpecialBeamerStatePatch), typeof(SpecialHeldShuntPatch), typeof(SpecialWideShuntPatch), typeof(SpecialGrappleFirePatch), typeof(SpecialGrappleStatePa