From 58c26301bf432e334ec0dcea53a67ea304232145 Mon Sep 17 00:00:00 2001 From: jake Date: Sun, 18 Sep 2022 21:01:32 -0600 Subject: [PATCH] make ClientLocation Clone-able --- Cargo.toml | 4 + src/lib.rs | 2 + src/ship/items/actions.rs | 2 - src/ship/location.rs | 483 ++++++++++------- src/ship/packet/builder/lobby.rs | 50 +- src/ship/packet/builder/room.rs | 28 +- src/ship/packet/handler/communication.rs | 20 +- src/ship/packet/handler/direct_message.rs | 68 +-- src/ship/packet/handler/lobby.rs | 66 +-- src/ship/packet/handler/message.rs | 56 +- src/ship/packet/handler/quest.rs | 32 +- src/ship/packet/handler/room.rs | 111 ++-- src/ship/packet/handler/trade.rs | 612 +++++++++++----------- src/ship/ship.rs | 66 +-- src/ship/trade.rs | 24 +- tests/test_trade.rs | 37 +- 16 files changed, 891 insertions(+), 770 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fb618d9..0dc9c0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,3 +34,7 @@ strum = "0.19.5" strum_macros = "0.19" anyhow = { version = "1.0.47", features = ["backtrace"] } fix-hidden-lifetime-bug = "0.2.4" + + +[patch."http://git.sharnoth.com/jake/libpso"] +libpso = { path = "../libpso" } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 66c081f..47c688a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,8 @@ #![feature(drain_filter)] #![feature(try_blocks)] #![feature(once_cell)] +#![feature(pin_macro)] + extern crate fix_hidden_lifetime_bug; diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index c2213b5..8d4b2ba 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -575,7 +575,6 @@ where move |(item_state, transaction), arg| { let input = input.clone(); let func = func.clone(); - println!("i {:?} {:?}", input, arg); Box::pin(async move { let (state, result) = iterate_inner((item_state, transaction), input, func, arg.clone()).await?; Ok((state, result)) @@ -622,7 +621,6 @@ where T: Clone + Send + Sync, { move |(item_state, transaction), items| { - println!("fe {:?}", items); let func = func.clone(); Box::pin(async move { let (state, result) = foreach_inner((item_state, transaction), items, func).await?; diff --git a/src/ship/location.rs b/src/ship/location.rs index f4a1433..3e3c388 100644 --- a/src/ship/location.rs +++ b/src/ship/location.rs @@ -4,6 +4,10 @@ use std::time::SystemTime; use thiserror::Error; use crate::common::serverstate::ClientId; +use async_std::sync::{Arc, RwLock}; +use futures::{stream, StreamExt}; +use std::pin::pin; + pub const MAX_ROOMS: usize = 128; pub enum AreaType { @@ -133,33 +137,48 @@ pub enum RoomLobby { Lobby(LobbyId), } +#[derive(Clone, Debug)] pub struct ClientLocation { - lobbies: [Lobby; 15], - rooms: [Option; MAX_ROOMS], - client_location: HashMap, + lobbies: [Arc>; 15], + rooms: [Option>>; MAX_ROOMS], + client_location: Arc>>, } impl Default for ClientLocation { fn default() -> ClientLocation { + //const RNONE: Option>> = None; + //const LINIT: Arc> = Arc::new(RwLock::new(Lobby([None; 12]))); ClientLocation { - lobbies: [Lobby([None; 12]); 15], - rooms: [None; MAX_ROOMS], - client_location: HashMap::new(), + //lobbies: [LINIT; 15], + lobbies: core::array::from_fn(|_| Arc::new(RwLock::new(Lobby([None; 12])))), + rooms: core::array::from_fn(|_| None), + //rooms: [RNONE; MAX_ROOMS], + client_location: Arc::new(RwLock::new(HashMap::new())), } } } impl ClientLocation { - pub fn add_client_to_lobby(&mut self, id: ClientId, lobby: LobbyId) -> Result<(), JoinLobbyError> { - let l = self.lobbies.get_mut(lobby.0).ok_or(JoinLobbyError::LobbyDoesNotExist)?; - if l.0.iter().filter(|c| c.is_none()).count() == 0 { - return Err(JoinLobbyError::LobbyFull); + pub async fn add_client_to_lobby(&self, id: ClientId, lobby_id: LobbyId) -> Result<(), JoinLobbyError> { + { + let lobby = self.lobbies + .get(lobby_id.0) + .ok_or(JoinLobbyError::LobbyDoesNotExist)? + .read() + .await; + if lobby.0.iter().all(|c| c.is_some()) { + return Err(JoinLobbyError::LobbyFull); + } } - self.remove_client_from_area(id); - - let l = self.lobbies.get_mut(lobby.0).ok_or(JoinLobbyError::LobbyDoesNotExist)?; - let (index, empty_slot) = l.0.iter_mut() + self.remove_client_from_area(id).await; + + let mut lobby = self.lobbies + .get(lobby_id.0) + .ok_or(JoinLobbyError::LobbyDoesNotExist)? + .write() + .await; + let (index, empty_slot) = lobby.0.iter_mut() .enumerate() .find(|(_, k)| k.is_none()) .ok_or(JoinLobbyError::LobbyFull)?; @@ -168,40 +187,45 @@ impl ClientLocation { local_client: LocalClientId(index), time_join: SystemTime::now(), }); - self.client_location.insert(id, RoomLobby::Lobby(lobby)); + self.client_location + .write() + .await + .insert(id, RoomLobby::Lobby(lobby_id)); Ok(()) } - pub fn add_client_to_next_available_lobby(&mut self, id: ClientId, lobby: LobbyId) -> Result { - let l = (0..15) - .map(|lobby_index| { + pub async fn add_client_to_next_available_lobby(&self, id: ClientId, lobby: LobbyId) -> Result { + pin!(stream::iter(0..15) + .filter_map(|lobby_index| async move { let new_lobby = LobbyId((lobby.0 + lobby_index) % 15); - (new_lobby, self.add_client_to_lobby(id, new_lobby)) - }) - .find(|(_, lobby_option)| { - lobby_option.is_ok() - }) - .ok_or(JoinLobbyError::LobbyFull)?; - - Ok(l.0) + Some((new_lobby, self.add_client_to_lobby(id, new_lobby).await.ok()?)) + })) + .next() + .await + .map(|l| l.0) + .ok_or(JoinLobbyError::LobbyFull) } - pub fn create_new_room(&mut self, id: ClientId) -> Result { + pub async fn create_new_room(&mut self, id: ClientId) -> Result { let (index, empty_slot) = self.rooms.iter_mut() .enumerate() .find(|(_, r)| r.is_none()) .ok_or(CreateRoomError::NoOpenSlots)?; - *empty_slot = Some(Room([None; 4])); - self.add_client_to_room(id, RoomId(index)).map_err(|_err| CreateRoomError::JoinError)?; + *empty_slot = Some(Arc::new(RwLock::new(Room([None; 4])))); + self.add_client_to_room(id, RoomId(index)) + .await + .map_err(|_err| CreateRoomError::JoinError)?; Ok(RoomId(index)) } - pub fn add_client_to_room(&mut self, id: ClientId, room: RoomId) -> Result<(), JoinRoomError> { - let r = self.rooms.get_mut(room.0) + pub async fn add_client_to_room(&mut self, id: ClientId, room: RoomId) -> Result<(), JoinRoomError> { + let mut r = self.rooms.get(room.0) .ok_or(JoinRoomError::RoomDoesNotExist)? - .as_mut() - .ok_or(JoinRoomError::RoomDoesNotExist)?; + .as_ref() + .ok_or(JoinRoomError::RoomDoesNotExist)? + .write() + .await; let (index, empty_slot) = r.0.iter_mut() .enumerate() .find(|(_, k)| k.is_none()) @@ -211,38 +235,51 @@ impl ClientLocation { local_client: LocalClientId(index), time_join: SystemTime::now(), }); - self.remove_client_from_area(id); - self.client_location.insert(id, RoomLobby::Room(room)); + self.remove_client_from_area(id).await; + self.client_location + .write() + .await + .insert(id, RoomLobby::Room(room)); Ok(()) } - pub fn get_all_clients_by_client(&self, id: ClientId) -> Result, GetNeighborError> { - let area = self.client_location.get(&id).ok_or(GetNeighborError::InvalidClient)?; + pub async fn get_all_clients_by_client(&self, id: ClientId) -> Result, GetNeighborError> { + let area = self.client_location + .read() + .await; + let area = area + .get(&id) + .ok_or(GetNeighborError::InvalidClient)?; match area { RoomLobby::Room(room) => { - Ok(self.get_clients_in_room(*room).map_err(|_| GetNeighborError::InvalidArea)? + Ok(self.get_clients_in_room(*room).await.map_err(|_| GetNeighborError::InvalidArea)? .into_iter() .collect()) }, RoomLobby::Lobby(lobby) => { - Ok(self.get_clients_in_lobby(*lobby).map_err(|_| GetNeighborError::InvalidArea)? + Ok(self.get_clients_in_lobby(*lobby).await.map_err(|_| GetNeighborError::InvalidArea)? .into_iter() .collect()) } } } - pub fn get_client_neighbors(&self, id: ClientId) -> Result, GetNeighborError> { - let area = self.client_location.get(&id).ok_or(GetNeighborError::InvalidClient)?; + pub async fn get_client_neighbors(&self, id: ClientId) -> Result, GetNeighborError> { + let area = self.client_location + .read() + .await; + let area = area + .get(&id) + .ok_or(GetNeighborError::InvalidClient)?; match area { RoomLobby::Room(room) => { - Ok(self.get_clients_in_room(*room).map_err(|_| GetNeighborError::InvalidArea)? + Ok(self.get_clients_in_room(*room).await.map_err(|_| GetNeighborError::InvalidArea)? .into_iter() .filter(|c| c.client != id) .collect()) }, RoomLobby::Lobby(lobby) => { - Ok(self.get_clients_in_lobby(*lobby).map_err(|_| GetNeighborError::InvalidArea)? + Ok(self.get_clients_in_lobby(*lobby).await.map_err(|_| GetNeighborError::InvalidArea)? .into_iter() .filter(|c| c.client != id) .collect()) @@ -250,51 +287,72 @@ impl ClientLocation { } } - pub fn get_room_leader(&self, room: RoomId) -> Result { - let mut r = self.rooms[room.0] + pub async fn get_room_leader(&self, room: RoomId) -> Result { + let r = self.rooms[room.0] .as_ref() .ok_or(GetLeaderError::InvalidArea)? - .0.iter().flatten() + .read() + .await; + let mut r = r + .0 + .iter() + .flatten() .collect::>(); r.sort_by_key(|k| k.time_join); - let c = r.get(0).ok_or(GetLeaderError::NoClientInArea)?; + let c = r.get(0) + .ok_or(GetLeaderError::NoClientInArea)?; Ok(**c) } - pub fn get_lobby_leader(&self, lobby: LobbyId) -> Result { - let mut l = self.lobbies[lobby.0] - .0.iter().flatten() + pub async fn get_lobby_leader(&self, lobby: LobbyId) -> Result { + let l = self.lobbies[lobby.0] + .read() + .await; + let mut l = l + .0 + .iter() + .flatten() .collect::>(); l.sort_by_key(|k| k.time_join); let c = l.get(0).ok_or(GetLeaderError::NoClientInArea)?; Ok(**c) } - pub fn get_area_leader(&self, roomlobby: RoomLobby) -> Result { + pub async fn get_area_leader(&self, roomlobby: RoomLobby) -> Result { match roomlobby { RoomLobby::Room(room) => { - self.get_room_leader(room) + self.get_room_leader(room).await }, RoomLobby::Lobby(lobby) => { - self.get_lobby_leader(lobby) + self.get_lobby_leader(lobby).await } } } - pub fn get_leader_by_client(&self, id: ClientId) -> Result { - let area = self.client_location.get(&id).ok_or(GetLeaderError::InvalidClient)?; + pub async fn get_leader_by_client(&self, id: ClientId) -> Result { + let area = self.client_location + .read() + .await; + let area = area + .get(&id) + .ok_or(GetLeaderError::InvalidClient)?; match area { RoomLobby::Room(room) => { - self.get_room_leader(*room) + self.get_room_leader(*room).await }, RoomLobby::Lobby(lobby) => { - self.get_lobby_leader(*lobby) + self.get_lobby_leader(*lobby).await } } } - pub fn get_clients_in_lobby(&self, lobby: LobbyId) -> Result, GetClientsError> { - Ok(self.lobbies.get(lobby.0).ok_or(GetClientsError::InvalidArea)?.0 + pub async fn get_clients_in_lobby(&self, lobby: LobbyId) -> Result, GetClientsError> { + Ok(self.lobbies + .get(lobby.0) + .ok_or(GetClientsError::InvalidArea)? + .read() + .await + .0 .iter() .filter_map(|client| { client.map(|c| { @@ -303,10 +361,14 @@ impl ClientLocation { }).collect()) } - pub fn get_clients_in_room(&self, room: RoomId) -> Result, GetClientsError> { + pub async fn get_clients_in_room(&self, room: RoomId) -> Result, GetClientsError> { Ok(self.rooms.get(room.0) .ok_or(GetClientsError::InvalidArea)? - .ok_or(GetClientsError::InvalidArea)?.0 + .as_ref() + .ok_or(GetClientsError::InvalidArea)? + .read() + .await + .0 .iter() .filter_map(|client| { client.map(|c| { @@ -315,17 +377,26 @@ impl ClientLocation { }).collect()) } - pub fn get_local_client(&self, id: ClientId) -> Result { - let area = self.client_location.get(&id).ok_or(GetClientsError::InvalidClient)?; + pub async fn get_local_client(&self, id: ClientId) -> Result { + let area = self.client_location + .read() + .await; + let area = area + .get(&id) + .ok_or(GetClientsError::InvalidClient)?; match area { RoomLobby::Room(room) => { - self.get_clients_in_room(*room).map_err(|_| GetClientsError::InvalidArea)? + self.get_clients_in_room(*room) + .await + .map_err(|_| GetClientsError::InvalidArea)? .into_iter() .find(|c| c.client == id) .ok_or(GetClientsError::InvalidClient) }, RoomLobby::Lobby(lobby) => { - self.get_clients_in_lobby(*lobby).map_err(|_| GetClientsError::InvalidArea)? + self.get_clients_in_lobby(*lobby) + .await + .map_err(|_| GetClientsError::InvalidArea)? .into_iter() .find(|c| c.client == id) .ok_or(GetClientsError::InvalidClient) @@ -333,14 +404,17 @@ impl ClientLocation { } } - pub fn get_area(&self, id: ClientId) -> Result { - self.client_location.get(&id) + pub async fn get_area(&self, id: ClientId) -> Result { + self.client_location + .read() + .await + .get(&id) .ok_or(GetAreaError::InvalidClient) .map(Clone::clone) } - pub fn get_room(&self, id: ClientId) -> Result { - if let RoomLobby::Room(room) = self.client_location.get(&id).ok_or(GetAreaError::InvalidClient)? { + pub async fn get_room(&self, id: ClientId) -> Result { + if let RoomLobby::Room(room) = self.client_location.read().await.get(&id).ok_or(GetAreaError::InvalidClient)? { Ok(*room) } else { @@ -348,8 +422,8 @@ impl ClientLocation { } } - pub fn get_lobby(&self, id: ClientId) -> Result { - if let RoomLobby::Lobby(lobby) = self.client_location.get(&id).ok_or(GetAreaError::InvalidClient)? { + pub async fn get_lobby(&self, id: ClientId) -> Result { + if let RoomLobby::Lobby(lobby) = self.client_location.read().await.get(&id).ok_or(GetAreaError::InvalidClient)? { Ok(*lobby) } else { @@ -357,31 +431,41 @@ impl ClientLocation { } } - pub fn remove_client_from_area(&mut self, id: ClientId) -> Result<(), ClientRemovalError> { - let area = self.client_location.get_mut(&id).ok_or(ClientRemovalError::ClientNotInArea)?; - let client_list = match area { - RoomLobby::Room(room) => { - self.rooms[room.0] - .as_mut() - .map(|r| { - r.0.iter_mut() + pub async fn remove_client_from_area(&self, id: ClientId) -> Result<(), ClientRemovalError> { + fn remove_client(id: ClientId, client_list : &mut [Option; N]) { + client_list + .iter_mut() + .filter(|client| { + client.map_or(false, |c| { + c.client == id }) + }) + .for_each(|client| { + *client = None + }); + } + + let area = self.client_location + .read() + .await; + let area = area + .get(&id) + .ok_or(ClientRemovalError::ClientNotInArea)?; + match area { + RoomLobby::Room(room) => { + let r = self.rooms.get(room.0).ok_or(ClientRemovalError::InvalidArea)?; + if let Some(r) = r { + remove_client(id, &mut r.write().await.0) + } + else { + return Err(ClientRemovalError::InvalidArea) + } }, RoomLobby::Lobby(lobby) => { - Some(self.lobbies[lobby.0].0.iter_mut()) + remove_client(id, &mut self.lobbies[lobby.0].write().await.0) } }; - client_list - .ok_or(ClientRemovalError::InvalidArea)? - .filter(|client| { - client.map_or(false, |c| { - c.client == id - }) - }) - .for_each(|client| { - *client = None - }); Ok(()) } } @@ -397,173 +481,174 @@ impl ClientLocation { mod test { use super::*; - #[test] - fn test_add_client_to_lobby() { - let mut cl = ClientLocation::default(); - cl.add_client_to_lobby(ClientId(12), LobbyId(0)); - cl.add_client_to_lobby(ClientId(13), LobbyId(1)); - cl.add_client_to_lobby(ClientId(14), LobbyId(0)); + #[async_std::test] + async fn test_add_client_to_lobby() { + let cl = ClientLocation::default(); + cl.add_client_to_lobby(ClientId(12), LobbyId(0)).await.unwrap(); + cl.add_client_to_lobby(ClientId(13), LobbyId(1)).await.unwrap(); + cl.add_client_to_lobby(ClientId(14), LobbyId(0)).await.unwrap(); - assert!(cl.get_clients_in_lobby(LobbyId(0)).into_iter().flatten().map(|c| (c.client, c.local_client)).collect::>() == vec![ + assert!(cl.get_clients_in_lobby(LobbyId(0)).await.into_iter().flatten().map(|c| (c.client, c.local_client)).collect::>() == vec![ (ClientId(12), LocalClientId(0)), (ClientId(14), LocalClientId(1)), ]); } - #[test] - fn test_add_client_to_full_lobby() { - let mut cl = ClientLocation::default(); - (0..12).for_each(|i| { - cl.add_client_to_lobby(ClientId(i), LobbyId(0)); - }); - assert!(cl.add_client_to_lobby(ClientId(99), LobbyId(0)) == Err(JoinLobbyError::LobbyFull)); + #[async_std::test] + async fn test_add_client_to_full_lobby() { + let cl = ClientLocation::default(); + for i in 0..12 { + cl.add_client_to_lobby(ClientId(i), LobbyId(0)).await.unwrap(); + } + assert!(cl.add_client_to_lobby(ClientId(99), LobbyId(0)).await == Err(JoinLobbyError::LobbyFull)); } - #[test] - fn test_add_client_to_next_available_lobby() { - let mut cl = ClientLocation::default(); - (1..4).for_each(|lobby| { - (0..12).for_each(|i| { - cl.add_client_to_lobby(ClientId(lobby*12+i), LobbyId(lobby)); - }); - }); - assert!(cl.add_client_to_next_available_lobby(ClientId(99), LobbyId(1)) == Ok(LobbyId(4))); + #[async_std::test] + async fn test_add_client_to_next_available_lobby() { + let cl = ClientLocation::default(); + for lobby in 1..4 { + for i in 0..12 { + cl.add_client_to_lobby(ClientId(lobby*12+i), LobbyId(lobby)).await.unwrap(); + } + } + assert!(cl.add_client_to_next_available_lobby(ClientId(99), LobbyId(1)).await == Ok(LobbyId(4))); } - #[test] - fn test_add_to_lobby_when_all_are_full() { - let mut cl = ClientLocation::default(); - (0..15).for_each(|lobby| { - (0..12).for_each(|i| { - cl.add_client_to_lobby(ClientId(lobby*12+i), LobbyId(lobby)); - }); - }); - assert!(cl.add_client_to_next_available_lobby(ClientId(99), LobbyId(1)) == Err(JoinLobbyError::LobbyFull)); + #[async_std::test] + async fn test_add_to_lobby_when_all_are_full() { + let cl = ClientLocation::default(); + for lobby in 0..15 { + for i in 0..12 { + cl.add_client_to_lobby(ClientId(lobby*12+i), LobbyId(lobby)).await.unwrap(); + } + } + assert_eq!(cl.add_client_to_next_available_lobby(ClientId(99), LobbyId(1)).await, Err(JoinLobbyError::LobbyFull)); } - #[test] - fn test_new_room() { + #[async_std::test] + async fn test_new_room() { let mut cl = ClientLocation::default(); - assert!(cl.create_new_room(ClientId(12)) == Ok(RoomId(0))); + assert!(cl.create_new_room(ClientId(12)).await == Ok(RoomId(0))); } - #[test] - fn test_add_client_to_room() { + #[async_std::test] + async fn test_add_client_to_room() { let mut cl = ClientLocation::default(); - let room = cl.create_new_room(ClientId(12)).unwrap(); - assert!(cl.add_client_to_room(ClientId(234), room) == Ok(())); - assert!(cl.get_clients_in_room(room).unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::>() == vec![ + let room = cl.create_new_room(ClientId(12)).await.unwrap(); + assert!(cl.add_client_to_room(ClientId(234), room).await == Ok(())); + assert!(cl.get_clients_in_room(room).await.unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::>() == vec![ (ClientId(12), LocalClientId(0)), (ClientId(234), LocalClientId(1)), ]); } - #[test] - fn test_no_new_room_slots() { + #[async_std::test] + async fn test_no_new_room_slots() { let mut cl = ClientLocation::default(); for i in 0..128 { - cl.create_new_room(ClientId(i)); + cl.create_new_room(ClientId(i)).await; } - assert!(cl.create_new_room(ClientId(234)) == Err(CreateRoomError::NoOpenSlots)); + assert!(cl.create_new_room(ClientId(234)).await == Err(CreateRoomError::NoOpenSlots)); } - #[test] - fn test_joining_full_room() { + #[async_std::test] + async fn test_joining_full_room() { let mut cl = ClientLocation::default(); - let room = cl.create_new_room(ClientId(0)).unwrap(); - assert!(cl.add_client_to_room(ClientId(1), room) == Ok(())); - assert!(cl.add_client_to_room(ClientId(2), room) == Ok(())); - assert!(cl.add_client_to_room(ClientId(3), room) == Ok(())); - assert!(cl.add_client_to_room(ClientId(234), room) == Err(JoinRoomError::RoomFull)); + let room = cl.create_new_room(ClientId(0)).await.unwrap(); + assert!(cl.add_client_to_room(ClientId(1), room).await == Ok(())); + assert!(cl.add_client_to_room(ClientId(2), room).await == Ok(())); + assert!(cl.add_client_to_room(ClientId(3), room).await == Ok(())); + assert!(cl.add_client_to_room(ClientId(234), room).await == Err(JoinRoomError::RoomFull)); } - #[test] - fn test_adding_client_to_room_removes_from_lobby() { + #[async_std::test] + async fn test_adding_client_to_room_removes_from_lobby() { let mut cl = ClientLocation::default(); - cl.add_client_to_lobby(ClientId(93), LobbyId(0)); - cl.add_client_to_lobby(ClientId(23), LobbyId(0)); - cl.add_client_to_lobby(ClientId(51), LobbyId(0)); - cl.add_client_to_lobby(ClientId(12), LobbyId(0)); - - let room = cl.create_new_room(ClientId(51)).unwrap(); - assert!(cl.add_client_to_room(ClientId(93), room) == Ok(())); - assert!(cl.get_clients_in_lobby(LobbyId(0)).unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::>() == vec![ + cl.add_client_to_lobby(ClientId(93), LobbyId(0)).await; + cl.add_client_to_lobby(ClientId(23), LobbyId(0)).await; + cl.add_client_to_lobby(ClientId(51), LobbyId(0)).await; + cl.add_client_to_lobby(ClientId(12), LobbyId(0)).await; + + let room = cl.create_new_room(ClientId(51)).await.unwrap(); + assert!(cl.add_client_to_room(ClientId(93), room).await == Ok(())); + assert!(cl.get_clients_in_lobby(LobbyId(0)).await.unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::>() == vec![ (ClientId(23), LocalClientId(1)), (ClientId(12), LocalClientId(3)), ]); - assert!(cl.get_clients_in_room(room).unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::>() == vec![ + assert!(cl.get_clients_in_room(room).await.unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::>() == vec![ (ClientId(51), LocalClientId(0)), (ClientId(93), LocalClientId(1)), ]); } - #[test] - fn test_getting_neighbors() { - let mut cl = ClientLocation::default(); - cl.add_client_to_lobby(ClientId(93), LobbyId(0)); - cl.add_client_to_lobby(ClientId(23), LobbyId(0)); - cl.add_client_to_lobby(ClientId(51), LobbyId(0)); - cl.add_client_to_lobby(ClientId(12), LobbyId(0)); + #[async_std::test] + async fn test_getting_neighbors() { + let cl = ClientLocation::default(); + cl.add_client_to_lobby(ClientId(93), LobbyId(0)).await.unwrap(); + cl.add_client_to_lobby(ClientId(23), LobbyId(0)).await.unwrap(); + cl.add_client_to_lobby(ClientId(51), LobbyId(0)).await.unwrap(); + cl.add_client_to_lobby(ClientId(12), LobbyId(0)).await.unwrap(); - assert!(cl.get_client_neighbors(ClientId(23)).unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::>() == vec![ + assert!(cl.get_client_neighbors(ClientId(23)).await.unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::>() == vec![ (ClientId(93), LocalClientId(0)), (ClientId(51), LocalClientId(2)), (ClientId(12), LocalClientId(3)), ]); } - #[test] - fn test_failing_to_join_lobby_does_not_remove_from_current_area() { - let mut cl = ClientLocation::default(); - (0..12).for_each(|i| { - cl.add_client_to_lobby(ClientId(i), LobbyId(0)); - }); - cl.add_client_to_lobby(ClientId(99), LobbyId(1)); - cl.add_client_to_lobby(ClientId(99), LobbyId(0)); - assert!(cl.get_clients_in_lobby(LobbyId(0)).unwrap().len() == 12); - assert!(cl.get_clients_in_lobby(LobbyId(1)).unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::>() == vec![ - (ClientId(99), LocalClientId(0)), - ]); + #[async_std::test] + async fn test_failing_to_join_lobby_does_not_remove_from_current_area() { + let cl = ClientLocation::default(); + for i in 0..12 { + cl.add_client_to_lobby(ClientId(i), LobbyId(0)).await.unwrap(); + } + assert!(cl.add_client_to_lobby(ClientId(99), LobbyId(1)).await.is_ok()); + assert!(cl.add_client_to_lobby(ClientId(99), LobbyId(0)).await.is_err()); + assert_eq!(cl.get_clients_in_lobby(LobbyId(0)).await.unwrap().len(), 12); + assert_eq!( + cl.get_clients_in_lobby(LobbyId(1)).await.unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::>(), + vec![(ClientId(99), LocalClientId(0))] + ); } - #[test] - fn test_get_leader() { - let mut cl = ClientLocation::default(); - cl.add_client_to_lobby(ClientId(93), LobbyId(0)); - cl.add_client_to_lobby(ClientId(23), LobbyId(0)); - cl.add_client_to_lobby(ClientId(51), LobbyId(0)); - cl.add_client_to_lobby(ClientId(12), LobbyId(0)); + #[async_std::test] + async fn test_get_leader() { + let cl = ClientLocation::default(); + cl.add_client_to_lobby(ClientId(93), LobbyId(0)).await; + cl.add_client_to_lobby(ClientId(23), LobbyId(0)).await; + cl.add_client_to_lobby(ClientId(51), LobbyId(0)).await; + cl.add_client_to_lobby(ClientId(12), LobbyId(0)).await; - assert!(cl.get_leader_by_client(ClientId(51)).map(|c| (c.client, c.local_client)) == Ok((ClientId(93), LocalClientId(0)))); + assert!(cl.get_leader_by_client(ClientId(51)).await.map(|c| (c.client, c.local_client)) == Ok((ClientId(93), LocalClientId(0)))); } - #[test] - fn test_remove_client_from_room() { + #[async_std::test] + async fn test_remove_client_from_room() { let mut cl = ClientLocation::default(); - let room = cl.create_new_room(ClientId(51)).unwrap(); - cl.add_client_to_room(ClientId(93), room); - cl.add_client_to_room(ClientId(23), room); - cl.remove_client_from_area(ClientId(51)); - cl.add_client_to_room(ClientId(12), room); + let room = cl.create_new_room(ClientId(51)).await.unwrap(); + cl.add_client_to_room(ClientId(93), room).await; + cl.add_client_to_room(ClientId(23), room).await; + cl.remove_client_from_area(ClientId(51)).await; + cl.add_client_to_room(ClientId(12), room).await; - assert!(cl.get_clients_in_room(room).unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::>() == vec![ + assert!(cl.get_clients_in_room(room).await.unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::>() == vec![ (ClientId(12), LocalClientId(0)), (ClientId(93), LocalClientId(1)), (ClientId(23), LocalClientId(2)), ]); } - #[test] - fn test_leader_changes_on_leader_leaving() { + #[async_std::test] + async fn test_leader_changes_on_leader_leaving() { let mut cl = ClientLocation::default(); - let room = cl.create_new_room(ClientId(51)).unwrap(); - cl.add_client_to_room(ClientId(93), room); - cl.add_client_to_room(ClientId(23), room); - cl.remove_client_from_area(ClientId(51)); - cl.add_client_to_room(ClientId(12), room); - cl.remove_client_from_area(ClientId(23)); - cl.add_client_to_room(ClientId(99), room); - - assert!(cl.get_leader_by_client(ClientId(12)).map(|c| (c.client, c.local_client)) == Ok((ClientId(93), LocalClientId(1)))); + let room = cl.create_new_room(ClientId(51)).await.unwrap(); + cl.add_client_to_room(ClientId(93), room).await.unwrap(); + cl.add_client_to_room(ClientId(23), room).await.unwrap(); + cl.remove_client_from_area(ClientId(51)).await.unwrap(); + cl.add_client_to_room(ClientId(12), room).await.unwrap(); + cl.remove_client_from_area(ClientId(23)).await.unwrap(); + cl.add_client_to_room(ClientId(99), room).await.unwrap(); + + assert!(cl.get_leader_by_client(ClientId(12)).await.map(|c| (c.client, c.local_client)) == Ok((ClientId(93), LocalClientId(1)))); } } diff --git a/src/ship/packet/builder/lobby.rs b/src/ship/packet/builder/lobby.rs index 7126aba..a976f02 100644 --- a/src/ship/packet/builder/lobby.rs +++ b/src/ship/packet/builder/lobby.rs @@ -6,13 +6,13 @@ use crate::ship::packet::builder::{player_info}; use crate::ship::items::state::ItemState; -pub fn join_lobby(id: ClientId, - lobby: LobbyId, - client_location: &ClientLocation, - clients: &Clients, - item_state: &ItemState) - -> Result { - let lobby_clients = client_location.get_clients_in_lobby(lobby).map_err(|err| -> ClientLocationError { err.into() })?; +pub async fn join_lobby(id: ClientId, + lobby: LobbyId, + client_location: &ClientLocation, + clients: &Clients, + item_state: &ItemState) + -> Result { + let lobby_clients = client_location.get_clients_in_lobby(lobby).await.map_err(|err| -> ClientLocationError { err.into() })?; let playerinfo = lobby_clients.iter() .map(|area_client| { let client = clients.get(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client)).unwrap(); @@ -20,8 +20,8 @@ pub fn join_lobby(id: ClientId, }); let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id)).unwrap(); - let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?; - let leader = client_location.get_lobby_leader(lobby).map_err(|err| -> ClientLocationError { err.into() })?; + let area_client = client_location.get_local_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?; + let leader = client_location.get_lobby_leader(lobby).await.map_err(|err| -> ClientLocationError { err.into() })?; Ok(JoinLobby { client: area_client.local_client.id(), leader: leader.local_client.id(), @@ -34,15 +34,15 @@ pub fn join_lobby(id: ClientId, }) } -pub fn add_to_lobby(id: ClientId, - lobby: LobbyId, - client_location: &ClientLocation, - clients: &Clients, - item_state: &ItemState) - -> Result { +pub async fn add_to_lobby(id: ClientId, + lobby: LobbyId, + client_location: &ClientLocation, + clients: &Clients, + item_state: &ItemState) + -> Result { let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id)).unwrap(); - let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?; - let leader = client_location.get_lobby_leader(lobby).map_err(|err| -> ClientLocationError { err.into() })?; + let area_client = client_location.get_local_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?; + let leader = client_location.get_lobby_leader(lobby).await.map_err(|err| -> ClientLocationError { err.into() })?; Ok(AddToLobby { flag: 1, client: area_client.local_client.id(), @@ -56,12 +56,16 @@ pub fn add_to_lobby(id: ClientId, }) } -pub fn remove_from_lobby(id: ClientId, - client_location: &ClientLocation) - -> Result { - let prev_area_index = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?.local_client.id(); - let prev_area_leader_index = client_location.get_area_leader(client_location.get_area(id) - .map_err(|err| -> ClientLocationError { err.into() })?) +pub async fn remove_from_lobby(id: ClientId, + client_location: &ClientLocation) + -> Result { + let prev_area_index = client_location.get_local_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?.local_client.id(); + let prev_area_leader_index = client_location + .get_area_leader(client_location + .get_area(id) + .await + .map_err(|err| -> ClientLocationError { err.into() })?) + .await .map_err(|err| -> ClientLocationError { err.into() })?.local_client.id(); Ok(LeaveLobby { client: prev_area_index, diff --git a/src/ship/packet/builder/room.rs b/src/ship/packet/builder/room.rs index 7e68077..6b55f21 100644 --- a/src/ship/packet/builder/room.rs +++ b/src/ship/packet/builder/room.rs @@ -7,26 +7,28 @@ use crate::ship::items::state::ItemState; use crate::ship::packet::builder::{player_header, player_info}; use std::convert::TryInto; -pub fn join_room(id: ClientId, - clients: &Clients, - client_location: &ClientLocation, - room_id: RoomId, - room: &RoomState) - -> Result { - let all_clients = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; - let players = all_clients.iter() +use futures::stream::StreamExt; + +pub async fn join_room(id: ClientId, + clients: &Clients, + client_location: &ClientLocation, + room_id: RoomId, + room: &RoomState) + -> Result { + let all_clients = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?; + let players = futures::stream::iter(all_clients.iter()) .enumerate() - .fold(Ok([PlayerHeader::default(); 4]), |acc, (i, c)| -> Result<_, ShipError> { + .fold::, _, _>(Ok([PlayerHeader::default(); 4]), |acc, (i, c)| async move { let header_client = clients.get(&c.client).ok_or(ShipError::ClientNotFound(id))?; - let header_area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?; + let header_area_client = client_location.get_local_client(id).await.map_err(|err| ShipError::ClientLocationError(err.into()))?; acc.map(|mut a| { a[i] = player_header(0x10000, header_client, &header_area_client); a }) - })?; + }).await?; - let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?; - let leader = client_location.get_room_leader(room_id).map_err(|err| -> ClientLocationError { err.into() })?; + let area_client = client_location.get_local_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?; + let leader = client_location.get_room_leader(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?; Ok(JoinRoom { flag: all_clients.len() as u32, maps: room.maps.map_headers(), diff --git a/src/ship/packet/handler/communication.rs b/src/ship/packet/handler/communication.rs index c8942ec..6a83033 100644 --- a/src/ship/packet/handler/communication.rs +++ b/src/ship/packet/handler/communication.rs @@ -4,24 +4,24 @@ use crate::ship::ship::{SendShipPacket, ShipError, Clients}; use crate::ship::location::{ClientLocation}; use crate::entity::gateway::EntityGateway; -pub fn player_chat(id: ClientId, - msg: &PlayerChat, - client_location: &ClientLocation, - clients: &Clients) -> Result + Send>, ShipError> { +pub async fn player_chat(id: ClientId, + msg: &PlayerChat, + client_location: &ClientLocation, + clients: &Clients) -> Result + Send>, ShipError> { let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; let cmsg = PlayerChat::new(client.user.id.0, msg.message.clone()); - Ok(Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter() + Ok(Box::new(client_location.get_all_clients_by_client(id).await.unwrap().into_iter() .map(move |client| { (client.client, SendShipPacket::PlayerChat(cmsg.clone())) }))) } -pub fn request_infoboard(id: ClientId, - client_location: &ClientLocation, - clients: &Clients) - -> Box + Send> { - let area_clients = client_location.get_client_neighbors(id).unwrap(); +pub async fn request_infoboard(id: ClientId, + client_location: &ClientLocation, + clients: &Clients) + -> Box + Send> { + let area_clients = client_location.get_client_neighbors(id).await.unwrap(); let r = area_clients.iter() .filter_map(|c| { clients.get(&c.client) diff --git a/src/ship/packet/handler/direct_message.rs b/src/ship/packet/handler/direct_message.rs index ffa9693..4397ed5 100644 --- a/src/ship/packet/handler/direct_message.rs +++ b/src/ship/packet/handler/direct_message.rs @@ -34,21 +34,24 @@ pub enum MessageError { MismatchedTekIds(ClientItemId, ClientItemId), } -fn send_to_client(id: ClientId, target: u8, msg: DirectMessage, client_location: &ClientLocation) - -> Box + Send> { - Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter() +async fn send_to_client(id: ClientId, + target: u8, + msg: DirectMessage, + client_location: &ClientLocation) + -> Box + Send> { + Box::new(client_location.get_all_clients_by_client(id).await.unwrap().into_iter() .filter(move |client| client.local_client.id() == target) .map(move |client| { (client.client, SendShipPacket::DirectMessage(msg.clone())) })) } -pub fn guildcard_send(id: ClientId, - guildcard_send: &GuildcardSend, - target: u32, - client_location: &ClientLocation, - clients: &Clients) - -> Box + Send> { +pub async fn guildcard_send(id: ClientId, + guildcard_send: &GuildcardSend, + target: u32, + client_location: &ClientLocation, + clients: &Clients) + -> Box + Send> { let client = clients.get(&id).unwrap(); let msg = DirectMessage{ flag: target, @@ -65,7 +68,7 @@ pub fn guildcard_send(id: ClientId, class: client.character.char_class.into(), }), }; - send_to_client(id, target as u8, msg, client_location) + send_to_client(id, target as u8, msg, client_location).await } pub async fn request_item(id: ClientId, @@ -79,7 +82,7 @@ pub async fn request_item(id: ClientId, where EG: EntityGateway { - let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?; + let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?; let room = rooms.get_mut(room_id.0) .ok_or(ShipError::InvalidRoom(room_id.0 as u32))? .as_mut() @@ -90,7 +93,7 @@ where return Err(ShipError::MonsterAlreadyDroppedItem(id, request_item.enemy_id).into()) } - let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; + let clients_in_area = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?; let client_and_drop = clients_in_area.into_iter() .filter_map(|area_client| { @@ -129,9 +132,9 @@ where EG: EntityGateway { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; - let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?; - let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?; - let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; + let area_client = client_location.get_local_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?; + let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?; + let clients_in_area = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?; let (item, floor_type) = item_state.get_floor_item(&client.character.id, &ClientItemId(pickup_item.item_id))?; let remove_item = builder::message::remove_item_from_floor(area_client, item)?; @@ -173,17 +176,17 @@ where } pub async fn request_box_item(id: ClientId, - box_drop_request: &BoxDropRequest, - mut entity_gateway: EG, - client_location: &ClientLocation, - clients: &mut Clients, - rooms: &mut Rooms, - item_state: &mut ItemState) - -> Result + Send>, anyhow::Error> + box_drop_request: &BoxDropRequest, + mut entity_gateway: EG, + client_location: &ClientLocation, + clients: &mut Clients, + rooms: &mut Rooms, + item_state: &mut ItemState) + -> Result + Send>, anyhow::Error> where -EG: EntityGateway + EG: EntityGateway { - let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?; + let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?; let room = rooms.get_mut(room_id.0) .ok_or(ShipError::InvalidRoom(room_id.0 as u32))? .as_mut() @@ -194,7 +197,7 @@ EG: EntityGateway return Err(ShipError::BoxAlreadyDroppedItem(id, box_drop_request.object_id).into()) } - let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; + let clients_in_area = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?; let client_and_drop = clients_in_area.into_iter() .filter_map(|area_client| { @@ -245,8 +248,8 @@ where EG: EntityGateway { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; - let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?; - let other_clients_in_area = client_location.get_all_clients_by_client(id).map_err(|err| -> ClientLocationError { err.into() })?; + let area_client = client_location.get_local_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?; + let other_clients_in_area = client_location.get_all_clients_by_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?; let bank_action_pkts = match bank_interaction.action { BANK_ACTION_DEPOSIT => { if bank_interaction.item_id == 0xFFFFFFFF { @@ -294,7 +297,7 @@ pub async fn shop_request(id: ClientId, -> Result + Send>, anyhow::Error> { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; - let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?; + let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?; let room = rooms.get(room_id.0) .ok_or(ShipError::InvalidRoom(room_id.0 as u32))? .as_ref() @@ -335,7 +338,7 @@ where EG: EntityGateway { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; - let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?; + let area_client = client_location.get_local_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?; let (item, remove): (&(dyn ShopItem + Send + Sync), bool) = match buy_item.shop_type { SHOP_OPTION_WEAPON => { @@ -371,7 +374,7 @@ where } } - let other_clients_in_area = client_location.get_client_neighbors(id).map_err(|err| -> ClientLocationError { err.into() })?; + let other_clients_in_area = client_location.get_client_neighbors(id).await.map_err(|err| -> ClientLocationError { err.into() })?; Ok(Box::new(other_clients_in_area.into_iter() .map(move |c| { (c.client, SendShipPacket::Message(Message::new(GameMessage::CreateItem(create.clone())))) @@ -400,6 +403,7 @@ where { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; + // TODO: secids have different mod rates let (grind_mod, special_mod, percent_mod) = { let mut rng = rand::thread_rng(); @@ -443,7 +447,7 @@ where EG: EntityGateway { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; - let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?; + let area_client = client_location.get_local_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?; if let Some((item_id, special_mod, percent_mod, grind_mod)) = client.tek { if item_id.0 != tek_accept.item_id { @@ -459,7 +463,7 @@ where let create_item_pkt = builder::message::create_individual_item(area_client, item_id, &weapon)?; - let neighbors = client_location.get_client_neighbors(id).map_err(|err| -> ClientLocationError { err.into() })?; + let neighbors = client_location.get_client_neighbors(id).await.map_err(|err| -> ClientLocationError { err.into() })?; Ok(Box::new(neighbors.into_iter() .map(move |c| { (c.client, SendShipPacket::Message(Message::new(GameMessage::CreateItem(create_item_pkt.clone())))) diff --git a/src/ship/packet/handler/lobby.rs b/src/ship/packet/handler/lobby.rs index 16483a9..3d133ad 100644 --- a/src/ship/packet/handler/lobby.rs +++ b/src/ship/packet/handler/lobby.rs @@ -46,16 +46,16 @@ pub fn block_selected(id: ClientId, ]) } -pub fn send_player_to_lobby(id: ClientId, - _pkt: &CharData, - client_location: &mut ClientLocation, - clients: &Clients, - item_state: &ItemState) - -> Result, anyhow::Error> { - let lobby = client_location.add_client_to_next_available_lobby(id, LobbyId(0)).map_err(|_| ShipError::TooManyClients)?; - let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_state)?; - let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state)?; - let neighbors = client_location.get_client_neighbors(id).unwrap(); +pub async fn send_player_to_lobby(id: ClientId, + _pkt: &CharData, + client_location: &mut ClientLocation, + clients: &Clients, + item_state: &ItemState) + -> Result, anyhow::Error> { + let lobby = client_location.add_client_to_next_available_lobby(id, LobbyId(0)).await.map_err(|_| ShipError::TooManyClients)?; + let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_state).await?; + let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state).await?; + let neighbors = client_location.get_client_neighbors(id).await.unwrap(); Ok(vec![(id, SendShipPacket::JoinLobby(join_lobby))] .into_iter() .chain(neighbors.into_iter() @@ -72,7 +72,7 @@ pub async fn change_lobby(id: ClientId, mut entity_gateway: EG) -> Result, anyhow::Error> { let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; - let prev_area = client_location.get_area(id).map_err(|err| -> ClientLocationError {err.into()})?; + let prev_area = client_location.get_area(id).await.map_err(|err| -> ClientLocationError {err.into()})?; match prev_area { RoomLobby::Lobby(old_lobby) => { if old_lobby.0 == requested_lobby as usize { @@ -80,30 +80,30 @@ pub async fn change_lobby(id: ClientId, } }, RoomLobby::Room(old_room) => { - if client_location.get_client_neighbors(id)?.is_empty() { + if client_location.get_client_neighbors(id).await?.is_empty() { ship_rooms[old_room.0] = None; } item_state.remove_character_from_room(&client.character); }, } - let leave_lobby = packet::builder::lobby::remove_from_lobby(id, client_location)?; - let old_neighbors = client_location.get_client_neighbors(id).unwrap(); + let leave_lobby = packet::builder::lobby::remove_from_lobby(id, client_location).await?; + let old_neighbors = client_location.get_client_neighbors(id).await.unwrap(); let mut lobby = LobbyId(requested_lobby as usize); - if client_location.add_client_to_lobby(id, lobby).is_err() { + if client_location.add_client_to_lobby(id, lobby).await.is_err() { match prev_area { RoomLobby::Lobby(_lobby) => { let dialog = SmallDialog::new(String::from("Lobby is full.")); return Ok(vec![(id, SendShipPacket::SmallDialog(dialog))]) } RoomLobby::Room(_room) => { - lobby = client_location.add_client_to_next_available_lobby(id, lobby).map_err(|_| ShipError::TooManyClients)?; + lobby = client_location.add_client_to_next_available_lobby(id, lobby).await.map_err(|_| ShipError::TooManyClients)?; } } } item_state.load_character(&mut entity_gateway, &client.character).await?; - let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_state)?; - let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state)?; - let neighbors = client_location.get_client_neighbors(id).unwrap(); + let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_state).await?; + let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state).await?; + let neighbors = client_location.get_client_neighbors(id).await.unwrap(); Ok(vec![(id, SendShipPacket::JoinLobby(join_lobby))] .into_iter() .chain(neighbors.into_iter() @@ -113,30 +113,30 @@ pub async fn change_lobby(id: ClientId, .collect()) } -pub fn remove_from_lobby(id: ClientId, - client_location: &mut ClientLocation) - -> Result, anyhow::Error> { - let area_client = client_location.get_local_client(id)?; - let neighbors = client_location.get_client_neighbors(id)?; - let leader = client_location.get_leader_by_client(id)?; +pub async fn remove_from_lobby(id: ClientId, + client_location: &mut ClientLocation) + -> Result, anyhow::Error> { + let area_client = client_location.get_local_client(id).await?; + let neighbors = client_location.get_client_neighbors(id).await?; + let leader = client_location.get_leader_by_client(id).await?; let leave_lobby_pkt = SendShipPacket::LeaveLobby(LeaveLobby::new(area_client.local_client.id(), leader.local_client.id())); - client_location.remove_client_from_area(id)?; + client_location.remove_client_from_area(id).await?; Ok(neighbors.into_iter().map(|n| { (n.client, leave_lobby_pkt.clone()) }).collect()) } -pub fn get_room_tab_info(id: ClientId, - pkt: &MenuDetail, - client_location: &mut ClientLocation, - clients: &Clients, - rooms: &mut Rooms) - -> Result, anyhow::Error> { +pub async fn get_room_tab_info(id: ClientId, + pkt: &MenuDetail, + client_location: &mut ClientLocation, + clients: &Clients, + rooms: &mut Rooms) + -> Result, anyhow::Error> { let room_id = RoomId(pkt.item as usize); if let Some(_room) = rooms.get(pkt.item as usize).ok_or(ShipError::InvalidRoom(pkt.item))? { let mut room_info = String::new(); - let clients_in_room = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; + let clients_in_room = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?; for client in clients_in_room { let cs = clients.get(&client.client).ok_or(ShipError::ClientNotFound(client.client))?; let gc = cs.user.guildcard; diff --git a/src/ship/packet/handler/message.rs b/src/ship/packet/handler/message.rs index b76c46c..c738078 100644 --- a/src/ship/packet/handler/message.rs +++ b/src/ship/packet/handler/message.rs @@ -19,8 +19,8 @@ pub async fn request_exp(id: ClientId, rooms: &mut Rooms) -> Result + Send>, anyhow::Error> { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; - let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?; - let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?; + let area_client = client_location.get_local_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?; + let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?; let room = rooms.get_mut(room_id.0) .ok_or(ShipError::InvalidRoom(room_id.0 as u32))? .as_mut() @@ -36,7 +36,7 @@ pub async fn request_exp(id: ClientId, ((monster_stats.exp as f32) * 0.8) as u32 }; - let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; + let clients_in_area = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?; let gain_exp_pkt = builder::message::character_gained_exp(area_client, exp_gain); let mut exp_pkts: Box + Send> = Box::new(clients_in_area.clone().into_iter() .map(move |c| { @@ -76,14 +76,14 @@ where EG: EntityGateway { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; - let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?; + let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?; let room = rooms.get_mut(room_id.0) .ok_or(ShipError::InvalidRoom(room_id.0 as u32))? .as_mut() .ok_or(ShipError::InvalidRoom(room_id.0 as u32))?; let area = room.map_areas.get_area_map(player_drop_item.map_area)?; drop_item(item_state, &mut entity_gateway, &client.character, &ClientItemId(player_drop_item.item_id), *area, (player_drop_item.x, player_drop_item.y, player_drop_item.z)).await?; - let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; + let clients_in_area = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?; let pdi = player_drop_item.clone(); Ok(Box::new(clients_in_area.into_iter() .map(move |c| { @@ -91,15 +91,15 @@ where }))) } -pub fn drop_coordinates(id: ClientId, - drop_coordinates: &DropCoordinates, - client_location: &ClientLocation, - clients: &mut Clients, - rooms: &Rooms) - -> Result + Send>, anyhow::Error> +pub async fn drop_coordinates(id: ClientId, + drop_coordinates: &DropCoordinates, + client_location: &ClientLocation, + clients: &mut Clients, + rooms: &Rooms) + -> Result + Send>, anyhow::Error> { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; - let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?; + let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?; let room = rooms.get(room_id.0) .ok_or(ShipError::InvalidRoom(room_id.0 as u32))? .as_ref() @@ -126,8 +126,8 @@ where EG: EntityGateway { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; - let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?; - let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?; + let area_client = client_location.get_local_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?; + let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?; if let Some(drop_location) = client.item_drop_location { if drop_location.item_id.0 != no_longer_has_item.item_id { return Err(ShipError::DropInvalidItemId(no_longer_has_item.item_id).into()); @@ -140,7 +140,7 @@ where let no_longer_has_meseta_pkt = builder::message::player_no_longer_has_meseta(area_client, no_longer_has_item.amount as u32); client.item_drop_location = None; - let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; + let clients_in_area = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?; Ok(Box::new(clients_in_area.into_iter() .flat_map(move |c| { std::iter::once((c.client, SendShipPacket::Message(Message::new(GameMessage::DropSplitStack(dropped_meseta_pkt.clone()))))) @@ -163,7 +163,7 @@ where let dropped_item_pkt = builder::message::drop_split_stack(area_client, &dropped_item)?; client.item_drop_location = None; - let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; + let clients_in_area = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?; Ok(Box::new(clients_in_area.into_iter() .map(move |c| { (c.client, SendShipPacket::Message(Message::new(GameMessage::DropSplitStack(dropped_item_pkt.clone())))) @@ -171,7 +171,7 @@ where } } else if let Some(_tek) = client.tek { - let neighbors = client_location.get_client_neighbors(id).map_err(|err| -> ClientLocationError { err.into() })?; + let neighbors = client_location.get_client_neighbors(id).await.map_err(|err| -> ClientLocationError { err.into() })?; let no_longer_has_item = no_longer_has_item.clone(); Ok(Box::new(neighbors.into_iter() .map(move |c| { @@ -183,14 +183,14 @@ where } } -pub fn update_player_position(id: ClientId, - message: &Message, - clients: &mut Clients, - client_location: &ClientLocation, - rooms: &Rooms) - -> Result + Send>, anyhow::Error> { +pub async fn update_player_position(id: ClientId, + message: &Message, + clients: &mut Clients, + client_location: &ClientLocation, + rooms: &Rooms) + -> Result + Send>, anyhow::Error> { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; - if let Ok(room_id) = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() }) { + if let Ok(room_id) = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() }) { let room = rooms.get(room_id.0) .ok_or(ShipError::InvalidRoom(room_id.0 as u32))? .as_ref() @@ -250,7 +250,7 @@ pub fn update_player_position(id: ClientId, } } else {} let m = message.clone(); - Ok(Box::new(client_location.get_client_neighbors(id).unwrap().into_iter() + Ok(Box::new(client_location.get_client_neighbors(id).await.unwrap().into_iter() .map(move |client| { (client.client, SendShipPacket::Message(m.clone())) }))) @@ -272,7 +272,7 @@ where take_meseta(item_state, &mut entity_gateway, &client.character.id, Meseta(charge.meseta)).await?; let charge = charge.clone(); - Ok(Box::new(client_location.get_client_neighbors(id).unwrap().into_iter() + Ok(Box::new(client_location.get_client_neighbors(id).await.unwrap().into_iter() .map(move |client| { (client.client, SendShipPacket::Message(Message::new(GameMessage::ChargeAttack(charge.clone())))) }))) @@ -308,7 +308,7 @@ where take_meseta(item_state, &mut entity_gateway, &client.character.id, Meseta(10)).await?; let pumc = pumc.clone(); - Ok(Box::new(client_location.get_client_neighbors(id).unwrap().into_iter() + Ok(Box::new(client_location.get_client_neighbors(id).await.unwrap().into_iter() .map(move |client| { (client.client, SendShipPacket::Message(Message::new(GameMessage::PlayerUsedMedicalCenter(pumc.clone())))) }))) @@ -329,7 +329,7 @@ where feed_mag(item_state, &mut entity_gateway, &client.character, &ClientItemId(mag_feed.mag_id), &ClientItemId(mag_feed.item_id)).await?; let mag_feed = mag_feed.clone(); - Ok(Box::new(client_location.get_client_neighbors(id).unwrap().into_iter() + Ok(Box::new(client_location.get_client_neighbors(id).await.unwrap().into_iter() .map(move |client| { (client.client, SendShipPacket::Message(Message::new(GameMessage::PlayerFeedMag(mag_feed.clone())))) }))) diff --git a/src/ship/packet/handler/quest.rs b/src/ship/packet/handler/quest.rs index c1c3f9f..7f97b7c 100644 --- a/src/ship/packet/handler/quest.rs +++ b/src/ship/packet/handler/quest.rs @@ -36,8 +36,8 @@ fn parse_filename(filename_bytes: &[u8; 16]) -> Result<(u16, u16, QuestFileType) } -pub fn send_quest_category_list(id: ClientId, rql: &RequestQuestList, client_location: &ClientLocation, rooms: &mut Rooms) -> Result + Send>, ShipError> { - let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?; +pub async fn send_quest_category_list(id: ClientId, rql: &RequestQuestList, client_location: &ClientLocation, rooms: &mut Rooms) -> Result + Send>, ShipError> { + let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?; let room = rooms.get_mut(room_id.0) .ok_or(ShipError::InvalidRoom(room_id.0 as u32))?.as_mut() .ok_or(ShipError::InvalidRoom(room_id.0 as u32))?; @@ -46,8 +46,8 @@ pub fn send_quest_category_list(id: ClientId, rql: &RequestQuestList, client_loc Ok(Box::new(vec![(id, SendShipPacket::QuestCategoryList(qcl))].into_iter())) } -pub fn select_quest_category(id: ClientId, menuselect: &MenuSelect, client_location: &ClientLocation, rooms: &mut Rooms) -> Result + Send>, ShipError> { - let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?; +pub async fn select_quest_category(id: ClientId, menuselect: &MenuSelect, client_location: &ClientLocation, rooms: &mut Rooms) -> Result + Send>, ShipError> { + let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?; let room = rooms.get_mut(room_id.0) .ok_or(ShipError::InvalidRoom(room_id.0 as u32))?.as_mut() .ok_or(ShipError::InvalidRoom(room_id.0 as u32))?; @@ -63,8 +63,8 @@ pub fn select_quest_category(id: ClientId, menuselect: &MenuSelect, client_locat } -pub fn quest_detail(id: ClientId, questdetailrequest: &QuestDetailRequest, client_location: &ClientLocation, rooms: &mut Rooms) -> Result + Send>, ShipError> { - let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?; +pub async fn quest_detail(id: ClientId, questdetailrequest: &QuestDetailRequest, client_location: &ClientLocation, rooms: &mut Rooms) -> Result + Send>, ShipError> { + let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?; let room = rooms.get_mut(room_id.0) .ok_or(ShipError::InvalidRoom(room_id.0 as u32))?.as_mut() .ok_or(ShipError::InvalidRoom(room_id.0 as u32))?; @@ -82,9 +82,9 @@ pub fn quest_detail(id: ClientId, questdetailrequest: &QuestDetailRequest, clien Ok(Box::new(vec![(id, SendShipPacket::QuestDetail(qd))].into_iter())) } -pub fn player_chose_quest(id: ClientId, questmenuselect: &QuestMenuSelect, clients: &mut Clients, client_location: &ClientLocation, rooms: &mut Rooms) +pub async fn player_chose_quest(id: ClientId, questmenuselect: &QuestMenuSelect, clients: &mut Clients, client_location: &ClientLocation, rooms: &mut Rooms) -> Result + Send>, ShipError> { - let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?; + let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?; let room = rooms.get_mut(room_id.0) .ok_or(ShipError::InvalidRoom(room_id.0 as u32))?.as_mut() .ok_or(ShipError::InvalidRoom(room_id.0 as u32))?; @@ -103,7 +103,7 @@ pub fn player_chose_quest(id: ClientId, questmenuselect: &QuestMenuSelect, clien let bin = quest::quest_header(questmenuselect, &quest.bin_blob, "bin"); let dat = quest::quest_header(questmenuselect, &quest.dat_blob, "dat"); - let area_clients = client_location.get_all_clients_by_client(id).map_err(|err| -> ClientLocationError { err.into() })?; + let area_clients = client_location.get_all_clients_by_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?; area_clients.iter().for_each(|c| { if let Some(client) = clients.get_mut(&c.client) { client.done_loading_quest = false; @@ -114,8 +114,8 @@ pub fn player_chose_quest(id: ClientId, questmenuselect: &QuestMenuSelect, clien }))) } -pub fn quest_file_request(id: ClientId, quest_file_request: &QuestFileRequest, client_location: &ClientLocation, rooms: &mut Rooms) -> Result + Send>, ShipError> { - let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?; +pub async fn quest_file_request(id: ClientId, quest_file_request: &QuestFileRequest, client_location: &ClientLocation, rooms: &mut Rooms) -> Result + Send>, ShipError> { + let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?; let room = rooms.get_mut(room_id.0) .ok_or(ShipError::InvalidRoom(room_id.0 as u32))?.as_mut() .ok_or(ShipError::InvalidRoom(room_id.0 as u32))?; @@ -143,8 +143,8 @@ pub fn quest_file_request(id: ClientId, quest_file_request: &QuestFileRequest, c Ok(Box::new(vec![(id, SendShipPacket::QuestChunk(qc))].into_iter())) } -pub fn quest_chunk_ack(id: ClientId, quest_chunk_ack: &QuestChunkAck, client_location: &ClientLocation, rooms: &mut Rooms) -> Result + Send>, ShipError> { - let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?; +pub async fn quest_chunk_ack(id: ClientId, quest_chunk_ack: &QuestChunkAck, client_location: &ClientLocation, rooms: &mut Rooms) -> Result + Send>, ShipError> { + let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?; let room = rooms.get_mut(room_id.0) .ok_or(ShipError::InvalidRoom(room_id.0 as u32))?.as_mut() .ok_or(ShipError::InvalidRoom(room_id.0 as u32))?; @@ -176,11 +176,11 @@ pub fn quest_chunk_ack(id: ClientId, quest_chunk_ack: &QuestChunkAck, client_loc Ok(Box::new(vec![(id, SendShipPacket::QuestChunk(qc))].into_iter())) } -pub fn done_loading_quest(id: ClientId, clients: &mut Clients, client_location: &ClientLocation) - -> Result + Send>, ShipError> { +pub async fn done_loading_quest(id: ClientId, clients: &mut Clients, client_location: &ClientLocation) + -> Result + Send>, ShipError> { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; client.done_loading_quest = true; - let area_clients = client_location.get_all_clients_by_client(id).map_err(|err| -> ClientLocationError { err.into() })?; + let area_clients = client_location.get_all_clients_by_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?; let all_loaded = area_clients.iter().all(|c| { clients.get(&c.client) .map(|client| { diff --git a/src/ship/packet/handler/room.rs b/src/ship/packet/handler/room.rs index a88203b..3a02102 100644 --- a/src/ship/packet/handler/room.rs +++ b/src/ship/packet/handler/room.rs @@ -8,14 +8,15 @@ use crate::ship::packet::builder; use crate::ship::room; use crate::ship::items::state::ItemState; use std::convert::{TryFrom}; - -pub fn create_room(id: ClientId, - create_room: &CreateRoom, - client_location: &mut ClientLocation, - clients: &mut Clients, - item_state: &mut ItemState, - rooms: &mut Rooms) - -> Result + Send>, anyhow::Error> { +use futures::StreamExt; + +pub async fn create_room(id: ClientId, + create_room: &CreateRoom, + client_location: &mut ClientLocation, + clients: &mut Clients, + item_state: &mut ItemState, + rooms: &mut Rooms) + -> Result + Send>, anyhow::Error> { let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; let level = LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp); match room::Difficulty::try_from(create_room.difficulty)? { @@ -37,22 +38,22 @@ pub fn create_room(id: ClientId, room::Difficulty::Normal => {}, }; - let area = client_location.get_area(id).unwrap(); - let area_client = client_location.get_local_client(id).unwrap(); - let lobby_neighbors = client_location.get_client_neighbors(id).unwrap(); - let room_id = client_location.create_new_room(id).unwrap(); + let area = client_location.get_area(id).await.unwrap(); + let area_client = client_location.get_local_client(id).await.unwrap(); + let lobby_neighbors = client_location.get_client_neighbors(id).await.unwrap(); + let room_id = client_location.create_new_room(id).await.unwrap(); let mut room = room::RoomState::from_create_room(create_room, client.character.section_id).unwrap(); room.bursting = true; item_state.add_character_to_room(room_id, &client.character, area_client); - let join_room = builder::room::join_room(id, clients, client_location, room_id, &room)?; + let join_room = builder::room::join_room(id, clients, client_location, room_id, &room).await?; rooms[room_id.0] = Some(room); let mut result: Box + Send> = Box::new( vec![(id, SendShipPacket::JoinRoom(join_room))].into_iter() ); - if let Ok(leader) = client_location.get_area_leader(area) { + if let Ok(leader) = client_location.get_area_leader(area).await { let leave_lobby = SendShipPacket::LeaveLobby(LeaveLobby::new(area_client.local_client.id(), leader.local_client.id())); result = Box::new(result.chain(lobby_neighbors .into_iter() @@ -64,24 +65,24 @@ pub fn create_room(id: ClientId, Ok(result) } -pub fn room_name_request(id: ClientId, - client_location: &ClientLocation, - rooms: &Rooms) - -> Box + Send> { - let area = client_location.get_area(id).unwrap(); +pub async fn room_name_request(id: ClientId, + client_location: &ClientLocation, + rooms: &Rooms) + -> Box + Send> { + let area = client_location.get_area(id).await.unwrap(); match area { RoomLobby::Room(room) => Box::new(vec![(id, SendShipPacket::RoomNameResponse(RoomNameResponse {name: rooms[room.0].as_ref().unwrap().name.clone()}))].into_iter()), RoomLobby::Lobby(_) => panic!() } } -pub fn join_room(id: ClientId, - pkt: &MenuSelect, - client_location: &mut ClientLocation, - clients: &mut Clients, - item_state: &mut ItemState, - rooms: &mut Rooms) - -> Result + Send>, ShipError> { +pub async fn join_room(id: ClientId, + pkt: &MenuSelect, + client_location: &mut ClientLocation, + clients: &mut Clients, + item_state: &mut ItemState, + rooms: &mut Rooms) + -> Result + Send>, ShipError> { let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; let level = LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp); // let room = rooms.get(pkt.item as usize).ok_or(ShipError::InvalidRoom(pkt.item))?.as_ref().unwrap(); // clippy look what you made me do @@ -106,22 +107,22 @@ pub fn join_room(id: ClientId, _ => {}, }; - let original_area = client_location.get_area(id).unwrap(); - let original_neighbors = client_location.get_client_neighbors(id).unwrap(); + let original_area = client_location.get_area(id).await.unwrap(); + let original_neighbors = client_location.get_client_neighbors(id).await.unwrap(); if room.bursting { return Ok(Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("player is bursting\nplease wait".into())))].into_iter())) } let room_id = RoomId(pkt.item as usize); - let original_room_clients = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; - client_location.add_client_to_room(id, room_id).unwrap(); // TODO: show room full error or whatever + let original_room_clients = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?; + client_location.add_client_to_room(id, room_id).await.unwrap(); // TODO: show room full error or whatever let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; - let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?; + let area_client = client_location.get_local_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?; item_state.add_character_to_room(room_id, &client.character, area_client); - let leader = client_location.get_room_leader(room_id).map_err(|err| -> ClientLocationError { err.into() })?; - let join_room = builder::room::join_room(id, clients, client_location, room_id, room)?; + let leader = client_location.get_room_leader(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?; + let join_room = builder::room::join_room(id, clients, client_location, room_id, room).await?; let add_to = builder::room::add_to_room(id, client, &area_client, &leader, item_state, room_id)?; let room = rooms.get_mut(room_id.0).unwrap().as_mut().unwrap(); @@ -134,7 +135,7 @@ pub fn join_room(id: ClientId, .map(move |c| (c.client, SendShipPacket::AddToRoom(add_to.clone()))) )); - if let Ok(leader) = client_location.get_area_leader(original_area) { + if let Ok(leader) = client_location.get_area_leader(original_area).await { let leave_lobby = SendShipPacket::LeaveLobby(LeaveLobby::new(area_client.local_client.id(), leader.local_client.id())); result = Box::new(result.chain(original_neighbors.into_iter() .map(move |c| (c.client, leave_lobby.clone())))) @@ -146,11 +147,11 @@ pub fn join_room(id: ClientId, } } -pub fn done_bursting(id: ClientId, - client_location: &ClientLocation, - rooms: &mut Rooms) - -> Box + Send> { - let area = client_location.get_area(id).unwrap(); +pub async fn done_bursting(id: ClientId, + client_location: &ClientLocation, + rooms: &mut Rooms) + -> Box + Send> { + let area = client_location.get_area(id).await.unwrap(); let mut rare_monster_list: Option> = None; if let RoomLobby::Room(room_id) = area { if let Some(room) = rooms.get_mut(room_id.0).unwrap().as_mut() { @@ -158,9 +159,9 @@ pub fn done_bursting(id: ClientId, rare_monster_list = Some(room.maps.get_rare_monster_list()); }; } - let area_client = client_location.get_local_client(id).unwrap(); // TODO: unwrap + let area_client = client_location.get_local_client(id).await.unwrap(); // TODO: unwrap let mut result: Box + Send> = Box::new( - client_location.get_client_neighbors(id).unwrap().into_iter() // TODO: unwrap + client_location.get_client_neighbors(id).await.unwrap().into_iter() // TODO: unwrap .flat_map(move |client| { vec![ (client.client, SendShipPacket::Message(Message::new(GameMessage::BurstDone(BurstDone { @@ -180,19 +181,19 @@ pub fn done_bursting(id: ClientId, result } -pub fn request_room_list(id: ClientId, - client_location: &ClientLocation, - rooms: &Rooms) - -> Box + Send> { - let active_room_list = rooms.iter() +pub async fn request_room_list(id: ClientId, + client_location: &ClientLocation, + rooms: &Rooms) + -> Box + Send> { + let active_room_list = futures::stream::iter(rooms.iter()) .enumerate() - .filter_map(|(i, r)| { - r.as_ref().map(|room| { + .filter_map(|(i, r)| async move { + r.as_ref().map(|room| async move { RoomList { menu_id: ROOM_MENU_ID, item_id: i as u32, difficulty: room.get_difficulty_for_room_list(), - players: client_location.get_clients_in_room(RoomId(i)).unwrap().len() as u8, + players: client_location.get_clients_in_room(RoomId(i)).await.unwrap().len() as u8, name: libpso::utf8_to_utf16_array!(room.name, 16), episode: room.get_episode_for_room_list(), flags: room.get_flags_for_room_list(), @@ -211,17 +212,17 @@ pub fn request_room_list(id: ClientId, Box::new(vec![(id, SendShipPacket::RoomListResponse(RoomListResponse { baseroom, - rooms: active_room_list.collect() + rooms: futures::future::join_all(active_room_list.collect::>().await).await }))].into_iter()) } -pub fn cool_62(id: ClientId, - cool_62: &Like62ButCooler, - client_location: &ClientLocation) - -> Box + Send> { +pub async fn cool_62(id: ClientId, + cool_62: &Like62ButCooler, + client_location: &ClientLocation) + -> Box + Send> { let target = cool_62.flag as u8; let cool_62 = cool_62.clone(); - Box::new(client_location.get_client_neighbors(id).unwrap().into_iter() + Box::new(client_location.get_client_neighbors(id).await.unwrap().into_iter() .filter(move |client| client.local_client.id() == target) .map(move |client| { (client.client, SendShipPacket::Like62ButCooler(cool_62.clone())) diff --git a/src/ship/packet/handler/trade.rs b/src/ship/packet/handler/trade.rs index 465b76d..8710d72 100644 --- a/src/ship/packet/handler/trade.rs +++ b/src/ship/packet/handler/trade.rs @@ -13,6 +13,7 @@ use crate::ship::packet::builder; use crate::ship::items::tasks::trade_items; use crate::ship::location::{AreaClient, RoomId}; use crate::entity::item::Meseta; +use crate::ship::trade::ClientTradeState; pub const MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFF01); pub const OTHER_MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFFFF); @@ -48,15 +49,47 @@ pub enum TradeError { } + +pub async fn do_trade_action(id: ClientId, + pkt: TradeRequest, + client_location: &ClientLocation, + target: u32, + this: &mut ClientTradeState, + other: &mut ClientTradeState, + action: F) -> Result + Send>, ShipError> +where + F: Fn(&mut ClientTradeState, &mut ClientTradeState) -> Result<(), ShipError>, +{ + Ok(match action(this, other) { + Ok(_) => { + Box::new(client_location.get_all_clients_by_client(id).await?.into_iter() + .filter(move |client| client.local_client.id() == target as u8) + .map(move |client| { + (client.client, SendShipPacket::DirectMessage(DirectMessage::new(target, GameMessage::TradeRequest(pkt.clone())))) + })) + }, + Err(_) => { + // TODO: some sort of error logging? + Box::new(client_location.get_all_clients_by_client(id).await?.into_iter() + .filter(move |client| client.local_client.id() == target as u8) + .map(move |client| { + (client.client, SendShipPacket::CancelTrade(CancelTrade {})) + }) + .chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {}))))) + } + }) +} + + // TODO: remove target pub async fn trade_request(id: ClientId, - trade_request: &TradeRequest, - target: u32, - client_location: &ClientLocation, - clients: &mut Clients, - item_state: &mut ItemState, - trades: &mut TradeState) - -> Result + Send>, ShipError> + trade_request: &TradeRequest, + target: u32, + client_location: &ClientLocation, + clients: &mut Clients, + item_state: &mut ItemState, + trades: &mut TradeState) + -> Result + Send>, ShipError> { let trade_request = trade_request.clone(); // TODO: make this function take ownership of packet match trade_request.trade { @@ -66,7 +99,7 @@ pub async fn trade_request(id: ClientId, if trades.in_trade(&id) { return Err(TradeError::ClientAlreadyInTrade.into()) } - let trade_partner = client_location.get_client_neighbors(id)? + let trade_partner = client_location.get_client_neighbors(id).await? .into_iter() .find(|ac| { ac.local_client.id() == target as u8 //trade_request.client @@ -76,204 +109,155 @@ pub async fn trade_request(id: ClientId, return Err(TradeError::OtherAlreadyInTrade.into()) } trades.new_trade(&id, &trade_partner.client); - Ok(Box::new(client_location.get_all_clients_by_client(id)?.into_iter() + Ok(Box::new(client_location.get_all_clients_by_client(id).await?.into_iter() .filter(move |client| client.local_client.id() == target as u8) .map(move |client| { (client.client, SendShipPacket::DirectMessage(DirectMessage::new(target, GameMessage::TradeRequest(trade_request.clone())))) }))) }, TradeRequestInitializeCommand::Respond => { - Ok(trades - .with(&id, |this, other| -> Option + Send>> { - if this.status == TradeStatus::ReceivedRequest && other.status == TradeStatus::SentRequest { - this.status = TradeStatus::Trading; - other.status = TradeStatus::Trading; - - let trade_request = trade_request.clone(); - Some(Box::new(client_location.get_all_clients_by_client(id).ok()?.into_iter() - .filter(move |client| client.local_client.id() == target as u8) - .map(move |client| { - (client.client, SendShipPacket::DirectMessage(DirectMessage::new(target, GameMessage::TradeRequest(trade_request.clone())))) - }))) - } - else { - None - } - })? - .unwrap_or_else(|| -> Box + Send> { - trades.remove_trade(&id); - Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter() - .filter(move |client| client.local_client.id() == target as u8) - .map(move |client| { - (client.client, SendShipPacket::CancelTrade(CancelTrade {})) - }) - .chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {}))))) - })) + trades + .with(&id, |mut this, mut other| { + let trade_request = trade_request.clone(); + async move { + do_trade_action(id, trade_request, client_location, target, &mut this, &mut other, |this, other| { + if this.status == TradeStatus::ReceivedRequest && other.status == TradeStatus::SentRequest { + this.status = TradeStatus::Trading; + other.status = TradeStatus::Trading; + Ok(()) + } + else { + Err(TradeError::MismatchedStatus.into()) + } + }).await + }}).await? } } }, TradeRequestCommand::AddItem(item_id, amount) => { - Ok(trades - .with(&id, |this, other| -> Result + Send>, anyhow::Error> { - if this.status == TradeStatus::Trading && other.status == TradeStatus::Trading { - let client = clients.get(&this.client()).ok_or_else(|| ShipError::ClientNotFound(this.client()))?; - let inventory = item_state.get_character_inventory(&client.character)?; - if ClientItemId(item_id) == MESETA_ITEM_ID { - this.meseta += amount as usize; - } - else { - let item = inventory.get_by_client_id(&ClientItemId(item_id)).ok_or(ItemStateError::InvalidItemId(ClientItemId(item_id)))?; - - match &item.item { - InventoryItemDetail::Individual(_) => { - this.items.push(TradeItem::Individual(ClientItemId(item_id))); - }, - InventoryItemDetail::Stacked(stacked_item) => { - if stacked_item.count() < amount as usize { - return Err(TradeError::InvalidStackAmount(ClientItemId(item_id), amount as usize).into()); + trades + .with(&id, |mut this, mut other| { + let trade_request = trade_request.clone(); + async move { + do_trade_action(id, trade_request, client_location, target, &mut this, &mut other, |this, other| { + if this.status == TradeStatus::Trading && other.status == TradeStatus::Trading { + let client = clients.get(&this.client()).ok_or_else(|| ShipError::ClientNotFound(this.client()))?; + let inventory = item_state.get_character_inventory(&client.character)?; + if ClientItemId(item_id) == MESETA_ITEM_ID { + this.meseta += amount as usize; + } + else { + let item = inventory.get_by_client_id(&ClientItemId(item_id)).ok_or(ItemStateError::InvalidItemId(ClientItemId(item_id)))?; + + match &item.item { + InventoryItemDetail::Individual(_) => { + this.items.push(TradeItem::Individual(ClientItemId(item_id))); + }, + InventoryItemDetail::Stacked(stacked_item) => { + if stacked_item.count() < amount as usize { + return Err(TradeError::InvalidStackAmount(ClientItemId(item_id), amount as usize).into()); + } + this.items.push(TradeItem::Stacked(ClientItemId(item_id), amount as usize)); + }, } - this.items.push(TradeItem::Stacked(ClientItemId(item_id), amount as usize)); - }, + } + Ok(()) } - } - - let trade_request = trade_request.clone(); - Ok(Box::new(client_location.get_all_clients_by_client(id)?.into_iter() - .filter(move |client| client.local_client.id() == target as u8) - .map(move |client| { - (client.client, SendShipPacket::DirectMessage(DirectMessage::new(target, GameMessage::TradeRequest(trade_request.clone())))) - }))) - } - else { - Err(TradeError::MismatchedStatus.into()) - } - })? - .unwrap_or_else(|_err| { - trades.remove_trade(&id); - Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter() - .filter(move |client| client.local_client.id() == target as u8) - .map(move |client| { - (client.client, SendShipPacket::CancelTrade(CancelTrade {})) - }) - .chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {}))))) - })) + else { + Err(TradeError::MismatchedStatus.into()) + } + }).await + }}).await? }, TradeRequestCommand::RemoveItem(item_id, amount) => { - Ok(trades - .with(&id, |this, other| -> Option + Send>> { - if this.status == TradeStatus::Trading && other.status == TradeStatus::Trading { - let client = clients.get(&this.client())?; //.ok_or(ShipError::ClientNotFound(id)).ok()?; - let inventory = item_state.get_character_inventory(&client.character).ok()?; - if ClientItemId(item_id) == MESETA_ITEM_ID { - this.meseta -= amount as usize; - } - else { - let item = inventory.get_by_client_id(&ClientItemId(item_id))?; - - match &item.item { - InventoryItemDetail::Individual(_) => { - this.items.retain(|item| { - item.item_id() != ClientItemId(item_id) - }) - }, - InventoryItemDetail::Stacked(_stacked_item) => { - let trade_item_index = this.items.iter() - .position(|item| { - item.item_id() == ClientItemId(item_id) - })?; + trades + .with(&id, |mut this, mut other| { + let trade_request = trade_request.clone(); + async move { + do_trade_action(id, trade_request, client_location, target, &mut this, &mut other, |this, other| { + if this.status == TradeStatus::Trading && other.status == TradeStatus::Trading { + let client = clients.get(&this.client()).ok_or_else(|| ShipError::ClientNotFound(this.client()))?; + let inventory = item_state.get_character_inventory(&client.character)?; + if ClientItemId(item_id) == MESETA_ITEM_ID { + this.meseta -= amount as usize; + } + else { + let item = inventory.get_by_client_id(&ClientItemId(item_id)).ok_or(ItemStateError::InvalidItemId(ClientItemId(item_id)))?; - match this.items[trade_item_index].stacked()?.1.cmp(&(amount as usize)) { - std::cmp::Ordering::Greater => { - *this.items[trade_item_index].stacked_mut()?.1 -= amount as usize; + match &item.item { + InventoryItemDetail::Individual(_) => { + this.items.retain(|item| { + item.item_id() != ClientItemId(item_id) + }) }, - std::cmp::Ordering::Equal => { - this.items.remove(trade_item_index); + InventoryItemDetail::Stacked(_stacked_item) => { + let trade_item_index = this.items.iter() + .position(|item| { + item.item_id() == ClientItemId(item_id) + }) + .ok_or(TradeError::InvalidItemId(ClientItemId(item_id)))?; + + match this.items[trade_item_index].stacked().ok_or(ItemStateError::InvalidItemId(ClientItemId(item_id)))?.1.cmp(&(amount as usize)) { + std::cmp::Ordering::Greater => { + *this.items[trade_item_index].stacked_mut().ok_or(ItemStateError::InvalidItemId(ClientItemId(item_id)))?.1 -= amount as usize; + }, + std::cmp::Ordering::Equal => { + this.items.remove(trade_item_index); + }, + std::cmp::Ordering::Less => { + return Err(TradeError::SketchyTrade.into()) + } + } }, - std::cmp::Ordering::Less => { - return None - } } - }, + } + Ok(()) } - } - let trade_request = trade_request.clone(); - Some(Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter() - .filter(move |client| client.local_client.id() == target as u8) - .map(move |client| { - (client.client, SendShipPacket::DirectMessage(DirectMessage::new(target, GameMessage::TradeRequest(trade_request.clone())))) - }))) - } - else { - None + else { + Err(TradeError::MismatchedStatus.into()) + } + + }).await } - })? - .unwrap_or_else(|| { - trades.remove_trade(&id); - Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter() - .filter(move |client| client.local_client.id() == target as u8) - .map(move |client| { - (client.client, SendShipPacket::CancelTrade(CancelTrade {})) - }) - .chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {}))))) - })) + }).await? }, TradeRequestCommand::Confirm => { - Ok(trades - .with(&id, |this, other| -> Option + Send>> { - if status_is(&this.status, &[TradeStatus::Trading]) && status_is(&other.status, &[TradeStatus::Trading, TradeStatus::Confirmed]) { - this.status = TradeStatus::Confirmed; - - let trade_request = trade_request.clone(); - Some(Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter() - .filter(move |client| client.local_client.id() == target as u8) - .map(move |client| { - (client.client, SendShipPacket::DirectMessage(DirectMessage::new(target, GameMessage::TradeRequest(trade_request.clone())))) - }))) - } - else { - None + trades + .with(&id, |mut this, mut other| { + let trade_request = trade_request.clone(); + async move { + do_trade_action(id, trade_request, client_location, target, &mut this, &mut other, |this, other| { + if status_is(&this.status, &[TradeStatus::Trading]) && status_is(&other.status, &[TradeStatus::Trading, TradeStatus::Confirmed]) { + this.status = TradeStatus::Confirmed; + Ok(()) + } + else { + Err(TradeError::MismatchedStatus.into()) + } + }).await } - })? - .unwrap_or_else(|| { - trades.remove_trade(&id); - Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter() - .filter(move |client| client.local_client.id() == target as u8) - .map(move |client| { - (client.client, SendShipPacket::CancelTrade(CancelTrade {})) - }) - .chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {}))))) - })) + }).await? }, TradeRequestCommand::FinalConfirm => { - Ok(trades - .with(&id, |this, other| -> Option + Send>> { - if this.status == TradeStatus::Confirmed && (other.status == TradeStatus::Confirmed || other.status == TradeStatus::FinalConfirm) { - this.status = TradeStatus::FinalConfirm; - - let trade_request = trade_request.clone(); - Some(Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter() - .filter(move |client| client.local_client.id() == target as u8) - .map(move |client| { - (client.client, SendShipPacket::DirectMessage(DirectMessage::new(target, GameMessage::TradeRequest(trade_request.clone())))) - }))) - } - else { - None + trades + .with(&id, |mut this, mut other| { + let trade_request = trade_request.clone(); + async move { + do_trade_action(id, trade_request, client_location, target, &mut this, &mut other, |this, other| { + if this.status == TradeStatus::Confirmed && (other.status == TradeStatus::Confirmed || other.status == TradeStatus::FinalConfirm) { + this.status = TradeStatus::FinalConfirm; + Ok(()) + } + else { + Err(TradeError::MismatchedStatus.into()) + } + }).await } - })? - .unwrap_or_else(|| { - trades.remove_trade(&id); - Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter() - .filter(move |client| client.local_client.id() == target as u8) - .map(move |client| { - (client.client, SendShipPacket::CancelTrade(CancelTrade {})) - }) - .chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {}))))) - })) + }).await? }, TradeRequestCommand::Cancel => { trades.remove_trade(&id); - Ok(Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter() + Ok(Box::new(client_location.get_all_clients_by_client(id).await?.into_iter() .filter(move |client| client.local_client.id() == target as u8) .map(move |client| { (client.client, SendShipPacket::CancelTrade(CancelTrade {})) @@ -298,10 +282,10 @@ pub async fn inner_items_to_trade(id: ClientId, clients: &mut Clients, item_state: &mut ItemState, trades: &mut TradeState) - -> Result + Send>, anyhow::Error> + -> Result + Send>, ShipError> { - Ok(trades - .with(&id, |this, other| -> Result + Send>, anyhow::Error> { + let pkts: Result + Send>, ShipError> = trades + .with(&id, |mut this, other| async move { if status_is_not(&this.status, &[TradeStatus::FinalConfirm]) || status_is_not(&other.status, &[TradeStatus::FinalConfirm, TradeStatus::ItemsChecked]) { return Err(TradeError::MismatchedStatus.into()) } @@ -379,29 +363,32 @@ pub async fn inner_items_to_trade(id: ClientId, } } }) - .collect::, anyhow::Error>>()?; + .collect::, ShipError>>()?; this.status = TradeStatus::ItemsChecked; if this.status == TradeStatus::ItemsChecked && other.status == TradeStatus::ItemsChecked { Ok(Box::new(vec![ (this.client(), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})), (other.client(), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})), - ].into_iter())) + ].into_iter()) as Box + Send>) } else { - Ok(Box::new(None.into_iter())) + Ok(Box::new(Vec::new().into_iter()) as Box + Send>) } - })? - .unwrap_or_else(|err| { - log::warn!("trade error: {:?}", err); - let (_this, other) = trades.remove_trade(&id); - Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter() - .filter(move |client| other.as_ref().map(|other| client.client == other.client() ).unwrap_or_else(|| false)) - .map(move |client| { - (client.client, SendShipPacket::CancelTrade(CancelTrade {})) - }) - .chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {}))))) - })) + }).await?; + match pkts { + Ok(pkts) => Ok(pkts), + Err(err) => { + log::warn!("trade error: {:?}", err); + let (_this, other) = trades.remove_trade(&id); + Ok(Box::new(client_location.get_all_clients_by_client(id).await?.into_iter() + .filter(move |client| other.as_ref().map(|other| client.client == other.client() ).unwrap_or_else(|| false)) + .map(move |client| { + (client.client, SendShipPacket::CancelTrade(CancelTrade {})) + }) + .chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {})))))) + } + } } pub async fn items_to_trade(id: ClientId, @@ -418,7 +405,7 @@ pub async fn items_to_trade(id: ClientId, Err(err) => { log::warn!("atrade error: {:?}", err); let (_this, other) = trades.remove_trade(&id); - Ok(Box::new(client_location.get_all_clients_by_client(id)?.into_iter() + Ok(Box::new(client_location.get_all_clients_by_client(id).await?.into_iter() .filter(move |client| other.as_ref().map(|other| client.client == other.client()).unwrap_or_else(|| false)) .map(move |client| { (client.client, SendShipPacket::CancelTrade(CancelTrade {})) @@ -428,13 +415,13 @@ pub async fn items_to_trade(id: ClientId, } } -pub async fn trade_confirmed(id: ClientId, - mut entity_gateway: EG, - client_location: &ClientLocation, - clients: &mut Clients, - item_state: &mut ItemState, - trades: &mut TradeState) - -> Result + Send>, anyhow::Error> +pub async fn trade_confirmed_inner(id: ClientId, + mut entity_gateway: EG, + client_location: &ClientLocation, + clients: &mut Clients, + item_state: &mut ItemState, + trades: &mut TradeState) + -> Result + Send>, ShipError> where EG: EntityGateway { @@ -445,123 +432,136 @@ where (AreaClient, &'a crate::ship::ship::ClientState, crate::ship::trade::ClientTradeState)), } - let trade_instructions = trades - .with(&id, |this, other| -> Result<_, anyhow::Error> { - if status_is_not(&this.status, &[TradeStatus::ItemsChecked]) || status_is_not(&other.status, &[TradeStatus::ItemsChecked, TradeStatus::TradeComplete]) { - return Err(TradeError::MismatchedStatus.into()) + let trade = trades + .with(&id, |mut this, other| { + async move { + if status_is_not(&this.status, &[TradeStatus::ItemsChecked]) || status_is_not(&other.status, &[TradeStatus::ItemsChecked, TradeStatus::TradeComplete]) { + return Err(ShipError::TradeError(TradeError::MismatchedStatus)) + } + this.status = TradeStatus::TradeComplete; + + if this.status == TradeStatus::TradeComplete && other.status == TradeStatus::TradeComplete { + let this_client = clients.get(&this.client()).ok_or_else(|| ShipError::ClientNotFound(this.client()))?; + let other_client = clients.get(&other.client()).ok_or_else(|| ShipError::ClientNotFound(other.client()))?; + let this_local_client = client_location.get_local_client(this.client()).await?; + let other_local_client = client_location.get_local_client(other.client()).await?; + let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?; + + Ok(TradeReady::BothPlayers(room_id, + (this_local_client, this_client, this.clone()), + (other_local_client, other_client, other.clone()))) + } + else { + Ok(TradeReady::OnePlayer) + } } - this.status = TradeStatus::TradeComplete; + }).await??; - if this.status == TradeStatus::TradeComplete && other.status == TradeStatus::TradeComplete { - let this_client = clients.get(&this.client()).ok_or_else(|| ShipError::ClientNotFound(this.client()))?; - let other_client = clients.get(&other.client()).ok_or_else(|| ShipError::ClientNotFound(other.client()))?; - let this_local_client = client_location.get_local_client(this.client())?; - let other_local_client = client_location.get_local_client(other.client())?; - let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?; + match trade { + TradeReady::OnePlayer => { + Ok(Box::new(None.into_iter()) as Box + Send>) + }, + TradeReady::BothPlayers(_room_id, (this_local_client, this_client, this), (other_local_client, other_client, other)) => { + let remove_item_packets = this.items + .clone() + .into_iter() + .map(move |item| { + (this_local_client, item) + }) + .chain(other.items + .clone() + .into_iter() + .map(move |item| { + (other_local_client, item) + })) + .map(|(client, item)| { + GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(client, item.item_id(), item.amount() as u32)) + }); - Ok(TradeReady::BothPlayers(room_id, - (this_local_client, this_client, this.clone()), - (other_local_client, other_client, other.clone()))) - } - else { - Ok(TradeReady::OnePlayer) - } - }); + let (this_new_items, other_new_items) = trade_items(item_state, + &mut entity_gateway, + (&this_local_client, &this_client.character, &this.items, Meseta(this.meseta as u32)), + (&other_local_client, &other_client.character, &other.items, Meseta(other.meseta as u32))).await?; - // TODO: this match needs to handle errors better - match trade_instructions { - Ok(Ok(trade)) => { - match trade { - TradeReady::OnePlayer => { - Ok(Box::new(None.into_iter()) as Box + Send>) - }, - TradeReady::BothPlayers(_room_id, (this_local_client, this_client, this), (other_local_client, other_client, other)) => { - let remove_item_packets = this.items - .clone() - .into_iter() - .map(move |item| { - (this_local_client, item) - }) - .chain(other.items - .clone() - .into_iter() - .map(move |item| { - (other_local_client, item) - })) - .map(|(client, item)| { - GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(client, item.item_id(), item.amount() as u32)) - }); + let create_item_packets = this_new_items + .into_iter() + .map(move |item| { + (this_local_client, item) + }) + .chain(other_new_items + .into_iter() + .map(move |item| { + (other_local_client, item) + })) + .map(|(client, item)| { + match item.item { + InventoryItemDetail::Individual(individual_item) => { + GameMessage::CreateItem(builder::message::create_individual_item(client, item.item_id, &individual_item).unwrap()) + }, + InventoryItemDetail::Stacked(stacked_item) => { + GameMessage::CreateItem(builder::message::create_stacked_item(client, item.item_id, &stacked_item.tool, stacked_item.count()).unwrap()) + } + } + }); - let (this_new_items, other_new_items) = trade_items(item_state, - &mut entity_gateway, - (&this_local_client, &this_client.character, &this.items, Meseta(this.meseta as u32)), - (&other_local_client, &other_client.character, &other.items, Meseta(other.meseta as u32))).await?; + let meseta_packets = vec![(this_local_client, other_local_client, this.meseta), (other_local_client, this_local_client, other.meseta)] + .into_iter() + .filter(|(_, _, meseta)| *meseta != 0) + .flat_map(|(this, other, meseta)| { + [ + GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(this, MESETA_ITEM_ID, meseta as u32)), + GameMessage::CreateItem(builder::message::create_meseta(other, meseta)), + ] + }); - let create_item_packets = this_new_items + let clients_in_room = client_location.get_all_clients_by_client(id).await?; + let traded_item_packets = remove_item_packets + .chain(create_item_packets) + .chain(meseta_packets) + .flat_map(move |packet| { + clients_in_room + .clone() .into_iter() - .map(move |item| { - (this_local_client, item) - }) - .chain(other_new_items - .into_iter() - .map(move |item| { - (other_local_client, item) - })) - .map(|(client, item)| { - match item.item { - InventoryItemDetail::Individual(individual_item) => { - GameMessage::CreateItem(builder::message::create_individual_item(client, item.item_id, &individual_item).unwrap()) - }, - InventoryItemDetail::Stacked(stacked_item) => { - GameMessage::CreateItem(builder::message::create_stacked_item(client, item.item_id, &stacked_item.tool, stacked_item.count()).unwrap()) + .filter_map(move |client| { + match packet { + GameMessage::PlayerNoLongerHasItem(ref no_longer) => { + if client.local_client == no_longer.client { + None + } + else { + Some((client.client, SendShipPacket::Message(Message::new(packet.clone())))) + } } + _ => Some((client.client, SendShipPacket::Message(Message::new(packet.clone())))) } - }); + }) + }); - let meseta_packets = vec![(this_local_client, other_local_client, this.meseta), (other_local_client, this_local_client, other.meseta)] - .into_iter() - .filter(|(_, _, meseta)| *meseta != 0) - .flat_map(|(this, other, meseta)| { - [ - GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(this, MESETA_ITEM_ID, meseta as u32)), - GameMessage::CreateItem(builder::message::create_meseta(other, meseta)), - ] - }); + let close_trade = vec![ + (this.client(), SendShipPacket::TradeSuccessful(TradeSuccessful::default())), + (other.client(), SendShipPacket::TradeSuccessful(TradeSuccessful::default())) + ].into_iter(); + Ok(Box::new(traded_item_packets.chain(close_trade))) + } + } +} - let clients_in_room = client_location.get_all_clients_by_client(id)?; - let traded_item_packets = remove_item_packets - .chain(create_item_packets) - .chain(meseta_packets) - .flat_map(move |packet| { - clients_in_room - .clone() - .into_iter() - .filter_map(move |client| { - match packet { - GameMessage::PlayerNoLongerHasItem(ref no_longer) => { - if client.local_client == no_longer.client { - None - } - else { - Some((client.client, SendShipPacket::Message(Message::new(packet.clone())))) - } - } - _ => Some((client.client, SendShipPacket::Message(Message::new(packet.clone())))) - } - }) - }); - let close_trade = vec![ - (this.client(), SendShipPacket::TradeSuccessful(TradeSuccessful::default())), - (other.client(), SendShipPacket::TradeSuccessful(TradeSuccessful::default())) - ].into_iter(); - Ok(Box::new(traded_item_packets.chain(close_trade))) - } - } - }, - _ => { +pub async fn trade_confirmed(id: ClientId, + entity_gateway: EG, + client_location: &ClientLocation, + clients: &mut Clients, + item_state: &mut ItemState, + trades: &mut TradeState) + -> Result + Send>, ShipError> +where + EG: EntityGateway +{ + match trade_confirmed_inner(id, entity_gateway, client_location, clients, item_state, trades).await { + Ok(result) => Ok(result), + Err(_err) => { let (_this, other) = trades.remove_trade(&id); - Ok(Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter() + Ok(Box::new(client_location.get_all_clients_by_client(id).await?.into_iter() .filter(move |client| other.as_ref().map(|other| client.client == other.client()).unwrap_or_else(|| false)) .map(move |client| { (client.client, SendShipPacket::CancelTrade(CancelTrade {})) diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 47e4529..f744bc0 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -190,7 +190,7 @@ impl RecvServerPacket for RecvShipPacket { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum SendShipPacket { ShipWelcome(ShipWelcome), LoginResponse(LoginResponse), @@ -514,7 +514,7 @@ impl ShipServerState { }, GameMessage::DropCoordinates(drop_coordinates) => { let block = self.blocks.with_client(id, &self.clients)?; - handler::message::drop_coordinates(id, drop_coordinates, &block.client_location, &mut self.clients, &block.rooms)? + handler::message::drop_coordinates(id, drop_coordinates, &block.client_location, &mut self.clients, &block.rooms).await? }, GameMessage::PlayerNoLongerHasItem(no_longer_has_item) => { let block = self.blocks.with_client(id, &self.clients)?; @@ -525,7 +525,7 @@ impl ShipServerState { GameMessage::PlayerLoadedIn(_) | GameMessage::PlayerWalking(_) | GameMessage::PlayerRunning(_) | GameMessage::PlayerWarped(_) | GameMessage::PlayerChangedFloor(_) | GameMessage::InitializeSpeechNpc(_) => { let block = self.blocks.with_client(id, &self.clients)?; - handler::message::update_player_position(id, msg, &mut self.clients, &block.client_location, &block.rooms)? + handler::message::update_player_position(id, msg, &mut self.clients, &block.client_location, &block.rooms).await? }, GameMessage::ChargeAttack(charge_attack) => { let block = self.blocks.with_client(id, &self.clients)?; @@ -558,7 +558,7 @@ impl ShipServerState { _ => { let cmsg = msg.clone(); let block = self.blocks.with_client(id, &self.clients)?; - Box::new(block.client_location.get_client_neighbors(id).unwrap().into_iter() + Box::new(block.client_location.get_client_neighbors(id).await.unwrap().into_iter() .map(move |client| { (client.client, SendShipPacket::Message(cmsg.clone())) })) @@ -571,7 +571,7 @@ impl ShipServerState { let block = self.blocks.with_client(id, &self.clients)?; Ok(match &msg.msg { GameMessage::GuildcardSend(guildcard_send) => { - handler::direct_message::guildcard_send(id, guildcard_send, target, &block.client_location, &self.clients) + handler::direct_message::guildcard_send(id, guildcard_send, target, &block.client_location, &self.clients).await }, GameMessage::RequestItem(request_item) => { handler::direct_message::request_item(id, request_item, self.entity_gateway.clone(), &block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_state).await? @@ -605,7 +605,7 @@ impl ShipServerState { }, _ => { let cmsg = msg.clone(); - Box::new(block.client_location.get_all_clients_by_client(id).unwrap().into_iter() + Box::new(block.client_location.get_all_clients_by_client(id).await.unwrap().into_iter() .filter(move |client| client.local_client.id() == target as u8) .map(move |client| { (client.client, SendShipPacket::DirectMessage(cmsg.clone())) @@ -650,7 +650,7 @@ impl ServerState for ShipServerState { RecvShipPacket::QuestDetailRequest(questdetailrequest) => { let block = self.blocks.with_client(id, &self.clients)?; match questdetailrequest.menu { - QUEST_SELECT_MENU_ID => handler::quest::quest_detail(id, questdetailrequest, &block.client_location, &mut block.rooms)?, + QUEST_SELECT_MENU_ID => handler::quest::quest_detail(id, questdetailrequest, &block.client_location, &mut block.rooms).await?, _ => unreachable!(), } }, @@ -658,27 +658,27 @@ impl ServerState for ShipServerState { let block = self.blocks.with_client(id, &self.clients)?; match menuselect.menu { SHIP_MENU_ID => { - let leave_lobby = handler::lobby::remove_from_lobby(id, &mut block.client_location).into_iter().into_iter().flatten(); + let leave_lobby = handler::lobby::remove_from_lobby(id, &mut block.client_location).await.into_iter().into_iter().flatten(); let select_ship = handler::ship::selected_ship(id, menuselect, &self.ship_list)?; Box::new(leave_lobby.chain(select_ship)) } BLOCK_MENU_ID => { - let leave_lobby = handler::lobby::remove_from_lobby(id, &mut block.client_location).into_iter().into_iter().flatten(); + let leave_lobby = handler::lobby::remove_from_lobby(id, &mut block.client_location).await.into_iter().into_iter().flatten(); let select_block = handler::lobby::block_selected(id, menuselect, &mut self.clients, &self.item_state)?.into_iter(); Box::new(leave_lobby.chain(select_block)) } - ROOM_MENU_ID => handler::room::join_room(id, menuselect, &mut block.client_location, &mut self.clients, &mut self.item_state, &mut block.rooms)?, - QUEST_CATEGORY_MENU_ID => handler::quest::select_quest_category(id, menuselect, &block.client_location, &mut block.rooms)?, + ROOM_MENU_ID => handler::room::join_room(id, menuselect, &mut block.client_location, &mut self.clients, &mut self.item_state, &mut block.rooms).await?, + QUEST_CATEGORY_MENU_ID => handler::quest::select_quest_category(id, menuselect, &block.client_location, &mut block.rooms).await?, _ => unreachable!(), } }, RecvShipPacket::QuestMenuSelect(questmenuselect) => { let block = self.blocks.with_client(id, &self.clients)?; - handler::quest::player_chose_quest(id, questmenuselect, &mut self.clients, &block.client_location, &mut block.rooms)? + handler::quest::player_chose_quest(id, questmenuselect, &mut self.clients, &block.client_location, &mut block.rooms).await? }, RecvShipPacket::MenuDetail(menudetail) => { let block = self.blocks.with_client(id, &self.clients)?; - Box::new(handler::lobby::get_room_tab_info(id, menudetail, &mut block.client_location, &self.clients, &mut block.rooms)?.into_iter()) + Box::new(handler::lobby::get_room_tab_info(id, menudetail, &mut block.client_location, &self.clients, &mut block.rooms).await?.into_iter()) }, RecvShipPacket::RoomPasswordReq(room_password_req) => { let block = self.blocks.with_client(id, &self.clients)?; @@ -689,7 +689,7 @@ impl ServerState for ShipServerState { menu: room_password_req.menu, item: room_password_req.item, }; - handler::room::join_room(id, &menuselect, &mut block.client_location, &mut self.clients, &mut self.item_state, &mut block.rooms)? + handler::room::join_room(id, &menuselect, &mut block.client_location, &mut self.clients, &mut self.item_state, &mut block.rooms).await? } else { Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("Incorrect password".into())))].into_iter()) @@ -697,7 +697,7 @@ impl ServerState for ShipServerState { }, RecvShipPacket::CharData(chardata) => { let block = self.blocks.with_client(id, &self.clients)?; - Box::new(handler::lobby::send_player_to_lobby(id, chardata, &mut block.client_location, &self.clients, &self.item_state)?.into_iter()) + Box::new(handler::lobby::send_player_to_lobby(id, chardata, &mut block.client_location, &self.clients, &self.item_state).await?.into_iter()) }, RecvShipPacket::Message(msg) => { self.message(id, msg).await? @@ -707,33 +707,33 @@ impl ServerState for ShipServerState { }, RecvShipPacket::PlayerChat(msg) => { let block = self.blocks.with_client(id, &self.clients)?; - Box::new(handler::communication::player_chat(id, msg, &block.client_location, &self.clients)?) + Box::new(handler::communication::player_chat(id, msg, &block.client_location, &self.clients).await?) }, RecvShipPacket::CreateRoom(create_room) => { let block = self.blocks.with_client(id, &self.clients)?; - handler::room::create_room(id, create_room, &mut block.client_location, &mut self.clients, &mut self.item_state, &mut block.rooms)? + handler::room::create_room(id, create_room, &mut block.client_location, &mut self.clients, &mut self.item_state, &mut block.rooms).await? }, RecvShipPacket::RoomNameRequest(_req) => { let block = self.blocks.with_client(id, &self.clients)?; - handler::room::room_name_request(id, &block.client_location, &block.rooms) + handler::room::room_name_request(id, &block.client_location, &block.rooms).await }, RecvShipPacket::UpdateConfig(pkt) => { handler::settings::update_config(id, pkt, &mut self.clients, self.entity_gateway.clone()).await }, RecvShipPacket::ViewInfoboardRequest(_pkt) => { let block = self.blocks.with_client(id, &self.clients)?; - handler::communication::request_infoboard(id, &block.client_location, &self.clients) + handler::communication::request_infoboard(id, &block.client_location, &self.clients).await }, RecvShipPacket::WriteInfoboard(pkt) => { handler::communication::write_infoboard(id, pkt, &mut self.clients, self.entity_gateway.clone()).await }, RecvShipPacket::RoomListRequest(_req) => { let block = self.blocks.with_client(id, &self.clients)?; - handler::room::request_room_list(id, &block.client_location, &block.rooms) + handler::room::request_room_list(id, &block.client_location, &block.rooms).await }, RecvShipPacket::Like62ButCooler(cool62) => { let block = self.blocks.with_client(id, &self.clients)?; - handler::room::cool_62(id, cool62, &block.client_location) + handler::room::cool_62(id, cool62, &block.client_location).await }, RecvShipPacket::ClientCharacterData(_) => { // TOOD: validate this in some way? @@ -741,11 +741,11 @@ impl ServerState for ShipServerState { }, RecvShipPacket::DoneBursting(_) => { let block = self.blocks.with_client(id, &self.clients)?; - handler::room::done_bursting(id, &block.client_location, &mut block.rooms) + handler::room::done_bursting(id, &block.client_location, &mut block.rooms).await }, RecvShipPacket::DoneBursting2(_) => { let block = self.blocks.with_client(id, &self.clients)?; - handler::room::done_bursting(id, &block.client_location, &mut block.rooms) + handler::room::done_bursting(id, &block.client_location, &mut block.rooms).await }, RecvShipPacket::LobbySelect(pkt) => { let block = self.blocks.with_client(id, &self.clients)?; @@ -753,19 +753,19 @@ impl ServerState for ShipServerState { }, RecvShipPacket::RequestQuestList(rql) => { let block = self.blocks.with_client(id, &self.clients)?; - handler::quest::send_quest_category_list(id, rql, &block.client_location, &mut block.rooms)? + handler::quest::send_quest_category_list(id, rql, &block.client_location, &mut block.rooms).await? }, RecvShipPacket::QuestFileRequest(quest_file_request) => { let block = self.blocks.with_client(id, &self.clients)?; - handler::quest::quest_file_request(id, quest_file_request, &block.client_location, &mut block.rooms)? + handler::quest::quest_file_request(id, quest_file_request, &block.client_location, &mut block.rooms).await? }, RecvShipPacket::QuestChunkAck(quest_chunk_ack) => { let block = self.blocks.with_client(id, &self.clients)?; - handler::quest::quest_chunk_ack(id, quest_chunk_ack, &block.client_location, &mut block.rooms)? + handler::quest::quest_chunk_ack(id, quest_chunk_ack, &block.client_location, &mut block.rooms).await? }, RecvShipPacket::DoneLoadingQuest(_) => { let block = self.blocks.with_client(id, &self.clients)?; - handler::quest::done_loading_quest(id, &mut self.clients, &block.client_location)? + handler::quest::done_loading_quest(id, &mut self.clients, &block.client_location).await? }, RecvShipPacket::FullCharacterData(_full_character_data) => { Box::new(None.into_iter()) @@ -799,19 +799,19 @@ impl ServerState for ShipServerState { async fn on_disconnect(&mut self, id: ClientId) -> Result, anyhow::Error> { let client = self.clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; let block = self.blocks.with_client(id, &self.clients)?; - let area_client = block.client_location.get_local_client(id)?; - let neighbors = block.client_location.get_client_neighbors(id)?; + let area_client = block.client_location.get_local_client(id).await?; + let neighbors = block.client_location.get_client_neighbors(id).await?; - let pkt = match block.client_location.get_area(id)? { + let pkt = match block.client_location.get_area(id).await? { RoomLobby::Room(room) => { if neighbors.is_empty() { block.rooms[room.0] = None; } - let leader = block.client_location.get_room_leader(room)?; + let leader = block.client_location.get_room_leader(room).await?; SendShipPacket::LeaveRoom(LeaveRoom::new(area_client.local_client.id(), leader.local_client.id())) }, RoomLobby::Lobby(lobby) => { - let leader = block.client_location.get_lobby_leader(lobby)?; + let leader = block.client_location.get_lobby_leader(lobby).await?; SendShipPacket::LeaveLobby(LeaveLobby::new(area_client.local_client.id(), leader.local_client.id())) } }; @@ -820,7 +820,7 @@ impl ServerState for ShipServerState { shipgate_sender(ShipMessage::RemoveUser(client.user.id)); } - block.client_location.remove_client_from_area(id); + block.client_location.remove_client_from_area(id).await; self.item_state.remove_character_from_room(&client.character); if let Some(mut client) = self.clients.remove(&id) { diff --git a/src/ship/trade.rs b/src/ship/trade.rs index 311cf9e..790c354 100644 --- a/src/ship/trade.rs +++ b/src/ship/trade.rs @@ -1,8 +1,8 @@ use std::collections::HashMap; -use std::cell::RefCell; - use crate::common::serverstate::ClientId; use crate::ship::items; +use async_std::sync::{Mutex, MutexGuard}; +use futures::future::Future; #[derive(Debug, Clone)] pub enum TradeItem { @@ -67,7 +67,7 @@ impl ClientTradeState { pub fn client(&self) -> ClientId { self.client } - + pub fn other_client(&self) -> ClientId { self.other_client } @@ -83,7 +83,7 @@ pub enum TradeStateError { #[derive(Default, Debug)] pub struct TradeState { - trades: HashMap>, + trades: HashMap>, } impl TradeState { @@ -95,7 +95,7 @@ impl TradeState { meseta: 0, status: TradeStatus::SentRequest, }; - self.trades.insert(*sender, RefCell::new(state)); + self.trades.insert(*sender, Mutex::new(state)); let state = ClientTradeState { client: *receiver, @@ -104,26 +104,26 @@ impl TradeState { meseta: 0, status: TradeStatus::ReceivedRequest, }; - self.trades.insert(*receiver, RefCell::new(state)); + self.trades.insert(*receiver, Mutex::new(state)); } pub fn in_trade(&self, client: &ClientId) -> bool { self.trades.contains_key(client) } - pub fn with (&self, client: &ClientId, func: F) -> Result + pub async fn with<'a, T, F, Fut> (&'a self, client: &ClientId, func: F) -> Result where - F: Fn(&mut ClientTradeState, &mut ClientTradeState) -> T + F: FnOnce(MutexGuard<'a, ClientTradeState>, MutexGuard<'a, ClientTradeState>) -> Fut + 'a, + Fut: Future { - let mut c1 = self.trades.get(client).ok_or(TradeStateError::ClientNotInTrade(*client))?.borrow_mut(); - let mut c2 = self.trades.get(&c1.other_client).ok_or(TradeStateError::ClientNotInTrade(c1.other_client))?.borrow_mut(); + let c1 = self.trades.get(client).ok_or(TradeStateError::ClientNotInTrade(*client))?.lock().await; + let c2 = self.trades.get(&c1.other_client).ok_or(TradeStateError::ClientNotInTrade(c1.other_client))?.lock().await; // sanity check if c1.client != c2.other_client { return Err(TradeStateError::MismatchedTrade(c1.client, c2.client)); } - - Ok(func(&mut c1, &mut c2)) + Ok(func(c1, c2).await) } // TODO: is it possible for this to not return Options? diff --git a/tests/test_trade.rs b/tests/test_trade.rs index 0d97cd7..fd6efe0 100644 --- a/tests/test_trade.rs +++ b/tests/test_trade.rs @@ -2251,8 +2251,14 @@ async fn test_trade_not_enough_inventory_space_individual() { assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { - })).await.err().unwrap(); - assert!(matches!(ack.downcast::().unwrap(), ItemStateError::InventoryError(InventoryError::InventoryFull))); + })).await.unwrap().collect::>(); + + assert_eq!(ack, + vec![ + (ClientId(1), SendShipPacket::CancelTrade(CancelTrade {})), + (ClientId(2), SendShipPacket::CancelTrade(CancelTrade {})), + ]); + let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 2); @@ -2363,8 +2369,13 @@ async fn test_trade_not_enough_inventory_space_stacked() { assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { - })).await.err().unwrap(); - assert!(matches!(ack.downcast::().unwrap(), ItemStateError::InventoryError(InventoryError::InventoryFull))); + })).await.unwrap().collect::>(); + + assert_eq!(ack, + vec![ + (ClientId(1), SendShipPacket::CancelTrade(CancelTrade {})), + (ClientId(2), SendShipPacket::CancelTrade(CancelTrade {})), + ]); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); @@ -2472,8 +2483,13 @@ async fn test_trade_stack_too_big() { assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { - })).await.err().unwrap(); - assert!(matches!(ack.downcast::().unwrap(), ItemStateError::InventoryError(InventoryError::StackFull))); + })).await.unwrap().collect::>(); + + assert_eq!(ack, + vec![ + (ClientId(1), SendShipPacket::CancelTrade(CancelTrade {})), + (ClientId(2), SendShipPacket::CancelTrade(CancelTrade {})), + ]); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); @@ -3094,8 +3110,13 @@ async fn test_invalid_trade_when_both_inventories_are_full() { assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { - })).await.err().unwrap(); - assert!(matches!(ack.downcast::().unwrap(), ItemStateError::InventoryError(InventoryError::InventoryFull))); + })).await.unwrap().collect::>(); + + assert_eq!(ack, + vec![ + (ClientId(1), SendShipPacket::CancelTrade(CancelTrade {})), + (ClientId(2), SendShipPacket::CancelTrade(CancelTrade {})), + ]); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 30);