Gateway: Add connection timeout, add Config to gateway. (#51)

This change fixes tasks hanging due to rare cases of messages being lost between full Discord reconnections by placing a configurable timeout on the `ConnectionInfo` responses. This is a companion fix to [serenity#1255](https://github.com/serenity-rs/serenity/pull/1255). To make this doable, `Config`s are now used by all versions of `Songbird`/`Call`, and relevant functions are  added to simplify setup with configuration. These are now non-exhaustive, correcting an earlier oversight. For future extensibility, this PR moves the return type of `join`/`join_gateway` into a custom future (no longer leaking flume's `RecvFut` type).

Additionally, this fixes the Makefile's feature sets for driver/gateway-only compilation.

This is a breaking change in:
* the return types of `join`/`join_gateway`
* moving `crate::driver::Config` -> `crate::Config`,
* `Config` and `JoinError` becoming `#[non_breaking]`.

This was tested via `cargo make ready`, and by testing `examples/serenity/voice_receive` with various timeout settings.
This commit is contained in:
Kyle Simpson
2021-03-29 19:51:13 +01:00
parent f449d4f679
commit 1fc3dc2259
18 changed files with 426 additions and 119 deletions

116
src/config.rs Normal file
View File

@@ -0,0 +1,116 @@
#[cfg(feature = "driver-core")]
use super::driver::{CryptoMode, DecodeMode};
#[cfg(feature = "gateway-core")]
use std::time::Duration;
/// Configuration for drivers and calls.
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct Config {
#[cfg(feature = "driver-core")]
/// Selected tagging mode for voice packet encryption.
///
/// Defaults to [`CryptoMode::Normal`].
///
/// Changes to this field will not immediately apply if the
/// driver is actively connected, but will apply to subsequent
/// sessions.
///
/// [`CryptoMode::Normal`]: CryptoMode::Normal
pub crypto_mode: CryptoMode,
#[cfg(feature = "driver-core")]
/// Configures whether decoding and decryption occur for all received packets.
///
/// If voice receiving voice packets, generally you should choose [`DecodeMode::Decode`].
/// [`DecodeMode::Decrypt`] is intended for users running their own selective decoding,
/// who rely upon [user speaking events], or who need to inspect Opus packets.
/// If you're certain you will never need any RT(C)P events, then consider [`DecodeMode::Pass`].
///
/// Defaults to [`DecodeMode::Decrypt`]. This is due to per-packet decoding costs,
/// which most users will not want to pay, but allowing speaking events which are commonly used.
///
/// [`DecodeMode::Decode`]: DecodeMode::Decode
/// [`DecodeMode::Decrypt`]: DecodeMode::Decrypt
/// [`DecodeMode::Pass`]: DecodeMode::Pass
/// [user speaking events]: crate::events::CoreEvent::SpeakingUpdate
pub decode_mode: DecodeMode,
#[cfg(feature = "gateway-core")]
/// Configures the amount of time to wait for Discord to reply with connection information
/// if [`Call::join`]/[`join_gateway`] are used.
///
/// This is a useful fallback in the event that:
/// * the underlying Discord client restarts and loses a join request, or
/// * a channel join fails because the bot is already believed to be there.
///
/// Defaults to 10 seconds. If set to `None`, connections will never time out.
///
/// [`Call::join`]: crate::Call::join
/// [`join_gateway`]: crate::Call::join_gateway
pub gateway_timeout: Option<Duration>,
#[cfg(feature = "driver-core")]
/// Number of concurrently active tracks to allocate memory for.
///
/// This should be set at, or just above, the maximum number of tracks
/// you expect your bot will play at the same time. Exceeding the size of
/// the internal queue will trigger a larger memory allocation and copy,
/// possibly causing the mixer thread to miss a packet deadline.
///
/// Defaults to `1`.
///
/// Changes to this field in a running driver will only ever increase
/// the capacity of the track store.
pub preallocated_tracks: usize,
}
impl Default for Config {
fn default() -> Self {
Self {
#[cfg(feature = "driver-core")]
crypto_mode: CryptoMode::Normal,
#[cfg(feature = "driver-core")]
decode_mode: DecodeMode::Decrypt,
#[cfg(feature = "gateway-core")]
gateway_timeout: Some(Duration::from_secs(10)),
#[cfg(feature = "driver-core")]
preallocated_tracks: 1,
}
}
}
#[cfg(feature = "driver-core")]
impl Config {
/// Sets this `Config`'s chosen cryptographic tagging scheme.
pub fn crypto_mode(mut self, crypto_mode: CryptoMode) -> Self {
self.crypto_mode = crypto_mode;
self
}
/// Sets this `Config`'s received packet decryption/decoding behaviour.
pub fn decode_mode(mut self, decode_mode: DecodeMode) -> Self {
self.decode_mode = decode_mode;
self
}
/// Sets this `Config`'s number of tracks to preallocate.
pub fn preallocated_tracks(mut self, preallocated_tracks: usize) -> Self {
self.preallocated_tracks = preallocated_tracks;
self
}
/// This is used to prevent changes which would invalidate the current session.
pub(crate) fn make_safe(&mut self, previous: &Config, connected: bool) {
if connected {
self.crypto_mode = previous.crypto_mode;
}
}
}
#[cfg(feature = "gateway-core")]
impl Config {
/// Sets this `Config`'s timeout for joining a voice channel.
pub fn gateway_timeout(mut self, gateway_timeout: Option<Duration>) -> Self {
self.gateway_timeout = gateway_timeout;
self
}
}