Gateway constructor

Gateway(
  1. NyxxGateway client,
  2. GatewayBot gatewayBot,
  3. List<Shard> shards,
  4. int totalShards,
  5. List<int> shardIds
)

Create a new Gateway.

Implementation

Gateway(this.client, this.gatewayBot, this.shards, this.totalShards, this.shardIds) : super.create() {
  final logger = Logger('${client.options.loggerName}.Gateway');

  // https://discord.com/developers/docs/topics/gateway#rate-limiting
  const identifyDelay = Duration(seconds: 5);
  final maxConcurrency = gatewayBot.sessionStartLimit.maxConcurrency;
  var remainingIdentifyRequests = gatewayBot.sessionStartLimit.remaining;

  // A mapping of rateLimitId (shard.id % maxConcurrency) to Futures that complete when the identify lock for that rate_limit_key is no longer used.
  final identifyLocks = <int, Future<void>>{};

  // Handle messages from the shards and start them according to their rate limit key.
  for (final shard in shards) {
    final rateLimitKey = shard.id % maxConcurrency;

    // Delay the shard starting until it is (approximately) also ready to identify.
    _startTimers.add(Timer(identifyDelay * (shard.id ~/ maxConcurrency), () {
      logger.fine('Starting shard ${shard.id}');
      shard.add(StartShard());
    }));

    shard.listen(
      (event) {
        _messagesController.add(event);

        if (event is RequestingIdentify) {
          final currentLock = identifyLocks[rateLimitKey] ?? Future.value();
          identifyLocks[rateLimitKey] = currentLock.then((_) async {
            if (_closing) return;

            if (remainingIdentifyRequests < client.options.minimumSessionStarts * 5) {
              logger.warning('$remainingIdentifyRequests session starts remaining');
            }

            if (remainingIdentifyRequests < client.options.minimumSessionStarts) {
              await client.close();
              throw OutOfRemainingSessionsError(gatewayBot);
            }

            remainingIdentifyRequests--;
            shard.add(Identify());
            return await Future.delayed(identifyDelay);
          });
        }
      },
      onError: _messagesController.addError,
      onDone: () async {
        if (_closing) {
          return;
        }

        await client.close();

        throw ShardDisconnectedError(shard);
      },
      cancelOnError: false,
    );
  }
}