diff options
| author | Zeyla Hellyer <[email protected]> | 2018-01-18 09:09:06 -0800 |
|---|---|---|
| committer | Zeyla Hellyer <[email protected]> | 2018-01-18 09:09:06 -0800 |
| commit | ba112ccb7d3b1d524b29a999f21624cfa356cffa (patch) | |
| tree | c9d86eb546b12f131532a36d3fad49c6a02501bf /src/client | |
| parent | Use an InterMessage to communicate over gateway (diff) | |
| download | serenity-ba112ccb7d3b1d524b29a999f21624cfa356cffa.tar.xz serenity-ba112ccb7d3b1d524b29a999f21624cfa356cffa.zip | |
Expose a client voice manager
Diffstat (limited to 'src/client')
| -rw-r--r-- | src/client/bridge/gateway/mod.rs | 2 | ||||
| -rw-r--r-- | src/client/bridge/gateway/shard_manager.rs | 13 | ||||
| -rw-r--r-- | src/client/bridge/gateway/shard_queuer.rs | 35 | ||||
| -rw-r--r-- | src/client/bridge/gateway/shard_runner.rs | 130 | ||||
| -rw-r--r-- | src/client/bridge/mod.rs | 3 | ||||
| -rw-r--r-- | src/client/bridge/voice/mod.rs | 117 | ||||
| -rw-r--r-- | src/client/mod.rs | 46 |
7 files changed, 268 insertions, 78 deletions
diff --git a/src/client/bridge/gateway/mod.rs b/src/client/bridge/gateway/mod.rs index 06f5ea4..3cc6dca 100644 --- a/src/client/bridge/gateway/mod.rs +++ b/src/client/bridge/gateway/mod.rs @@ -60,7 +60,7 @@ pub use self::shard_manager::{ShardManager, ShardManagerOptions}; pub use self::shard_manager_monitor::ShardManagerMonitor; pub use self::shard_messenger::ShardMessenger; pub use self::shard_queuer::ShardQueuer; -pub use self::shard_runner::ShardRunner; +pub use self::shard_runner::{ShardRunner, ShardRunnerOptions}; pub use self::shard_runner_message::ShardRunnerMessage; use std::fmt::{Display, Formatter, Result as FmtResult}; diff --git a/src/client/bridge/gateway/shard_manager.rs b/src/client/bridge/gateway/shard_manager.rs index ea3ad1e..b62ff00 100644 --- a/src/client/bridge/gateway/shard_manager.rs +++ b/src/client/bridge/gateway/shard_manager.rs @@ -20,6 +20,8 @@ use typemap::ShareMap; #[cfg(feature = "framework")] use framework::Framework; +#[cfg(feature = "voice")] +use client::bridge::voice::ClientVoiceManager; /// A manager for handling the status of shards by starting them, restarting /// them, and stopping them when required. @@ -40,6 +42,11 @@ use framework::Framework; /// /// # use std::error::Error; /// # +/// # #[cfg(feature = "voice")] +/// # use serenity::client::bridge::voice::ClientVoiceManager; +/// # #[cfg(feature = "voice")] +/// # use serenity::model::id::UserId; +/// # /// # #[cfg(feature = "framework")] /// # fn try_main() -> Result<(), Box<Error>> { /// # @@ -78,6 +85,8 @@ use framework::Framework; /// shard_total: 5, /// token: &token, /// threadpool, +/// # #[cfg(feature = "voice")] +/// # voice_manager: &Arc::new(Mutex::new(ClientVoiceManager::new(0, UserId(0)))), /// ws_url: &gateway_url, /// }); /// # Ok(()) @@ -135,6 +144,8 @@ impl ShardManager { rx: shard_queue_rx, threadpool: opt.threadpool, token: Arc::clone(opt.token), + #[cfg(feature = "voice")] + voice_manager: Arc::clone(opt.voice_manager), ws_url: Arc::clone(opt.ws_url), }; @@ -343,5 +354,7 @@ pub struct ShardManagerOptions<'a, H: EventHandler + Send + Sync + 'static> { pub shard_total: u64, pub threadpool: ThreadPool, pub token: &'a Arc<Mutex<String>>, + #[cfg(feature = "voice")] + pub voice_manager: &'a Arc<Mutex<ClientVoiceManager>>, pub ws_url: &'a Arc<Mutex<String>>, } diff --git a/src/client/bridge/gateway/shard_queuer.rs b/src/client/bridge/gateway/shard_queuer.rs index 461fdd1..afcd4c7 100644 --- a/src/client/bridge/gateway/shard_queuer.rs +++ b/src/client/bridge/gateway/shard_queuer.rs @@ -13,11 +13,14 @@ use super::{ ShardQueuerMessage, ShardRunner, ShardRunnerInfo, + ShardRunnerOptions, }; use threadpool::ThreadPool; use typemap::ShareMap; use ::gateway::ConnectionStage; +#[cfg(feature = "voice")] +use client::bridge::voice::ClientVoiceManager; #[cfg(feature = "framework")] use framework::Framework; @@ -70,6 +73,9 @@ pub struct ShardQueuer<H: EventHandler + Send + Sync + 'static> { pub threadpool: ThreadPool, /// A copy of the token to connect with. pub token: Arc<Mutex<String>>, + /// A copy of the client's voice manager. + #[cfg(feature = "voice")] + pub voice_manager: Arc<Mutex<ClientVoiceManager>>, /// A copy of the URI to use to connect to the gateway. pub ws_url: Arc<Mutex<String>>, } @@ -165,24 +171,17 @@ impl<H: EventHandler + Send + Sync + 'static> ShardQueuer<H> { shard_info, )?; - let mut runner = feature_framework! {{ - ShardRunner::new( - shard, - self.manager_tx.clone(), - Arc::clone(&self.framework), - Arc::clone(&self.data), - Arc::clone(&self.event_handler), - self.threadpool.clone(), - ) - } else { - ShardRunner::new( - shard, - self.manager_tx.clone(), - self.data.clone(), - self.event_handler.clone(), - self.threadpool.clone(), - ) - }}; + let mut runner = ShardRunner::new(ShardRunnerOptions { + data: Arc::clone(&self.data), + event_handler: Arc::clone(&self.event_handler), + #[cfg(feature = "framework")] + framework: Arc::clone(&self.framework), + manager_tx: self.manager_tx.clone(), + threadpool: self.threadpool.clone(), + #[cfg(feature = "voice")] + voice_manager: Arc::clone(&self.voice_manager), + shard, + }); let runner_info = ShardRunnerInfo { latency: None, diff --git a/src/client/bridge/gateway/shard_runner.rs b/src/client/bridge/gateway/shard_runner.rs index ba9e00e..bfe4214 100644 --- a/src/client/bridge/gateway/shard_runner.rs +++ b/src/client/bridge/gateway/shard_runner.rs @@ -17,6 +17,8 @@ use websocket::WebSocketError; #[cfg(feature = "framework")] use framework::Framework; +#[cfg(feature = "voice")] +use super::super::voice::ClientVoiceManager; /// A runner for managing a [`Shard`] and its respective WebSocket client. /// @@ -33,53 +35,27 @@ pub struct ShardRunner<H: EventHandler + Send + Sync + 'static> { runner_tx: Sender<InterMessage>, shard: Shard, threadpool: ThreadPool, + #[cfg(feature = "voice")] + voice_manager: Arc<Mutex<ClientVoiceManager>>, } impl<H: EventHandler + Send + Sync + 'static> ShardRunner<H> { /// Creates a new runner for a Shard. - #[allow(too_many_arguments)] - #[cfg(feature = "framework")] - pub fn new( - shard: Shard, - manager_tx: Sender<ShardManagerMessage>, - framework: Arc<Mutex<Option<Box<Framework + Send>>>>, - data: Arc<Mutex<ShareMap>>, - event_handler: Arc<H>, - threadpool: ThreadPool, - ) -> Self { - let (tx, rx) = mpsc::channel(); - - Self { - runner_rx: rx, - runner_tx: tx, - data, - event_handler, - framework, - manager_tx, - shard, - threadpool, - } - } - - /// Creates a new runner for a Shard. - #[cfg(not(feature = "framework"))] - pub fn new( - shard: Shard, - manager_tx: Sender<ShardManagerMessage>, - data: Arc<Mutex<ShareMap>>, - event_handler: Arc<H>, - threadpool: ThreadPool, - ) -> Self { + pub fn new(opt: ShardRunnerOptions<H>) -> Self { let (tx, rx) = mpsc::channel(); Self { runner_rx: rx, runner_tx: tx, - data, - event_handler, - manager_tx, - shard, - threadpool, + data: opt.data, + event_handler: opt.event_handler, + #[cfg(feature = "framework")] + framework: opt.framework, + manager_tx: opt.manager_tx, + shard: opt.shard, + threadpool: opt.threadpool, + #[cfg(feature = "voice")] + voice_manager: opt.voice_manager, } } @@ -132,19 +108,8 @@ impl<H: EventHandler + Send + Sync + 'static> ShardRunner<H> { return self.request_restart(); } - #[cfg(feature = "voice")] - { - for message in self.shard.cycle_voice_recv() { - if let Err(why) = self.shard.client.send_json(&message) { - println!("Err sending from voice over WS: {:?}", why); - } - } - } - let pre = self.shard.stage(); - let (event, action, successful) = self.recv_event(); - let post = self.shard.stage(); if post != pre { @@ -325,6 +290,39 @@ impl<H: EventHandler + Send + Sync + 'static> ShardRunner<H> { } } + #[cfg(feature = "voice")] + fn handle_voice_event(&self, event: &Event) { + match *event { + Event::Ready(_) => { + self.voice_manager.lock().set( + self.shard.shard_info()[0], + self.runner_tx.clone(), + ); + }, + Event::VoiceServerUpdate(ref event) => { + if let Some(guild_id) = event.guild_id { + let mut manager = self.voice_manager.lock(); + let mut search = manager.get_mut(guild_id); + + if let Some(handler) = search { + handler.update_server(&event.endpoint, &event.token); + } + } + }, + Event::VoiceStateUpdate(ref event) => { + if let Some(guild_id) = event.guild_id { + let mut manager = self.voice_manager.lock(); + let mut search = manager.get_mut(guild_id); + + if let Some(handler) = search { + handler.update_state(&event.voice_state); + } + } + }, + _ => {}, + } + } + // Receives values over the internal shard runner rx channel and handles // them. // @@ -431,11 +429,15 @@ impl<H: EventHandler + Send + Sync + 'static> ShardRunner<H> { }, }; - match event { - Ok(GatewayEvent::HeartbeatAck) => { - self.update_manager(); - }, - _ => {}, + if let Ok(GatewayEvent::HeartbeatAck) = event { + self.update_manager(); + } + + #[cfg(feature = "voice")] + { + if let Ok(GatewayEvent::Dispatch(_, ref event)) = event { + self.handle_voice_event(&event); + } } let event = match event { @@ -457,6 +459,11 @@ impl<H: EventHandler + Send + Sync + 'static> ShardRunner<H> { let msg = ShardManagerMessage::Restart(shard_id); let _ = self.manager_tx.send(msg); + #[cfg(feature = "voice")] + { + self.voice_manager.lock().manager_remove(&shard_id.0); + } + Ok(()) } @@ -468,3 +475,18 @@ impl<H: EventHandler + Send + Sync + 'static> ShardRunner<H> { }); } } + +/// Options to be passed to [`ShardRunner::new`]. +/// +/// [`ShardRunner::new`]: struct.ShardRunner.html#method.new +pub struct ShardRunnerOptions<H: EventHandler + Send + Sync + 'static> { + pub data: Arc<Mutex<ShareMap>>, + pub event_handler: Arc<H>, + #[cfg(feature = "framework")] + pub framework: Arc<Mutex<Option<Box<Framework + Send>>>>, + pub manager_tx: Sender<ShardManagerMessage>, + pub shard: Shard, + pub threadpool: ThreadPool, + #[cfg(feature = "voice")] + pub voice_manager: Arc<Mutex<ClientVoiceManager>>, +} diff --git a/src/client/bridge/mod.rs b/src/client/bridge/mod.rs index ea18d73..41fcdec 100644 --- a/src/client/bridge/mod.rs +++ b/src/client/bridge/mod.rs @@ -8,3 +8,6 @@ //! [`client`]: ../ pub mod gateway; + +#[cfg(feature = "voice")] +pub mod voice; diff --git a/src/client/bridge/voice/mod.rs b/src/client/bridge/voice/mod.rs new file mode 100644 index 0000000..e67366b --- /dev/null +++ b/src/client/bridge/voice/mod.rs @@ -0,0 +1,117 @@ +use gateway::InterMessage; +use std::collections::HashMap; +use std::sync::mpsc::Sender as MpscSender; +use ::model::id::{ChannelId, GuildId, UserId}; +use ::voice::{Handler, Manager}; +use ::utils; + +pub struct ClientVoiceManager { + managers: HashMap<u64, Manager>, + shard_count: u64, + user_id: UserId, +} + +impl ClientVoiceManager { + pub fn new(shard_count: u64, user_id: UserId) -> Self { + Self { + managers: HashMap::default(), + shard_count, + user_id, + } + } + + pub fn get<G: Into<GuildId>>(&self, guild_id: G) -> Option<&Handler> { + let (gid, sid) = self.manager_info(guild_id); + + self.managers.get(&sid)?.get(gid) + } + + pub fn get_mut<G: Into<GuildId>>(&mut self, guild_id: G) + -> Option<&mut Handler> { + let (gid, sid) = self.manager_info(guild_id); + + self.managers.get_mut(&sid)?.get_mut(gid) + } + + /// Refer to [`Manager::join`]. + /// + /// This is a shortcut to retrieving the inner [`Manager`] and then calling + /// its `join` method. + /// + /// [`Manager`]: ../../../voice/struct.Manager.html + /// [`Manager::join`]: ../../../voice/struct.Manager.html#method.join + pub fn join<C, G>(&mut self, guild_id: G, channel_id: C) + -> Option<&mut Handler> where C: Into<ChannelId>, G: Into<GuildId> { + let (gid, sid) = self.manager_info(guild_id); + + self.managers.get_mut(&sid).map(|manager| manager.join(gid, channel_id)) + } + + /// Refer to [`Manager::leave`]. + /// + /// This is a shortcut to retrieving the inner [`Manager`] and then calling + /// its `leave` method. + /// + /// [`Manager`]: ../../../voice/struct.Manager.html + /// [`Manager::leave`]: ../../../voice/struct.Manager.html#method.leave + pub fn leave<G: Into<GuildId>>(&mut self, guild_id: G) -> Option<()> { + let (gid, sid) = self.manager_info(guild_id); + + self.managers.get_mut(&sid).map(|manager| manager.leave(gid)) + } + + /// Refer to [`Manager::remove`]. + /// + /// This is a shortcut to retrieving the inner [`Manager`] and then calling + /// its `remove` method. + /// + /// [`Manager`]: ../../../voice/struct.Manager.html + /// [`Manager::remove`]: ../../../voice/struct.Manager.html#method.remove + pub fn remove<G: Into<GuildId>>(&mut self, guild_id: G) -> Option<()> { + let (gid, sid) = self.manager_info(guild_id); + + self.managers.get_mut(&sid).map(|manager| manager.leave(gid)) + } + + pub fn set(&mut self, shard_id: u64, sender: MpscSender<InterMessage>) { + self.managers.insert(shard_id, Manager::new(sender, self.user_id)); + } + + /// Sets the number of shards for the voice manager to use when calculating + /// guilds' shard numbers. + /// + /// You probably should not call this. + #[doc(hidden)] + pub fn set_shard_count(&mut self, shard_count: u64) { + self.shard_count = shard_count; + } + + /// Sets the ID of the user for the voice manager. + /// + /// You probably _really_ should not call this. + /// + /// But it's there if you need it. For some reason. + #[doc(hidden)] + pub fn set_user_id(&mut self, user_id: UserId) { + self.user_id = user_id; + } + + pub fn manager_get(&self, shard_id: &u64) -> Option<&Manager> { + self.managers.get(shard_id) + } + + pub fn manager_get_mut(&mut self, shard_id: &u64) -> Option<&mut Manager> { + self.managers.get_mut(shard_id) + } + + pub fn manager_remove(&mut self, shard_id: &u64) -> Option<Manager> { + self.managers.remove(shard_id) + } + + fn manager_info<G: Into<GuildId>>(&self, guild_id: G) -> (GuildId, u64) { + let guild_id = guild_id.into(); + let shard_id = utils::shard_id(guild_id.0, self.shard_count); + + (guild_id, shard_id) + } +} diff --git a/src/client/mod.rs b/src/client/mod.rs index 1219793..9dfaa10 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -47,6 +47,10 @@ use typemap::ShareMap; #[cfg(feature = "framework")] use framework::Framework; +#[cfg(feature = "voice")] +use model::id::UserId; +#[cfg(feature = "voice")] +use self::bridge::voice::ClientVoiceManager; /// The Client is the way to be able to start sending authenticated requests /// over the REST API, as well as initializing a WebSocket connection through @@ -276,6 +280,12 @@ pub struct Client { pub threadpool: ThreadPool, /// The token in use by the client. pub token: Arc<Mutex<String>>, + /// The voice manager for the client. + /// + /// This is an ergonomic structure for interfacing over shards' voice + /// connections. + #[cfg(feature = "voice")] + pub voice_manager: Arc<Mutex<ClientVoiceManager>>, /// URI that the client's shards will use to connect to the gateway. /// /// This is likely not important for production usage and is, at best, used @@ -337,6 +347,12 @@ impl Client { #[cfg(feature = "framework")] let framework = Arc::new(Mutex::new(None)); + #[cfg(feature = "voice")] + let voice_manager = Arc::new(Mutex::new(ClientVoiceManager::new( + 0, + UserId(0), + ))); + let (shard_manager, shard_manager_worker) = { ShardManager::new(ShardManagerOptions { data: &data, @@ -348,6 +364,8 @@ impl Client { shard_total: 0, threadpool: threadpool.clone(), token: &locked, + #[cfg(feature = "voice")] + voice_manager: &voice_manager, ws_url: &url, }) }; @@ -361,6 +379,8 @@ impl Client { shard_manager, shard_manager_worker, threadpool, + #[cfg(feature = "voice")] + voice_manager, }) } @@ -794,15 +814,31 @@ impl Client { // // [`ClientError::Shutdown`]: enum.ClientError.html#variant.Shutdown fn start_connection(&mut self, shard_data: [u64; 3]) -> Result<()> { - // Update the framework's current user if the feature is enabled. + #[cfg(feature = "voice")] + self.voice_manager.lock().set_shard_count(shard_data[2]); + + // This is kind of gross, but oh well. // - // This also acts as a form of check to ensure the token is correct. - #[cfg(all(feature = "standard_framework", feature = "framework"))] + // Both the framework and voice bridge need the user's ID, so we'll only + // retrieve it over REST if at least one of those are enabled. + #[cfg(any(all(feature = "standard_framework", feature = "framework"), + feature = "voice"))] { let user = http::get_current_user()?; - if let Some(ref mut framework) = *self.framework.lock() { - framework.update_current_user(user.id); + // Update the framework's current user if the feature is enabled. + // + // This also acts as a form of check to ensure the token is correct. + #[cfg(all(feature = "standard_framework", feature = "framework"))] + { + if let Some(ref mut framework) = *self.framework.lock() { + framework.update_current_user(user.id); + } + } + + #[cfg(feature = "voice")] + { + self.voice_manager.lock().set_user_id(user.id); } } |