From 15d3e978a9fd90eb7cd9f9eaf324744b8bee053e Mon Sep 17 00:00:00 2001 From: Lasm Gratel Date: Wed, 29 Sep 2021 21:19:53 +0800 Subject: [PATCH] refactor --- Cargo.lock | 6 +- cardgame-client/Cargo.toml | 2 +- cardgame-server/Cargo.toml | 4 +- cardgame-server/src/main.rs | 234 ++++++++------------------ cardgame-server/src/server_lobby.rs | 234 +++++++++++++++++++------- cardgame-server/src/server_network.rs | 72 ++++++++ cardgame/src/lobby.rs | 41 ++++- 7 files changed, 362 insertions(+), 231 deletions(-) create mode 100644 cardgame-server/src/server_network.rs diff --git a/Cargo.lock b/Cargo.lock index 33244ba..28fd182 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -97,9 +97,9 @@ dependencies = [ [[package]] name = "async-std" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f06685bad74e0570f5213741bea82158279a4103d988e57bfada11ad230341" +checksum = "f8056f1455169ab86dd47b47391e4ab0cbd25410a70e9fe675544f49bafaf952" dependencies = [ "async-channel", "async-global-executor", @@ -252,6 +252,8 @@ dependencies = [ name = "cardgame-server" version = "0.1.0" dependencies = [ + "anyhow", + "async-std", "bimap", "bincode", "cardgame", diff --git a/cardgame-client/Cargo.toml b/cardgame-client/Cargo.toml index 152a69f..cd9d074 100644 --- a/cardgame-client/Cargo.toml +++ b/cardgame-client/Cargo.toml @@ -12,6 +12,6 @@ serde = {version = "1.0", features = ["derive"]} bincode = "1.3.3" message-io = "0.13" uuid = {version = "0.8", features = ["serde", "v4"]} -async-std = "1.9.0" +async-std = "1.10" humantime = "2.1.0" timeago = "0.3.0" diff --git a/cardgame-server/Cargo.toml b/cardgame-server/Cargo.toml index 7d3281c..b853e3c 100644 --- a/cardgame-server/Cargo.toml +++ b/cardgame-server/Cargo.toml @@ -13,4 +13,6 @@ cardgame = {path = "../cardgame"} message-io = "0.13" uuid = {version = "0.8", features = ["serde", "v4"]} humantime = "2.1.0" -timeago = "0.3.0" \ No newline at end of file +timeago = "0.3.0" +anyhow = "1.0" +async-std = "1.10" \ No newline at end of file diff --git a/cardgame-server/src/main.rs b/cardgame-server/src/main.rs index d1f82a6..8294f4b 100644 --- a/cardgame-server/src/main.rs +++ b/cardgame-server/src/main.rs @@ -7,9 +7,16 @@ use std::collections::HashMap; use crate::server_lobby::ServerLobby; use message_io::node::NodeEvent; use std::time::Duration; +use std::sync::Mutex; +use std::rc::Rc; +use crate::server_network::{MessagePacket, MessageTarget}; +/// 大厅 pub mod server_lobby; +/// 网络管理 +pub mod server_network; + pub fn main() { // Create a node, the main message-io entity. It is divided in 2 parts: // The 'handler', used to make actions (connect, send messages, signals, stop the node...) @@ -25,12 +32,12 @@ pub fn main() { .listen(Transport::FramedTcp, address) .expect("Unable to listen on the address!"); - let network = handler.network(); let signals = handler.signals(); + let network = handler.network(); println!("服务器在 {} 上监听", address); - let mut lobby = ServerLobby::new(network); + let lobby = Rc::new(Mutex::new(ServerLobby::new(network))); let mut clients: Vec = vec![]; let mut client_map: BiHashMap = BiHashMap::new(); let mut user_manager = UserManager::new(String::from("users")); @@ -61,6 +68,16 @@ pub fn main() { let to_send = bincode::serialize(msg).unwrap(); network.send(endpoint, &to_send); }; + let send_packet = |packet: MessagePacket| { + match packet.0 { + MessageTarget::Reply => { + lobby.lock().unwrap().send_packet((MessageTarget::Endpoint(endpoint), packet.1)); + } + _ => { + lobby.lock().unwrap().send_packet(packet); + } + } + }; let send_to_user = |user: &UserId, msg: &S2CMessage| { let to_send = bincode::serialize(msg).unwrap(); let user_endpoint = client_map.get_by_left(user).unwrap(); @@ -75,89 +92,18 @@ pub fn main() { C2SMessage::Login(username) => { println!("玩家 {} 登入", username); send_to_client(&S2CMessage::LoggedIn); - - client_map.insert(username.clone(), endpoint); - - let user = client_map.get_by_right(&endpoint).map(|x| user_manager.get_user(x).unwrap()).unwrap(); - match user_states.get(&username) { - None => { - user_states.insert(username, UserState::Idle); - } - Some(state) => { - match state { - UserState::Idle => { - lobby.login(user.id); - } - UserState::Matchmaking => { - // 断线后取消匹配 - user_states.insert(user.id.clone(), UserState::Idle); - lobby.login(user.id); - } - UserState::Playing(_room) => { - // TODO 断线重连 - } - } - } - } + lobby.lock().unwrap().connect(username, endpoint); } // 加入房间 C2SMessage::JoinRoom(room_name) => { - let user = get_user().unwrap(); - let result = lobby.join_room(&room_name, user.id.clone()); - match result { - Ok(room) => { - send_to_client(&S2CMessage::RoomJoined(room_name.clone())); - println!( - "{} 加入 {} 房间,共有 {} 人", - user.id, - room_name, - room.users.len() - ); - - if room.users.len() == 3 { - // 开始游戏 - if let Some(room) = lobby.rooms.get_mut(&room_name) { - match room.start_game() { - Ok((landlord_player, players)) => { - for player in players { - send_to_user(&player.user, &S2CMessage::GameStarted(player.cards.clone(), landlord_player.user.clone())); - user_states.insert(player.user.clone(), UserState::Playing(room_name.clone())); - } - } - Err(err) => { - send_to_client(&S2CMessage::RoomErr(err)); - } - } - } else { - send_to_client(&S2CMessage::RoomErr(RoomError::NotReady)); - } - } - } - Err(err) => { - send_to_client(&S2CMessage::LobbyErr(err)); - } - } + lobby.lock().unwrap().join_room_by_endpoint(&room_name, endpoint); } C2SMessage::StartGame(room_name) => { - if let Some(room) = lobby.rooms.get_mut(&room_name) { - match room.start_game() { - Ok((landlord_player, players)) => { - for player in players { - send_to_user(&player.user, &S2CMessage::GameStarted(player.cards.clone(), landlord_player.user.clone())); - user_states.insert(player.user.clone(), UserState::Playing(room_name.clone())); - } - } - Err(err) => { - send_to_client(&S2CMessage::RoomErr(err)); - } - } - } else { - send_to_client(&S2CMessage::RoomErr(RoomError::NotReady)); - } + lobby.lock().unwrap().start_game_by_name(&room_name); } C2SMessage::ChooseLandlord(choose) => { let room = if let Some(UserState::Playing(room_name)) = user_states.get(client_map.get_by_right(&endpoint).unwrap()) { - lobby.rooms.get_mut(room_name) + lobby.lock().unwrap().rooms.get_mut(room_name) } else { None }; @@ -191,7 +137,7 @@ pub fn main() { } C2SMessage::Pass => { let room = if let Some(UserState::Playing(room_name)) = user_states.get(client_map.get_by_right(&endpoint).unwrap()) { - lobby.rooms.get_mut(room_name) + lobby.lock().unwrap().rooms.get_mut(room_name) } else { None }; @@ -219,47 +165,18 @@ pub fn main() { } C2SMessage::RematchVote(rematch) => { let user_id = client_map.get_by_right(&endpoint).unwrap(); - let room = if let Some(UserState::Playing(room_name)) = user_states.get(user_id) { - lobby.rooms.get_mut(room_name) - } else { - None - }; + match lobby.lock().unwrap().rematch_vote(user_id, rematch) { + Ok(packet) => { - if let Some(room) = room { - let current_state = room.state.clone(); - let room_name = room.name.clone(); - if let RoomState::WaitingForRematch(count) = current_state { - if rematch { - if count + 1 == 3 { - room.game.reset(); + } + Err(err) => { - match room.start_game() { - Ok((landlord_player, players)) => { - for player in players { - send_to_user(&player.user, &S2CMessage::GameStarted(player.cards.clone(), landlord_player.user.clone())); - user_states.insert(player.user.clone(), UserState::Playing(room_name.clone())); - } - } - Err(err) => { - send_to_client(&S2CMessage::RoomErr(err)); - } - } - } - room.state = RoomState::WaitingForRematch(count + 1); - send_to_client(&S2CMessage::RematchVote(user_id.clone(), rematch, count + 1)); - } else { - let name = room.name.clone(); - *room = Room::new(name); - send_to_client(&S2CMessage::RematchVote(user_id.clone(), rematch, count)); - } - } else { - send_to_client(&S2CMessage::RoomErr(RoomError::NotReady)); } } } C2SMessage::SubmitCards(cards) => { let room = if let Some(UserState::Playing(room_name)) = user_states.get(client_map.get_by_right(&endpoint).unwrap()) { - lobby.rooms.get_mut(room_name) + lobby.lock().unwrap().rooms.get_mut(room_name) } else { None }; @@ -298,12 +215,12 @@ pub fn main() { C2SMessage::Matchmake => { let user_id = get_user_id().unwrap(); matchmake_timer = 120; // 重设等待玩家倒计时 - lobby.waiting_list.push(user_id.clone()); + lobby.lock().unwrap().waiting_list.push(user_id.clone()); user_states.insert(user_id.clone(), UserState::Matchmaking); signals.send(Signal::Matchmake); } C2SMessage::QueryRoomList => { - let data = lobby.rooms.keys().map(|x| x.to_string()).collect(); + let data = lobby.lock().unwrap().rooms.keys().map(|x| x.to_string()).collect(); send_to_client(&S2CMessage::RoomList(data)); } _ => { @@ -314,7 +231,7 @@ pub fn main() { NetEvent::Disconnected(endpoint) => { println!("{} 已断开", endpoint); if let Some(user) = client_map.get_by_right(&endpoint).map(|x| user_manager.get_user(x).unwrap()) { - lobby.disconnect(&user.id); + lobby.lock().unwrap().disconnect(&user.id); user_states.remove(&user.id); } let client = clients @@ -326,71 +243,56 @@ pub fn main() { }, } }, - NodeEvent::Signal(signal) => match signal { - cardgame::Signal::Matchmake => { - let send_to_user = |user: &UserId, msg: &S2CMessage| -> () { - let to_send = bincode::serialize(msg).unwrap(); - let user_endpoint = client_map.get_by_left(user).unwrap(); - network.send(*user_endpoint, &to_send); - }; + NodeEvent::Signal(signal) => if let cardgame::Signal::Matchmake = signal { + let send_to_user = |user: &UserId, msg: &S2CMessage| { + let to_send = bincode::serialize(msg).unwrap(); + let user_endpoint = client_map.get_by_left(user).unwrap(); + network.send(*user_endpoint, &to_send); + }; - if lobby.waiting_list.len() == 0 { - // 没有人在等待队列,停止匹配 - matchmake_timer = 0; - } else if matchmake_timer > 0 { - matchmake_timer -= 1; - signals.send_with_timer(Signal::Matchmake, Duration::from_secs(1)); + if lobby.lock().unwrap().waiting_list.len() == 0 { + // 没有人在等待队列,停止匹配 + matchmake_timer = 0; + } else if matchmake_timer > 0 { + matchmake_timer -= 1; + signals.send_with_timer(Signal::Matchmake, Duration::from_secs(1)); - if lobby.waiting_list.len() >= 3 { - lobby.waiting_list.shuffle(&mut rng); + if lobby.lock().unwrap().waiting_list.len() >= 3 { + lobby.lock().unwrap().waiting_list.shuffle(&mut rng); - for i in 0..(lobby.waiting_list.len() / 3) { - matchmake_id += 1; - let room_name = format!("Matchmake Room #{}", matchmake_id); - for j in 0..3 { - let user = lobby.waiting_list.get(j).unwrap().clone(); + for i in 0..(lobby.lock().unwrap().waiting_list.len() / 3) { + matchmake_id += 1; + let room_name = format!("Matchmake Room #{}", matchmake_id); + for j in 0..3 { + let user = lobby.lock().unwrap().waiting_list.get(j).unwrap().clone(); - let result = lobby.join_room(&room_name, user.clone()); - match result { - Ok(room) => { - send_to_user(&user, &S2CMessage::RoomJoined(room.name.clone())); - } - Err(err) => { - // send_to_client(&S2CMessage::LobbyErr(err)); - } - } - } - for _ in 0..3 { - lobby.waiting_list.remove(0); - } - - let room = lobby.rooms.get_mut(&room_name).unwrap(); - // 开始游戏 - match room.start_game() { - Ok((landlord_player, players)) => { - for player in players { - send_to_user(&player.user, &S2CMessage::GameStarted(player.cards.clone(), landlord_player.user.clone())); - user_states.insert(player.user.clone(), UserState::Playing(room_name.clone())); - } + let result = lobby.lock().unwrap().join_room(&room_name, user.clone()); + match result { + Ok(packet) => { + lobby.lock().unwrap().send_packet(packet); } Err(err) => { - // send_to_client(&S2CMessage::RoomErr(err)); + // send_to_client(&S2CMessage::LobbyErr(err)); } } } - } - - for user in lobby.waiting_list.iter() { - let msg = &S2CMessage::MatchmakeStatus(lobby.waiting_list.len() as u32, Duration::from_secs(120), Duration::from_secs(matchmake_timer)); + for _ in 0..3 { + lobby.lock().unwrap().waiting_list.remove(0); + } - let to_send = bincode::serialize(msg).unwrap(); - let user_endpoint = client_map.get_by_left(user).unwrap(); - network.send(*user_endpoint, &to_send); + lobby.lock().unwrap().start_game_by_name(&room_name); } + } + + for user in lobby.lock().unwrap().waiting_list.iter() { + let msg = &S2CMessage::MatchmakeStatus(lobby.lock().unwrap().waiting_list.len() as u32, Duration::from_secs(120), Duration::from_secs(matchmake_timer)); + let to_send = bincode::serialize(msg).unwrap(); + let user_endpoint = client_map.get_by_left(user).unwrap(); + network.send(*user_endpoint, &to_send); } + } - _ => {} } }); }); diff --git a/cardgame-server/src/server_lobby.rs b/cardgame-server/src/server_lobby.rs index cc14e7a..765fd7f 100644 --- a/cardgame-server/src/server_lobby.rs +++ b/cardgame-server/src/server_lobby.rs @@ -3,8 +3,10 @@ use std::collections::HashMap; use message_io::network::{NetworkController, Endpoint, SendStatus}; use cardgame::user::{UserId, UserState}; -use cardgame::{Game, Lobby, LobbyError, Room, RoomState, S2CMessage}; +use cardgame::{Game, Lobby, LobbyError, Room, RoomState, S2CMessage, RoomError, GameState}; use bimap::{BiMap, BiHashMap}; +use anyhow::Error; +use crate::server_network::{NetworkManager, MessagePacket, MessageTarget}; /// 大厅的服务器实现。 pub struct ServerLobby<'a> { @@ -38,7 +40,35 @@ impl ServerLobby<'_> { } } - pub fn join_room(&mut self, room_name: &str, user: String) -> Result<&Room, LobbyError> { + pub fn connect(&mut self, user_id: UserId, endpoint: Endpoint) { + self.login(user_id.clone()); + self.network.connect(user_id.clone(), endpoint); + + match self.user_states.get(&user_id) { + None => { + self.user_states.insert(user_id, UserState::Idle); + } + Some(state) => { + match state { + UserState::Idle => { + } + UserState::Matchmaking => { + // 断线后取消匹配 + self.user_states.insert(user_id, UserState::Idle); + } + UserState::Playing(_room) => { + // TODO 断线重连 + } + } + } + } + } + + pub fn join_room_by_endpoint(&mut self, room_name: &str, endpoint: Endpoint) -> Result { + self.join_room(room_name, self.get_user(&endpoint).expect("User not found").to_string()) + } + + pub fn join_room(&mut self, room_name: &str, user: UserId) -> Result { if !self.rooms.contains_key(room_name) { self.rooms .insert(room_name.to_string(), Room::new(room_name.to_string())); @@ -54,12 +84,136 @@ impl ServerLobby<'_> { if room.users.len() == 3 { room.state = RoomState::Ready; + + match room.start_game() { + Ok((landlord_player, players)) => { + for player in players { + self.network.send_to_user(&player.user, &S2CMessage::GameStarted(player.cards.clone(), landlord_player.user.clone())); + self.user_states.insert(player.user.clone(), UserState::Playing(room_name.to_string())); + } + } + Err(err) => { + return Err(LobbyError::RoomErr(err)); + } + } } - Ok(room) + Ok((MessageTarget::Reply, S2CMessage::RoomJoined(room_name.to_string()))) } } - pub fn get_room(&self, user_id: &str) -> Option<&Room> { + pub fn start_game(&mut self, room: &mut Room) -> Result<(), Error> { + let room_name = room.name.clone(); + match room.start_game() { + Ok((landlord_player, players)) => { + for player in players { + self.network.send_to_user(&player.user, &S2CMessage::GameStarted(player.cards.clone(), landlord_player.user.clone())); + self.user_states.insert(player.user.clone(), UserState::Playing(room_name.clone())); + } + } + Err(err) => { + self.send_to_room(&room.name, &S2CMessage::RoomErr(err.clone())); + return Err(Error::from(err)); + } + } + Ok(()) + } + + pub fn start_game_by_name(&mut self, room_name: &str) -> Result<&mut Room, RoomError> { + let room = self.rooms.get_mut(room_name); + if room.is_none() { + return Err(RoomError::NotReady); + } + let room = room.unwrap(); + match room.start_game() { + Ok((landlord_player, players)) => { + for player in players { + self.network.send_to_user(&player.user, &S2CMessage::GameStarted(player.cards.clone(), landlord_player.user.clone())); + self.user_states.insert(player.user.clone(), UserState::Playing(room_name.to_string())); + } + } + Err(err) => { + return Err(err); + } + } + Ok(room) + } + + pub fn rematch_vote(&mut self, user: &str, vote: bool) -> Result { + if let Some(room) = self.get_room_by_user_mut(user) { + let current_state = room.state.clone(); + if let RoomState::WaitingForRematch(count) = current_state { + return if vote { + if count + 1 == 3 { + room.game.reset(); + + self.start_game(room); + } + room.state = RoomState::WaitingForRematch(count + 1); + Ok((MessageTarget::Reply, S2CMessage::RematchVote(user.to_string(), vote, count + 1))) + } else { + let name = room.name.clone(); + *room = Room::new(name); + Ok((MessageTarget::Reply, S2CMessage::RematchVote(user.to_string(), vote, count))) + } + } + } + Err(RoomError::NotReady) + } + + pub fn send_packet(&self, packet: MessagePacket) -> Option { + match packet.0 { + MessageTarget::All => { + self.network.send_to_all(&packet.1).ok().map(|_| SendStatus::Sent) + } + MessageTarget::Reply => { + None + } + MessageTarget::Endpoint(endpoint) => { + self.network.send_to_endpoint(&packet.1, endpoint) + } + MessageTarget::User(user) => { + self.network.send_to_user(&user, &packet.1) + } + MessageTarget::Room(room) => { + self.send_to_room(&room, &packet.1) + } + } + } + + pub fn choose_landlord_by_endpoint(&mut self, endpoint: &Endpoint, choose: bool) -> Result { + let user = self.get_user(endpoint).expect("No user found by endpoint").clone(); + let network = &self.network; + if let Some(room) = self.get_room_by_user_mut(&user) { + // 尚未开始叫地主阶段 + if room.game.state != GameState::WaitingForLandlord { + return Err(Error::from(RoomError::NotStarted)); + } + // 不是当前的地主玩家 + if room.game.players[room.game.landlord_index].user != user { + return Err(Error::from(RoomError::NotLandlordPlayer)); + } + if choose { // 叫地主 + room.game.run().expect("Game cannot run"); + // 通知所有玩家地主人选 + + Ok((MessageTarget::Room(room.name.clone()), S2CMessage::LordCards(room.game.current_player().user.clone(), room.game.landlord_cards.clone()))) + } else { // 不叫 + room.game.move_landlord_index(); + // 通知下一个地主 + + Ok((MessageTarget::Room(room.name.clone()), S2CMessage::LandlordMove(room.game.landlord_player().user.clone()))) + } + } else { + // 房间还未准备好 + Err(Error::from(RoomError::NotReady)) + } + } + + pub fn get_user(&self, endpoint: &Endpoint) -> Option<&UserId> { + self.network.user_map.get_by_right(endpoint) + } + + pub fn get_room_by_user(&self, user_id: &str) -> Option<&Room> { if let Some(UserState::Playing(room_name)) = self.user_states.get(user_id) { self.rooms.get(room_name) } else { @@ -67,7 +221,7 @@ impl ServerLobby<'_> { } } - pub fn get_room_mut(&mut self, user_id: &str) -> Option<&mut Room> { + pub fn get_room_by_user_mut(&mut self, user_id: &str) -> Option<&mut Room> { if let Some(UserState::Playing(room_name)) = self.user_states.get(user_id) { self.rooms.get_mut(room_name) } else { @@ -75,24 +229,33 @@ impl ServerLobby<'_> { } } - pub fn send_to_room(&self, room_name: &str, message: &S2CMessage) -> Option<()> { - let room = self.get_room(room_name)?; + pub fn get_room_by_endpoint(&self, endpoint: &Endpoint) -> Option<&Room> { + self.get_room_by_user(self.get_user(endpoint)?) + } + + pub fn get_room_by_endpoint_mut(&mut self, endpoint: &Endpoint) -> Option<&mut Room> { + let user = self.get_user(endpoint)?.to_string(); + self.get_room_by_user_mut(&user) + } + + pub fn send_to_room(&self, room_name: &str, message: &S2CMessage) -> Option { + let room = self.get_room_by_user(room_name)?; for user in room.users.iter() { let status = self.network.send_to_user(user, message)?; if status != SendStatus::Sent { return None } } - Some(()) + Some(SendStatus::Sent) } } impl Lobby for ServerLobby<'_> { fn login(&mut self, user: String) { + if !self.user_states.contains_key(&user) { + self.user_states.insert(user.clone(), UserState::Idle); + } self.users.push(user); - // if !self.user_states.contains_key(&user) { - // self.user_states.insert(user, UserState::Idle); - // } } fn disconnect(&mut self, user: &str) { @@ -100,53 +263,4 @@ impl Lobby for ServerLobby<'_> { self.users.remove(pos); } } -} - -pub struct NetworkManager<'a> { - pub user_map: BiMap, - - controller: &'a NetworkController, -} - -impl NetworkManager<'_> { - fn new(controller: &NetworkController) -> NetworkManager { - NetworkManager { - user_map: BiHashMap::new(), - controller - } - } - - fn connect(&mut self, user_id: UserId, endpoint: Endpoint) { - self.user_map.insert(user_id, endpoint); - } - - fn send_to_user(&self, user_id: &str, message: &S2CMessage) -> Option { - let endpoint = self.user_map.get_by_left(user_id)?; - let to_send = bincode::serialize(message).ok()?; - Some(self.controller.send(*endpoint, &to_send)) - } - - /// Send a message to all clients registered - /// - /// # Arguments - /// - /// * `message`: message to send - /// - /// returns: Result amount of clients, error index - /// - fn send_to_all(&self, message: &S2CMessage) -> Result { - let mut i: usize = 0; - if let Ok(to_send) = bincode::serialize(message) { - for endpoint in self.user_map.right_values() { - if self.controller.send(*endpoint, &to_send) == SendStatus::Sent { - i += 1; - } else { - return Err(i); - } - } - Ok(i) - } else { - Err(0) - } - } } \ No newline at end of file diff --git a/cardgame-server/src/server_network.rs b/cardgame-server/src/server_network.rs new file mode 100644 index 0000000..c5904c7 --- /dev/null +++ b/cardgame-server/src/server_network.rs @@ -0,0 +1,72 @@ +use cardgame::S2CMessage; +use message_io::network::{SendStatus, Endpoint, NetworkController}; +use bimap::{BiMap, BiHashMap}; +use cardgame::user::UserId; + +pub type MessagePacket = (MessageTarget, S2CMessage); + +pub enum MessageTarget { + All, + Reply, + Endpoint(Endpoint), + User(UserId), + Room(String) +} + +pub fn send_to_endpoint(controller: &NetworkController, message: &S2CMessage, endpoint: Endpoint) -> Option { + let to_send = bincode::serialize(message).ok()?; + Some(controller.send(endpoint, &to_send)) +} + +pub struct NetworkManager<'a> { + pub user_map: BiMap, + + controller: &'a NetworkController, +} + +impl NetworkManager<'_> { + pub fn new(controller: &NetworkController) -> NetworkManager { + NetworkManager { + user_map: BiHashMap::new(), + controller + } + } + + pub fn connect(&mut self, user_id: UserId, endpoint: Endpoint) { + self.user_map.insert(user_id, endpoint); + } + + pub fn send_to_endpoint(&self, message: &S2CMessage, endpoint: Endpoint) -> Option { + let to_send = bincode::serialize(message).ok()?; + Some(self.controller.send(endpoint, &to_send)) + } + + pub fn send_to_user(&self, user_id: &str, message: &S2CMessage) -> Option { + let endpoint = self.user_map.get_by_left(user_id).expect("No such client found").clone(); + send_to_endpoint(self.controller, message, endpoint) + } + + /// Send a message to all clients registered + /// + /// # Arguments + /// + /// * `message`: message to send + /// + /// returns: Result amount of clients, error index + /// + pub fn send_to_all(&self, message: &S2CMessage) -> Result { + let mut i: usize = 0; + if let Ok(to_send) = bincode::serialize(message) { + for endpoint in self.user_map.right_values() { + if self.controller.send(*endpoint, &to_send) == SendStatus::Sent { + i += 1; + } else { + return Err(i); + } + } + Ok(i) + } else { + Err(0) + } + } +} \ No newline at end of file diff --git a/cardgame/src/lobby.rs b/cardgame/src/lobby.rs index 7dd7c9a..18b8fe5 100644 --- a/cardgame/src/lobby.rs +++ b/cardgame/src/lobby.rs @@ -2,6 +2,8 @@ use crate::user::UserId; use crate::{Game, Player}; use serde::{Serialize, Deserialize}; use std::slice::Iter; +use std::error::Error; +use std::fmt::{Debug, Formatter, Display}; pub struct Room { pub name: String, @@ -43,7 +45,7 @@ impl Room { } } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone)] pub enum RoomError { /// 未准备好(人数不足) NotReady, @@ -55,6 +57,40 @@ pub enum RoomError { NotLandlordPlayer } +impl Debug for RoomError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match *self { + RoomError::NotReady => { + write!(f, "RoomError::NotReady") + } + RoomError::NotStarted => { + write!(f, "RoomError::NotStarted") + } + RoomError::NotLandlordPlayer => { + write!(f, "RoomError::NotLandlordPlayer") + } + } + } +} + +impl Display for RoomError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match *self { + RoomError::NotReady => { + write!(f, "房间未准备好") + } + RoomError::NotStarted => { + write!(f, "游戏尚未开始") + } + RoomError::NotLandlordPlayer => { + write!(f, "不是你叫地主") + } + } + } +} + +impl Error for RoomError {} + /// 游戏大厅,用于加入房间和匹配玩家。 pub trait Lobby { /// 玩家登入 @@ -71,4 +107,7 @@ pub enum LobbyError { /// 房间已满 RoomFull, + + RoomErr(RoomError), + OtherError } \ No newline at end of file