using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using Lib.Networking.Connections; using Lib.Networking.Connections.Messages; namespace Lib.Networking.Connections.Helpers { /// /// Waits for a response from a connection. /// internal class ConnectionResponseWaiter { private readonly IConnection connection; public ConnectionResponseWaiter(IConnection connection) { this.connection = connection; ExpectedMessageNames = Array.Empty(); this.connection.ConnectionLost += OnConnectionLost; } private void OnConnectionLost(Events.ConnectionLostEventArgs e) { _connectionLost = true; } private bool _waitingForResponse; private bool _gotResponse; private ConnectionMessage? _receivedMessage; private bool _connectionLost; /// /// What responses are expected by the waiter. If the received message is not contained in this array, the response should be ignored. If this array is empty, all responses should be accepted. /// private MessageName[] ExpectedMessageNames; public ConnectionMessage? WaitForResponse(int timeout = 5, params MessageName[] expectedMessages) { ExpectedMessageNames = expectedMessages; var response = WaitForResponse(timeout); ExpectedMessageNames = Array.Empty(); return response; } /// /// Waits for a response, regardless of what the response is. /// /// public ConnectionMessage? WaitForResponse(int timeout = 5) { if (_waitingForResponse) throw new InvalidOperationException("One ConnectionResponseWaiter cannot wait twice at the same time. Please create a new ConnectionResponseWaiter for the same connection."); _waitingForResponse = true; _gotResponse = false; _receivedMessage = null; connection.MessageReceived += OnMessageReceived; DateTime startTime = DateTime.Now; while (!_gotResponse && (DateTime.Now - startTime).TotalSeconds < timeout) Thread.Sleep(10); //If there is a message being downloaded at the moment, wait for it //It might be the message that is being waited for, but it couldn't be downloaded in the timeout period. while (!_gotResponse && !_connectionLost && connection.ClientManager.Downloading == true) Thread.Sleep(10); connection.MessageReceived -= OnMessageReceived; _waitingForResponse = false; return _receivedMessage; } public async Task WaitForResponseAsync(int timeout = 5, params MessageName[] expectedMessages) { ExpectedMessageNames = expectedMessages; var response = await WaitForResponseAsync(timeout); ExpectedMessageNames = Array.Empty(); return response; } public async Task WaitForResponseAsync(int timeout = 5) { if (_waitingForResponse) throw new InvalidOperationException("One ConnectionResponseWaiter cannot wait twice at the same time. Please create a new ConnectionResponseWaiter for the same connection."); _waitingForResponse = true; _gotResponse = false; _receivedMessage = null; connection.MessageReceived += OnMessageReceived; DateTime startTime = DateTime.Now; while (!_gotResponse && !_connectionLost && (DateTime.Now - startTime).TotalSeconds < timeout) await Task.Delay(10); //If there is a message being downloaded at the moment, wait for it //It might be the message that is being waited for, but it couldn't be downloaded in the timeout period. while (!_gotResponse && connection.ClientManager.Downloading == true) await Task.Delay(10); connection.MessageReceived -= OnMessageReceived; _waitingForResponse = false; return _receivedMessage; } private void OnMessageReceived(Events.MessageReceivedEventArgs args) { if (args.Message.MessageTypeEnum != MessageType.Response) return; if (ExpectedMessageNames.Length > 0 && !ExpectedMessageNames.Contains(args.Message.MessageNameEnum)) return; _gotResponse = true; _receivedMessage = args.Message; } } }