lavina/src/core/room.rs

184 lines
5.2 KiB
Rust
Raw Normal View History

2023-02-04 01:01:49 +00:00
//! Domain of rooms — chats with multiple participants.
2023-02-03 22:43:59 +00:00
use std::{
collections::HashMap,
hash::Hash,
sync::{Arc, RwLock},
};
2023-02-12 22:23:52 +00:00
use prometheus::{IntGauge, Registry as MetricRegistry};
use tokio::sync::RwLock as AsyncRwLock;
2023-02-03 22:43:59 +00:00
use crate::{
core::player::{PlayerHandle, PlayerId},
2023-02-03 22:43:59 +00:00
prelude::*,
};
use super::player::{ConnectionId, Updates};
2023-02-13 18:32:52 +00:00
2023-02-03 22:43:59 +00:00
/// Opaque room id
2023-02-12 23:31:16 +00:00
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2023-02-14 19:07:07 +00:00
pub struct RoomId(ByteVec);
impl RoomId {
pub fn from_bytes(bytes: ByteVec) -> Result<RoomId> {
if bytes.len() > 32 {
return Err(anyhow::Error::msg(
"Room name cannot be longer than 32 symbols",
));
2023-02-14 19:07:07 +00:00
}
if bytes.contains(&b' ') {
return Err(anyhow::Error::msg("Room name cannot contain spaces"));
}
Ok(RoomId(bytes))
}
pub fn as_bytes(&self) -> &ByteVec {
&self.0
}
2023-02-14 19:07:07 +00:00
}
2023-02-03 22:43:59 +00:00
/// Shared datastructure for storing metadata about rooms.
#[derive(Clone)]
pub struct RoomRegistry(Arc<RwLock<RoomRegistryInner>>);
impl RoomRegistry {
2023-02-12 22:23:52 +00:00
pub fn empty(metrics: &mut MetricRegistry) -> Result<RoomRegistry> {
let metric_active_rooms =
IntGauge::new("chat_rooms_active", "Number of alive room actors")?;
metrics.register(Box::new(metric_active_rooms.clone()))?;
2023-02-03 22:43:59 +00:00
let inner = RoomRegistryInner {
rooms: HashMap::new(),
2023-02-12 22:23:52 +00:00
metric_active_rooms,
2023-02-03 22:43:59 +00:00
};
2023-02-12 22:23:52 +00:00
Ok(RoomRegistry(Arc::new(RwLock::new(inner))))
2023-02-03 22:43:59 +00:00
}
2023-02-12 23:31:16 +00:00
pub fn get_or_create_room(&mut self, room_id: RoomId) -> RoomHandle {
2023-02-03 22:43:59 +00:00
let mut inner = self.0.write().unwrap();
if let Some(room_handle) = inner.rooms.get(&room_id) {
2023-02-13 18:32:52 +00:00
room_handle.clone()
} else {
let room = Room {
room_id: room_id.clone(),
subscriptions: HashMap::new(),
2023-02-14 18:46:42 +00:00
topic: b"New room".to_vec(),
};
let room_handle = RoomHandle(Arc::new(AsyncRwLock::new(room)));
inner.rooms.insert(room_id, room_handle.clone());
2023-02-13 18:32:52 +00:00
inner.metric_active_rooms.inc();
room_handle
}
2023-02-03 22:43:59 +00:00
}
pub fn get_room(&self, room_id: &RoomId) -> Option<RoomHandle> {
2023-02-03 22:43:59 +00:00
let inner = self.0.read().unwrap();
let res = inner.rooms.get(room_id);
res.map(|r| r.clone())
2023-02-03 22:43:59 +00:00
}
}
struct RoomRegistryInner {
rooms: HashMap<RoomId, RoomHandle>,
2023-02-12 22:23:52 +00:00
metric_active_rooms: IntGauge,
2023-02-03 22:43:59 +00:00
}
#[derive(Clone)]
pub struct RoomHandle(Arc<AsyncRwLock<Room>>);
2023-02-03 22:43:59 +00:00
impl RoomHandle {
pub async fn subscribe(
&self,
player_id: PlayerId,
connection_id: ConnectionId,
player_handle: PlayerHandle,
) {
let mut lock = self.0.write().await;
lock.add_subscriber(player_id, connection_id, player_handle)
.await;
2023-02-03 22:43:59 +00:00
}
2023-02-13 18:32:52 +00:00
pub async fn send_message(
2023-02-14 00:44:03 +00:00
&self,
2023-02-13 18:32:52 +00:00
player_id: PlayerId,
connection_id: ConnectionId,
body: String,
) {
let lock = self.0.read().await;
lock.send_message(player_id, connection_id, body).await;
2023-02-03 22:43:59 +00:00
}
2023-02-14 00:44:03 +00:00
pub async fn get_members(&self) -> Vec<PlayerId> {
let lock = self.0.read().await;
lock.subscriptions
.keys()
.map(|x| x.clone())
.collect::<Vec<_>>()
}
pub async fn get_room_info(&self) -> RoomInfo {
let lock = self.0.read().await;
RoomInfo {
id: lock.room_id.clone(),
members: lock
.subscriptions
.keys()
.map(|x| x.clone())
.collect::<Vec<_>>(),
2023-02-14 18:46:42 +00:00
topic: lock.topic.clone(),
}
}
pub async fn set_topic(&mut self, new_topic: ByteVec) {
let mut lock = self.0.write().await;
lock.topic = new_topic.clone();
for (_, player_handle) in &lock.subscriptions {
let update = Updates::RoomTopicChanged {
room_id: lock.room_id.clone(),
new_topic: new_topic.clone(),
};
player_handle.update(update.clone()).await;
}
2023-02-14 00:44:03 +00:00
}
2023-02-03 22:43:59 +00:00
}
struct Room {
room_id: RoomId,
2023-02-03 22:43:59 +00:00
subscriptions: HashMap<PlayerId, PlayerHandle>,
2023-02-14 18:46:42 +00:00
topic: ByteVec,
2023-02-03 22:43:59 +00:00
}
impl Room {
async fn add_subscriber(
&mut self,
player_id: PlayerId,
connection_id: ConnectionId,
player_handle: PlayerHandle,
) {
tracing::info!("Adding a subscriber to room");
self.subscriptions.insert(player_id.clone(), player_handle);
let update = Updates::RoomJoined {
room_id: self.room_id.clone(),
new_member_id: player_id.clone(),
connection_id: connection_id.clone(),
};
for (_, sub) in &self.subscriptions {
sub.update(update.clone()).await;
}
}
async fn send_message(&self, author_id: PlayerId, connection_id: ConnectionId, body: String) {
tracing::info!("Adding a message to room");
let update = Updates::NewMessage {
room_id: self.room_id.clone(),
connection_id,
author_id,
body,
};
for (_, sub) in &self.subscriptions {
log::info!("Sending a message from room to player");
sub.update(update.clone()).await;
}
2023-02-03 22:43:59 +00:00
}
}
2023-02-14 00:44:03 +00:00
pub struct RoomInfo {
pub id: RoomId,
pub members: Vec<PlayerId>,
pub topic: ByteVec,
}