Voice requests
This commit is contained in:
@@ -43,6 +43,9 @@ impl Server {
|
|||||||
ClientMessage::Typing { channel_id } => {
|
ClientMessage::Typing { channel_id } => {
|
||||||
indicator::start_typing(self, client, 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) => {
|
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");
|
crate::logger!(LOGGER "Voice chat");
|
||||||
|
|
||||||
pub fn voice(server: &Arc<Server>, client: &Client, data: &[u8]) -> crate::Result<()> {
|
pub fn join(server: &Arc<Server>, client: &Client, channel_id: &str) -> crate::Result<()> {
|
||||||
let prefix = (10u16).to_le_bytes();
|
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());
|
let mut payload = Vec::with_capacity(prefix.len() + data.len());
|
||||||
payload.extend_from_slice(&prefix);
|
payload.extend_from_slice(&prefix);
|
||||||
payload.extend_from_slice(data);
|
payload.extend_from_slice(data);
|
||||||
|
|
||||||
client.send_bin(&payload)?;
|
client.send_bin(&payload)?;
|
||||||
|
|
||||||
|
server.broadcast_bin_to(&targets, payload)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -25,7 +25,7 @@ use crate::{
|
|||||||
cli, logger,
|
cli, logger,
|
||||||
plugin::{Plugin, loader::PluginLoader, types::LoaderMessage},
|
plugin::{Plugin, loader::PluginLoader, types::LoaderMessage},
|
||||||
types::{self, message::WsMessage},
|
types::{self, message::WsMessage},
|
||||||
utils::{self, auth, client::Client},
|
utils::{self, auth, client::Client, voice::Voice},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(serde::Serialize, serde::Deserialize)]
|
#[derive(serde::Serialize, serde::Deserialize)]
|
||||||
@@ -45,6 +45,7 @@ pub struct Server {
|
|||||||
pub db: utils::database::Database,
|
pub db: utils::database::Database,
|
||||||
pub shutting_down: AtomicBool,
|
pub shutting_down: AtomicBool,
|
||||||
pub indicators: Mutex<Vec<crate::requests::indicator::IndicatorContext>>,
|
pub indicators: Mutex<Vec<crate::requests::indicator::IndicatorContext>>,
|
||||||
|
pub voice: Mutex<crate::utils::voice::Voice>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ServerConfig {
|
impl Default for ServerConfig {
|
||||||
@@ -81,6 +82,7 @@ impl Server {
|
|||||||
plugins: Mutex::new(Vec::new()),
|
plugins: Mutex::new(Vec::new()),
|
||||||
shutting_down: AtomicBool::new(false),
|
shutting_down: AtomicBool::new(false),
|
||||||
indicators: Mutex::new(Vec::new()),
|
indicators: Mutex::new(Vec::new()),
|
||||||
|
voice: Mutex::new(Voice::new()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -92,6 +92,14 @@ pub mod message {
|
|||||||
Typing {
|
Typing {
|
||||||
channel_id: String,
|
channel_id: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
JoinVoice {
|
||||||
|
channel_id: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
LeaveVoice {
|
||||||
|
channel_id: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Messages sent *from the server* to the client
|
/// Messages sent *from the server* to the client
|
||||||
@@ -136,6 +144,18 @@ pub mod message {
|
|||||||
},
|
},
|
||||||
|
|
||||||
Chunk(Vec<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)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
|||||||
@@ -3,3 +3,4 @@ pub mod client;
|
|||||||
pub mod database;
|
pub mod database;
|
||||||
pub mod logger;
|
pub mod logger;
|
||||||
pub mod vfs;
|
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