Пример #1
0
        /// <summary>
        /// Initializes a new instance of the <see cref="DiscordGatewayClient"/> class.
        /// </summary>
        /// <param name="gatewayAPI">The gateway API.</param>
        /// <param name="transportService">The payload transport service.</param>
        /// <param name="gatewayOptions">The gateway options.</param>
        /// <param name="tokenStore">The token store.</param>
        /// <param name="random">An entropy source.</param>
        /// <param name="log">The logging instance.</param>
        /// <param name="services">The available services.</param>
        /// <param name="responderTypeRepository">The responder type repository.</param>
        public DiscordGatewayClient
        (
            IDiscordRestGatewayAPI gatewayAPI,
            IPayloadTransportService transportService,
            IOptions <DiscordGatewayClientOptions> gatewayOptions,
            ITokenStore tokenStore,
            Random random,
            ILogger <DiscordGatewayClient> log,
            IServiceProvider services,
            IResponderTypeRepository responderTypeRepository
        )
        {
            _gatewayAPI       = gatewayAPI;
            _transportService = transportService;
            _gatewayOptions   = gatewayOptions.Value;
            _tokenStore       = tokenStore;
            _random           = random;
            _log      = log;
            _services = services;
            _responderTypeRepository = responderTypeRepository;

            _runningResponderDispatches = new ConcurrentQueue <Task <IReadOnlyList <Result> > >();

            _payloadsToSend   = new ConcurrentQueue <IPayload>();
            _receivedPayloads = new ConcurrentQueue <IPayload>();

            _connectionStatus = GatewayConnectionStatus.Offline;

            _disconnectRequestedSource = new CancellationTokenSource();
            _sendTask    = Task.FromResult(Result.FromSuccess());
            _receiveTask = Task.FromResult(Result.FromSuccess());
        }
Пример #2
0
        /// <summary>
        /// Starts and connects the gateway client.
        /// </summary>
        /// <remarks>
        /// This task will not complete until cancelled (or faulted), maintaining the connection for the duration of it.
        ///
        /// If the gateway client encounters a fatal problem during the execution of this task, it will return with a
        /// failed result. If a shutdown is requested, it will gracefully terminate the connection and return a
        /// successful result.
        /// </remarks>
        /// <param name="stopRequested">A token by which the caller can request this method to stop.</param>
        /// <returns>A gateway connection result which may or may not have succeeded.</returns>
        public async Task <Result> RunAsync(CancellationToken stopRequested)
        {
            try
            {
                if (_connectionStatus != GatewayConnectionStatus.Offline)
                {
                    return(new GenericError("Already connected."));
                }

                // Until cancellation has been requested or we hit a fatal error, reconnections should be attempted.
                _disconnectRequestedSource.Dispose();
                _disconnectRequestedSource = new CancellationTokenSource();

                while (!stopRequested.IsCancellationRequested)
                {
                    var iterationResult = await RunConnectionIterationAsync(stopRequested);

                    if (iterationResult.IsSuccess)
                    {
                        continue;
                    }

                    // Something has gone wrong. Close the socket, and handle it
                    // Terminate the send and receive tasks
                    _disconnectRequestedSource.Cancel();

                    // The results of the send and receive tasks are discarded here, because the iteration result will
                    // contain whichever of them failed if any of them did
                    _ = await _sendTask;
                    _ = await _receiveTask;

                    if (_transportService.IsConnected)
                    {
                        var disconnectResult = await _transportService.DisconnectAsync(stopRequested.IsCancellationRequested, stopRequested);

                        if (!disconnectResult.IsSuccess)
                        {
                            // Couldn't disconnect cleanly :(
                            return(disconnectResult);
                        }
                    }

                    // Finish up the responders
                    foreach (var runningResponder in _runningResponderDispatches)
                    {
                        await FinalizeResponderDispatchAsync(runningResponder);
                    }

                    if (stopRequested.IsCancellationRequested)
                    {
                        // The user requested a termination, and we don't intend to reconnect.
                        return(iterationResult);
                    }

                    if (ShouldReconnect(iterationResult, out var shouldTerminate, out var withNewSession))
                    {
                        if (withNewSession)
                        {
                            _sessionID        = null;
                            _connectionStatus = GatewayConnectionStatus.Disconnected;
                        }
                        else
                        {
                            _connectionStatus = GatewayConnectionStatus.Disconnected;
                        }
                    }
                    else if (shouldTerminate)
                    {
                        return(iterationResult);
                    }

                    // This token's been cancelled, we'll need a new one to reconnect.
                    _disconnectRequestedSource.Dispose();
                    _disconnectRequestedSource = new CancellationTokenSource();
                }