using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using EventsIO; using EventsIO.Interfaces; using ModHelper; using ModHelper.Interfaces; using WebSocketIO.Data; using WebSocketSharp; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("WebSocketIO")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("BFT")] [assembly: AssemblyProduct("WebSocketIO")] [assembly: AssemblyCopyright("Copyright © 2022")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("27b98c5b-ac6a-4518-ae12-3c9957b291e4")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace WebSocketIO { public class ChaosWebSocketHandler { public const string TypeEventMessage = "event"; public const string TypeConfigMessage = "config"; public bool isEnableDebugLog = true; public bool isEnableDebugLogOnClose; public bool isEnableDebugLogOnOpen; public bool isEnableDebugLogOnCloseValidConnect = true; protected bool isNeedLogOnCloseValidConnect; protected ILogger logger; public Action OnCustomMessage { get; set; } public Action OnEventMessage { get; set; } public Action OnConfigMessage { get; set; } public Action OnWebSocketOpen { get; set; } public Action OnWebSocketClose { get; set; } public Action OnWebSocketError { get; set; } public ChaosWebSocketHandler(ILogger logger = null) { this.logger = logger; } protected void DebugWebSocketLog(string s) { if (isEnableDebugLog && logger != null) { logger.LogInfo((object)("[DEBUG] TokControl_ WebSocketHandler: " + s)); } } protected void DebugWebSocketLogError(string s) { if (isEnableDebugLog && logger != null) { logger.LogError((object)("[DEBUG] TokControl_ WebSocketHandler: " + s)); } } public void OnOpen() { if (isEnableDebugLogOnOpen) { DebugWebSocketLog("OnOpen"); } try { if (isEnableDebugLogOnCloseValidConnect) { isNeedLogOnCloseValidConnect = true; } OnWebSocketOpen?.Invoke(); } catch (Exception arg) { if (isEnableDebugLogOnOpen) { DebugWebSocketLogError($"OnOpen {arg}"); } } } public void OnClose(CloseEventArgs e) { if (isEnableDebugLogOnClose) { DebugWebSocketLog("OnClose " + e.Reason); } try { OnWebSocketClose?.Invoke(e); } catch (Exception arg) { if (isEnableDebugLogOnClose) { DebugWebSocketLogError($"OnClose {arg}"); } else if (isEnableDebugLogOnCloseValidConnect && isNeedLogOnCloseValidConnect) { isNeedLogOnCloseValidConnect = false; DebugWebSocketLogError($"OnClose {arg}"); } } } public void OnError(ErrorEventArgs e) { DebugWebSocketLogError("Error " + e.Message); try { OnWebSocketError?.Invoke(e); } catch (Exception arg) { DebugWebSocketLogError($"OnError inner error {arg}"); } } public WebSocketMessage ParseWebSocketMessage(string data) { try { return JsonUtils.Generate((IDebugLogger)null).FromString(data) ?? new WebSocketMessage(); } catch (Exception arg) { DebugWebSocketLogError($"ParseWebSocketMessage {arg}"); return new WebSocketMessage(); } } public void OnMessage(MessageEventArgs e) { if (!e.IsText) { return; } try { WebSocketMessage webSocketMessage = ParseWebSocketMessage(e.Data); string type = webSocketMessage.type; string data = webSocketMessage.data; if (!(type == "config")) { if (type == "event") { DebugWebSocketLog("OnMessage - event"); OnEventMessage?.Invoke(data); } else { DebugWebSocketLog("OnMessage - custom msg"); OnCustomMessage?.Invoke(type, data); } } else { DebugWebSocketLog("OnMessage - config"); OnConfigMessage?.Invoke(data); } } catch (Exception arg) { DebugWebSocketLogError($"OnMessage {arg}"); } } } public class WebSocketEventsReader : EventsReaderOnFrame, IDisposable where T : class { protected object locker = new object(); protected List eventListFromSocket = new List(); protected bool isDisposed; protected bool isSocketMessageProcessing; public bool isAutoRequestSettingsOnOpen = true; public bool isAutoConnectOnRead = true; public const int DEFAULT_PORT = 13715; public float autoConnectOnReadPeriod = 8f; protected float autoConnectOnReadPeriodTicks; protected bool isNeedLogOnClose; protected bool logOnConnectionError; public virtual ChaosWebSocketHandler SocketHandler { get; set; } public virtual WebSocket Socket { get; set; } public virtual T Settings { get; protected set; } public Action OnMessage { get; set; } public Action OnOpen { get; set; } public Action OnCloseOnlyAfterConnect { get; set; } public Action OnErrorOnlyAfterConnect { get; set; } public Action OnClose { get; set; } public Action OnError { get; set; } public WebSocketEventsReader(Action processEvent, IEventsDataParser parser, Action log = null, Action errorLog = null, float checkPeriod = 2f, float delay = 1f, float initDelay = 0.5f, bool logOnConnectionError = false) : base("", processEvent, parser, log, errorLog, checkPeriod, delay, initDelay) { this.logOnConnectionError = logOnConnectionError; } public virtual int GetPortFromSettingsOrDefault(string settingsDir) { try { string path = Path.Combine(settingsDir, "TokControlWsConfig.txt"); if (File.Exists(path)) { return int.Parse(File.ReadAllLines(path)[0]); } } catch (Exception arg) { ((EventsReader)this).ReaderLogError($"GetPortFromSettingsOrDefault error: {arg}. (will be used {18715})"); } return 18715; } public virtual T ParseSettings(string data) { try { T result = JsonUtils.Generate((IDebugLogger)null).FromString(data); ((EventsReader)this).ReaderLog("settings parsing completed: " + data); return result; } catch (Exception arg) { ((EventsReader)this).ReaderLogError($"error: {arg}.\nCan't parse settings. Using standard."); } T result2 = null; try { result2 = Activator.CreateInstance(); return result2; } catch (Exception arg2) { ((EventsReader)this).ReaderLogError($"error: {arg2}.\nCan't create default data class."); return result2; } } public virtual void OpenSocket(bool isLogOnError = true) { WebSocket socket = Socket; try { if (socket != null && !socket.IsAlive) { ((EventsReader)this).ReaderLog("try to open socket connection"); socket.ConnectAsync(); } } catch (Exception arg) { if (isLogOnError) { ((EventsReader)this).ReaderLogError($"OpenSocket error: {arg}"); } try { socket.Close(); } catch (Exception) { } } } public virtual void CloseSocket() { try { WebSocket socket = Socket; if (socket != null) { socket.Close(); } } catch (Exception arg) { ((EventsReader)this).ReaderLogError($"CloseSocket error: {arg}"); } } public virtual void RequestSettings() { try { WebSocket socket = Socket; if (socket != null) { socket.Send(new WebSocketMessage("config", "").ToString()); } } catch (Exception arg) { ((EventsReader)this).ReaderLogError($"RequestSettings error: {arg}"); } } public virtual void InitDefaultSocket(string settingsDir, string room, int port, string ip = "127.0.0.1") { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown WebSocket val = new WebSocket($"ws://{ip}:{port}/{room}", Array.Empty()); ChaosWebSocketHandler socketHandler = new ChaosWebSocketHandler { OnEventMessage = delegate(string data) { isSocketMessageProcessing = true; lock (locker) { eventListFromSocket.Add(data); } isSocketMessageProcessing = false; }, OnConfigMessage = delegate(string data) { Settings = ParseSettings(data); }, OnWebSocketOpen = delegate { isNeedLogOnClose = true; ((EventsReader)this).ReaderLog("TokControl_ connected"); if (isAutoRequestSettingsOnOpen) { RequestSettings(); } try { OnOpen?.Invoke(); } catch (Exception arg) { ((EventsReader)this).ReaderLogError($"TokControl_ custom OnOpen error: {arg}"); } }, OnWebSocketClose = delegate(CloseEventArgs e) { if (isNeedLogOnClose) { isNeedLogOnClose = false; ((EventsReader)this).ReaderLog($"TokControl_ disconnected {e.Reason} {e.Code}"); try { OnCloseOnlyAfterConnect?.Invoke(e); } catch (Exception arg) { ((EventsReader)this).ReaderLogError($"TokControl_ custom OnClose after success connection error: {arg}"); } } try { OnClose?.Invoke(e); } catch (Exception arg2) { ((EventsReader)this).ReaderLogError($"TokControl_ custom OnClose error: {arg2}"); } }, OnWebSocketError = delegate(ErrorEventArgs e) { if (isNeedLogOnClose) { ((EventsReader)this).ReaderLog($"TokControl_ ws error {e.Message} {e.Exception}"); try { OnErrorOnlyAfterConnect?.Invoke(e); } catch (Exception arg) { ((EventsReader)this).ReaderLogError($"TokControl_ custom OnError after success connection error: {arg}"); } } try { OnError?.Invoke(e); } catch (Exception arg2) { ((EventsReader)this).ReaderLogError($"TokControl_ custom OnError error: {arg2}"); } } }; val.OnMessage += delegate(object sender, MessageEventArgs e) { socketHandler.OnMessage(e); try { OnMessage?.Invoke(e); } catch (Exception arg) { ((EventsReader)this).ReaderLogError($"TokControl_ custom OnMessage error: {arg}"); } }; val.OnOpen += delegate { socketHandler.OnOpen(); }; val.OnClose += delegate(object sender, CloseEventArgs e) { socketHandler.OnClose(e); }; val.OnError += delegate(object sender, ErrorEventArgs e) { socketHandler.OnError(e); }; if (!File.Exists(Path.Combine(settingsDir ?? "", ".debug_websocket"))) { val.Log.Level = (LogLevel)10; } SocketHandler = socketHandler; Socket = val; SetSocketForConnectLimit(val, int.MaxValue); } public void SetSocketForConnectLimit(WebSocket ws, int max) { FieldInfo field = ((object)ws).GetType().GetField("_maxRetryCountForConnect", BindingFlags.Static | BindingFlags.NonPublic); if (field == null) { ((EventsReader)this).ReaderLogError("FixSocketLimit error, maxRetryField is null"); } else { field.SetValue(ws, max); } } public override string[] ReadAll(string path, bool isClearFile = true) { string[] result = null; if (!isSocketMessageProcessing) { lock (locker) { if (eventListFromSocket.Count > 0) { result = eventListFromSocket.ToArray(); eventListFromSocket.Clear(); } } } return result; } public override void ReadAndProcessAllWithDelayOnFrame(float dt) { if (isAutoConnectOnRead) { autoConnectOnReadPeriodTicks += dt; if (autoConnectOnReadPeriodTicks > autoConnectOnReadPeriod) { autoConnectOnReadPeriodTicks = 0f; OpenSocket(logOnConnectionError); } } ((EventsReaderOnFrame)this).ReadAndProcessAllWithDelayOnFrame(dt); } protected virtual void Dispose(bool disposing) { if (!isDisposed) { isDisposed = true; if (disposing) { ((IDisposable)Socket)?.Dispose(); } } } ~WebSocketEventsReader() { try { Dispose(disposing: false); } finally { ((object)this).Finalize(); } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } } namespace WebSocketIO.Interfaces { public interface IChaosWebSocket { void Start(); void Stop(); } } namespace WebSocketIO.Data { public class WebSocketMessage { public string type = ""; public string data = ""; public WebSocketMessage() { } public WebSocketMessage(string type, string data) { this.type = type; this.data = data; } public override string ToString() { string text = data.Replace("\"", "\\\""); return "{\"type\":\"" + type + "\", \"data\":\"" + text + "\"}"; } } }