use std::{ collections::HashMap, sync::{Arc, RwLock}, }; use tokio::{ sync::mpsc::{channel, Receiver, Sender}, task::JoinHandle, }; use crate::{ room::{RoomId, RoomRegistry}, table::AnonTable, }; /// Opaque player id #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct PlayerId(u64); #[derive(Clone)] pub struct PlayerHandle { tx: Sender, } impl PlayerHandle { pub async fn subscribe(&mut self) -> Receiver { let (sender, rx) = channel(32); self.tx.send(PlayerCommand::AddSocket { sender }).await; rx } pub async fn create_room(&mut self) { match self.tx.send(PlayerCommand::CreateRoom).await { Ok(_) => {} Err(_) => { panic!("unexpected err"); } }; } pub async fn send_message(&mut self, room_id: RoomId, body: String) { self.tx .send(PlayerCommand::SendMessage { room_id, body }) .await; } pub async fn join_room(&mut self, room_id: RoomId) { self.tx.send(PlayerCommand::JoinRoom { room_id }).await; } pub async fn receive_message(&mut self, room_id: RoomId, author: PlayerId, body: String) { self.tx .send(PlayerCommand::IncomingMessage { room_id, author, body, }) .await; } } pub enum Updates { RoomJoined { room_id: RoomId }, NewMessage { room_id: RoomId, body: String }, } enum PlayerCommand { AddSocket { sender: Sender, }, CreateRoom, JoinRoom { room_id: RoomId, }, SendMessage { room_id: RoomId, body: String, }, IncomingMessage { room_id: RoomId, author: PlayerId, body: String, }, } #[derive(Clone)] pub struct PlayerRegistry(Arc>); impl PlayerRegistry { pub fn empty(room_registry: RoomRegistry) -> PlayerRegistry { let inner = PlayerRegistryInner { next_id: PlayerId(0), room_registry, players: HashMap::new(), }; PlayerRegistry(Arc::new(RwLock::new(inner))) } pub async fn create_player(&mut self) -> (PlayerId, PlayerHandle) { let player = Player { sockets: AnonTable::new(), }; let mut inner = self.0.write().unwrap(); let id = inner.next_id; inner.next_id.0 += 1; let (handle, fiber) = player.launch(id, inner.room_registry.clone()); inner.players.insert(id, (handle.clone(), fiber)); (id, handle) } } struct PlayerRegistryInner { next_id: PlayerId, room_registry: RoomRegistry, players: HashMap)>, } struct Player { sockets: AnonTable>, } impl Player { fn launch( mut self, player_id: PlayerId, mut rooms: RoomRegistry, ) -> (PlayerHandle, JoinHandle) { let (tx, mut rx) = channel(32); let handle = PlayerHandle { tx }; let handle_clone = handle.clone(); let fiber = tokio::task::spawn(async move { while let Some(cmd) = rx.recv().await { match cmd { PlayerCommand::AddSocket { sender } => { self.sockets.insert(sender); } PlayerCommand::CreateRoom => { let (room_id, room_handle) = rooms.create_room(); } PlayerCommand::JoinRoom { room_id } => { let room = rooms.get_room(room_id); match room { Some(mut room) => { room.subscribe(player_id, handle.clone()).await; } None => { tracing::info!("no room found"); } } } PlayerCommand::SendMessage { room_id, body } => { let room = rooms.get_room(room_id); match room { Some(mut room) => { room.send_message(player_id, body).await; } None => { tracing::info!("no room found"); } } } PlayerCommand::IncomingMessage { room_id, author, body, } => { tracing::info!("Handling incoming message"); for socket in &self.sockets { socket .send(Updates::NewMessage { room_id, body: body.clone(), }) .await; } } } } self }); (handle_clone, fiber) } }