Voice requests
This commit is contained in:
@@ -43,6 +43,9 @@ impl Server {
|
||||
ClientMessage::Typing { channel_id } => {
|
||||
indicator::start_typing(self, client, channel_id)?
|
||||
}
|
||||
|
||||
ClientMessage::JoinVoice { channel_id } => voice::join(self, client, channel_id)?,
|
||||
ClientMessage::LeaveVoice { channel_id } => voice::leave(self, client, channel_id)?,
|
||||
},
|
||||
|
||||
WsMessage::Binary(data) => {
|
||||
@@ -70,4 +73,27 @@ impl Server {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn broadcast_bin_to(
|
||||
self: &Arc<Self>,
|
||||
targets: &[&String],
|
||||
bytes: Vec<u8>,
|
||||
) -> crate::Result<()> {
|
||||
for c in self.clients.lock().unwrap().iter() {
|
||||
if !targets.contains(&&c.get_uuid()?) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let c = c.clone();
|
||||
let server = self.clone();
|
||||
let bytes = bytes.clone();
|
||||
std::thread::spawn(move || {
|
||||
server
|
||||
.wrap_err(&c, c.send_bin(&bytes))
|
||||
.expect("Failed to broadcast");
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
+45
-2
@@ -3,13 +3,56 @@ use std::sync::Arc;
|
||||
|
||||
crate::logger!(LOGGER "Voice chat");
|
||||
|
||||
pub fn voice(server: &Arc<Server>, client: &Client, data: &[u8]) -> crate::Result<()> {
|
||||
let prefix = (10u16).to_le_bytes();
|
||||
pub fn join(server: &Arc<Server>, client: &Client, channel_id: &str) -> crate::Result<()> {
|
||||
let user_id = client.get_uuid()?;
|
||||
|
||||
let voice_id = server
|
||||
.voice
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set(user_id.clone(), channel_id.to_string());
|
||||
|
||||
server.broadcast(crate::types::message::ServerMessage::VoiceJoin {
|
||||
user_id,
|
||||
channel_id: channel_id.to_string(),
|
||||
voice_id: voice_id,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn leave(server: &Arc<Server>, client: &Client, channel_id: &str) -> crate::Result<()> {
|
||||
let user_id = client.get_uuid()?;
|
||||
|
||||
let Some(voice_id) = server.voice.lock().unwrap().remove(channel_id, &user_id) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
server.broadcast(crate::types::message::ServerMessage::VoiceLeave {
|
||||
user_id,
|
||||
channel_id: channel_id.to_string(),
|
||||
voice_id,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn voice(server: &Arc<Server>, client: &Client, data: &[u8]) -> crate::Result<()> {
|
||||
let v = server.voice.lock().unwrap();
|
||||
let Some((channel_id, voice_id)) = v.find_user(&client.get_uuid()?) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let targets = v.get(&channel_id);
|
||||
|
||||
let prefix = voice_id.to_le_bytes();
|
||||
let mut payload = Vec::with_capacity(prefix.len() + data.len());
|
||||
payload.extend_from_slice(&prefix);
|
||||
payload.extend_from_slice(data);
|
||||
|
||||
client.send_bin(&payload)?;
|
||||
|
||||
server.broadcast_bin_to(&targets, payload)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+3
-1
@@ -25,7 +25,7 @@ use crate::{
|
||||
cli, logger,
|
||||
plugin::{Plugin, loader::PluginLoader, types::LoaderMessage},
|
||||
types::{self, message::WsMessage},
|
||||
utils::{self, auth, client::Client},
|
||||
utils::{self, auth, client::Client, voice::Voice},
|
||||
};
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
@@ -45,6 +45,7 @@ pub struct Server {
|
||||
pub db: utils::database::Database,
|
||||
pub shutting_down: AtomicBool,
|
||||
pub indicators: Mutex<Vec<crate::requests::indicator::IndicatorContext>>,
|
||||
pub voice: Mutex<crate::utils::voice::Voice>,
|
||||
}
|
||||
|
||||
impl Default for ServerConfig {
|
||||
@@ -81,6 +82,7 @@ impl Server {
|
||||
plugins: Mutex::new(Vec::new()),
|
||||
shutting_down: AtomicBool::new(false),
|
||||
indicators: Mutex::new(Vec::new()),
|
||||
voice: Mutex::new(Voice::new()),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -92,6 +92,14 @@ pub mod message {
|
||||
Typing {
|
||||
channel_id: String,
|
||||
},
|
||||
|
||||
JoinVoice {
|
||||
channel_id: String,
|
||||
},
|
||||
|
||||
LeaveVoice {
|
||||
channel_id: String,
|
||||
},
|
||||
}
|
||||
|
||||
/// Messages sent *from the server* to the client
|
||||
@@ -136,6 +144,18 @@ pub mod message {
|
||||
},
|
||||
|
||||
Chunk(Vec<Message>),
|
||||
|
||||
VoiceJoin {
|
||||
user_id: String,
|
||||
channel_id: String,
|
||||
voice_id: u16,
|
||||
},
|
||||
|
||||
VoiceLeave {
|
||||
user_id: String,
|
||||
channel_id: String,
|
||||
voice_id: u16,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
||||
@@ -3,3 +3,4 @@ pub mod client;
|
||||
pub mod database;
|
||||
pub mod logger;
|
||||
pub mod vfs;
|
||||
pub mod voice;
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct Voice {
|
||||
// channel_id -> (user_id -> voice_id)
|
||||
connections: HashMap<String, HashMap<String, u16>>,
|
||||
}
|
||||
|
||||
impl Voice {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
connections: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert or update a user's voice_id in a channel
|
||||
pub fn set(&mut self, user_id: String, channel_id: String) -> u16 {
|
||||
let voice_id = rand::random::<u16>();
|
||||
|
||||
self.connections
|
||||
.entry(channel_id)
|
||||
.or_insert_with(HashMap::new)
|
||||
.insert(user_id, voice_id);
|
||||
|
||||
voice_id
|
||||
}
|
||||
|
||||
/// Remove a user from a channel
|
||||
/// Returns their voice_id if they existed
|
||||
pub fn remove(&mut self, channel_id: &str, user_id: &str) -> Option<u16> {
|
||||
let channel = self.connections.get_mut(channel_id)?;
|
||||
let voice_id = channel.remove(user_id)?;
|
||||
|
||||
// Clean up empty channels
|
||||
if channel.is_empty() {
|
||||
self.connections.remove(channel_id);
|
||||
}
|
||||
|
||||
Some(voice_id)
|
||||
}
|
||||
|
||||
/// Get all users + voice_ids in a channel
|
||||
pub fn get(&self, channel_id: &str) -> Vec<&String> {
|
||||
self.connections
|
||||
.get(channel_id)
|
||||
.map(|users| users.keys().collect())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Check if a user exists in a channel
|
||||
pub fn check(&self, channel_id: &str, user_id: &str) -> Option<u16> {
|
||||
self.connections
|
||||
.get(channel_id)
|
||||
.and_then(|users| users.get(user_id).copied())
|
||||
}
|
||||
|
||||
pub fn find_user(&self, user_id: &str) -> Option<(&String, u16)> {
|
||||
self.connections
|
||||
.iter()
|
||||
.find_map(|(channel_id, users)| users.get(user_id).map(|v| (channel_id, *v)))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user