private async Task DispatchData(byte[] data) { NetworkMessage message = NetworkMessage.CreateFromByteArray(data); bool received = false; UpdateSessionInfo(message); if (_highPriorityDispatchers.TryGetValue(message.MessageType, out var highPriorityDispatch)) { foreach (MessageReceiver dispatcher in highPriorityDispatch.GetInvocationList()) { received = true; await dispatcher(message).ConfigureAwait(false); } } if (_jobs.IsRunningJob(message.Header.JobId)) { await _jobs.SetJobResult(message, message.Header.JobId).ConfigureAwait(false); } if (_eventDispatchers.TryGetValue(message.MessageType, out var dispatch)) { foreach (MessageReceiver dispatcher in dispatch.GetInvocationList()) { received = true; await dispatcher(message).TimeoutWrap(TaskTimeout, NetLog).ConfigureAwait(false); } } if (!received) { await NetLog.DebugAsync($"No receiver found for message type {message.MessageType} ({(int)message.MessageType})").ConfigureAwait(false); } }
private async Task RunHeartbeatAsync(int interval, CancellationToken token) { #region Valve sucks at naming enum members /* * Let me tell you a story about 10/14/2017 * So I was working on this lib ironing out some kinks, rewriting the connection code * when I finally completed it and decided to test it. So I pop open a console just to * find obvious bugs like the whole thing exploding for no reason. Suddenly at about 5:08 PM * it starts working properly. Stuff starts printing in the console and it's working properly. * It works correctly until about 20 seconds later when the console window vanishes and up pops an error * "IOException: Unable to transfer data" bla bla bla BASICALLY the connection was aborted. * This brought up another bug in my TCP client where I wouldn't actually tell anyone the connection disconnected, * but who cares about that. This brought in a more important problem: Steam drops my connection even though my heart is beating * So I try a WebSocket. Same thing. Check the headers (because there's nothing the in body), everything lines up. * I reference the SteamKit, everything related to serialization is in order. Everything is right except one thing. * One small piece that nobody would notice. * The correct message type for a heartbeat is "ClientHeartBeat", not "Heartbeat"... * I found this out after 3 hours. Please kill me. * * * TLDR: Valve sucks at naming enum members. */ #endregion // cache the data so we don't serialize 500 times per session. Our session ID will never change and neither will our Steam ID var beat = NetworkMessage.CreateProtobufMessage(MessageType.ClientHeartBeat, new CMsgClientHeartBeat()).WithClientInfo(SteamId, SessionId).Serialize(); try { await NetLog.DebugAsync($"Heartbeat started on a {interval} ms interval").ConfigureAwait(false); while (!token.IsCancellationRequested) { await Task.Delay(interval, token).ConfigureAwait(false); try { await SendAsync(beat).ConfigureAwait(false); } catch (Exception ex) { await NetLog.ErrorAsync($"The heartbeat task encountered an unknown exception while sending the heartbeat message", ex).ConfigureAwait(false); } } } catch (OperationCanceledException) { await NetLog.DebugAsync("Heartbeat stopped").ConfigureAwait(false); } catch (Exception ex) { await NetLog.ErrorAsync($"The heartbeat task encountered an unknown exception", ex).ConfigureAwait(false); } }
private async Task ProcessEncryptResult(NetworkMessage message) { ChannelEncryptResult encryptResult = message.Deserialize <ChannelEncryptResult>(); if (encryptResult.Result == Result.OK) { await NetLog.DebugAsync("Channel encrypted").ConfigureAwait(false); await _connection.CompleteAsync().ConfigureAwait(false); } }
/// <summary> /// Sends an array of bytes to Steam /// </summary> /// <param name="message"></param> /// <returns></returns> protected internal async Task SendAsync(byte[] message) { if (message == null) { throw new ArgumentNullException(nameof(message)); } await NetLog.DebugAsync($"Sending {message.Length} byte message.").ConfigureAwait(false); await Socket.SendAsync(Encryption.Encrypt(message)).ConfigureAwait(false); }
private async Task OnDisconnectingInternalAsync(Exception ex) { await NetLog.DebugAsync("Cancelling all jobs").ConfigureAwait(false); await _jobs.CancelAllJobs().ConfigureAwait(false); await OnDisconnectingAsync(ex).ConfigureAwait(false); await NetLog.DebugAsync("Disconnecting client").ConfigureAwait(false); await DisconnectAsync().ConfigureAwait(false); }
protected sealed override async Task OnConnectedAsync() { await NetLog.DebugAsync("Performing possible login actions").ConfigureAwait(false); // todo: resume connections and things if (_logonFunc != null) { await _logonFunc().ConfigureAwait(false); } else { await Ready.InvokeAsync(this, EventArgs.Empty).ConfigureAwait(false); } }
protected sealed override async Task OnDisconnectingAsync(Exception ex) { if (!_gracefulLogoff) { CurrentServer = null; } await NetLog.DebugAsync("Waiting for heartbeat").ConfigureAwait(false); _heartbeatCancel?.Cancel(); if (_heartBeatTask != null) { await _heartBeatTask.ConfigureAwait(false); } _heartBeatTask = null; }
/// <summary> /// Connects this client to a connection manager /// </summary> /// <returns></returns> private async Task OnConnectingInternalAsync() { await NetLog.DebugAsync("Connecting client").ConfigureAwait(false); await ConnectAsync().ConfigureAwait(false); if (!(Socket is IWebSocketClient)) { await NetLog.DebugAsync("Waiting for encryption").ConfigureAwait(false); await _connection.WaitAsync().ConfigureAwait(false); } await OnConnectedAsync().ConfigureAwait(false); await _connection.CompleteAsync().ConfigureAwait(false); }