Browse Source

make ClientLocation Clone-able

pull/121/head
jake 2 years ago
parent
commit
58c26301bf
  1. 4
      Cargo.toml
  2. 2
      src/lib.rs
  3. 2
      src/ship/items/actions.rs
  4. 483
      src/ship/location.rs
  5. 50
      src/ship/packet/builder/lobby.rs
  6. 28
      src/ship/packet/builder/room.rs
  7. 20
      src/ship/packet/handler/communication.rs
  8. 68
      src/ship/packet/handler/direct_message.rs
  9. 66
      src/ship/packet/handler/lobby.rs
  10. 56
      src/ship/packet/handler/message.rs
  11. 32
      src/ship/packet/handler/quest.rs
  12. 111
      src/ship/packet/handler/room.rs
  13. 612
      src/ship/packet/handler/trade.rs
  14. 66
      src/ship/ship.rs
  15. 24
      src/ship/trade.rs
  16. 37
      tests/test_trade.rs

4
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" }

2
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;

2
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?;

483
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<Room>; MAX_ROOMS],
client_location: HashMap<ClientId, RoomLobby>,
lobbies: [Arc<RwLock<Lobby>>; 15],
rooms: [Option<Arc<RwLock<Room>>>; MAX_ROOMS],
client_location: Arc<RwLock<HashMap<ClientId, RoomLobby>>>,
}
impl Default for ClientLocation {
fn default() -> ClientLocation {
//const RNONE: Option<Arc<RwLock<Room>>> = None;
//const LINIT: Arc<RwLock<Lobby>> = 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<LobbyId, JoinLobbyError> {
let l = (0..15)
.map(|lobby_index| {
pub async fn add_client_to_next_available_lobby(&self, id: ClientId, lobby: LobbyId) -> Result<LobbyId, JoinLobbyError> {
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<RoomId, CreateRoomError> {
pub async fn create_new_room(&mut self, id: ClientId) -> Result<RoomId, CreateRoomError> {
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<Vec<AreaClient>, GetNeighborError> {
let area = self.client_location.get(&id).ok_or(GetNeighborError::InvalidClient)?;
pub async fn get_all_clients_by_client(&self, id: ClientId) -> Result<Vec<AreaClient>, 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<Vec<AreaClient>, GetNeighborError> {
let area = self.client_location.get(&id).ok_or(GetNeighborError::InvalidClient)?;
pub async fn get_client_neighbors(&self, id: ClientId) -> Result<Vec<AreaClient>, 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<AreaClient, GetLeaderError> {
let mut r = self.rooms[room.0]
pub async fn get_room_leader(&self, room: RoomId) -> Result<AreaClient, GetLeaderError> {
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::<Vec<_>>();
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<AreaClient, GetLeaderError> {
let mut l = self.lobbies[lobby.0]
.0.iter().flatten()
pub async fn get_lobby_leader(&self, lobby: LobbyId) -> Result<AreaClient, GetLeaderError> {
let l = self.lobbies[lobby.0]
.read()
.await;
let mut l = l
.0
.iter()
.flatten()
.collect::<Vec<_>>();
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<AreaClient, GetLeaderError> {
pub async fn get_area_leader(&self, roomlobby: RoomLobby) -> Result<AreaClient, GetLeaderError> {
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<AreaClient, GetLeaderError> {
let area = self.client_location.get(&id).ok_or(GetLeaderError::InvalidClient)?;
pub async fn get_leader_by_client(&self, id: ClientId) -> Result<AreaClient, GetLeaderError> {
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<Vec<AreaClient>, GetClientsError> {
Ok(self.lobbies.get(lobby.0).ok_or(GetClientsError::InvalidArea)?.0
pub async fn get_clients_in_lobby(&self, lobby: LobbyId) -> Result<Vec<AreaClient>, 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<Vec<AreaClient>, GetClientsError> {
pub async fn get_clients_in_room(&self, room: RoomId) -> Result<Vec<AreaClient>, 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<AreaClient, GetClientsError> {
let area = self.client_location.get(&id).ok_or(GetClientsError::InvalidClient)?;
pub async fn get_local_client(&self, id: ClientId) -> Result<AreaClient, GetClientsError> {
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<RoomLobby, GetAreaError> {
self.client_location.get(&id)
pub async fn get_area(&self, id: ClientId) -> Result<RoomLobby, GetAreaError> {
self.client_location
.read()
.await
.get(&id)
.ok_or(GetAreaError::InvalidClient)
.map(Clone::clone)
}
pub fn get_room(&self, id: ClientId) -> Result<RoomId, GetAreaError> {
if let RoomLobby::Room(room) = self.client_location.get(&id).ok_or(GetAreaError::InvalidClient)? {
pub async fn get_room(&self, id: ClientId) -> Result<RoomId, GetAreaError> {
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<LobbyId, GetAreaError> {
if let RoomLobby::Lobby(lobby) = self.client_location.get(&id).ok_or(GetAreaError::InvalidClient)? {
pub async fn get_lobby(&self, id: ClientId) -> Result<LobbyId, GetAreaError> {
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<const N: usize>(id: ClientId, client_list : &mut [Option<AreaClient>; 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<_>>() == vec![
assert!(cl.get_clients_in_lobby(LobbyId(0)).await.into_iter().flatten().map(|c| (c.client, c.local_client)).collect::<Vec<_>>() == 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<_>>() == 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<_>>() == 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<_>>() == 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<_>>() == 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<_>>() == vec![
assert!(cl.get_clients_in_room(room).await.unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::<Vec<_>>() == 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<_>>() == vec![
assert!(cl.get_client_neighbors(ClientId(23)).await.unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::<Vec<_>>() == 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<_>>() == 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<_>>(),
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<_>>() == vec![
assert!(cl.get_clients_in_room(room).await.unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::<Vec<_>>() == 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))));
}
}

50
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<JoinLobby, anyhow::Error> {
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<JoinLobby, anyhow::Error> {
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<AddToLobby, ShipError> {
pub async fn add_to_lobby(id: ClientId,
lobby: LobbyId,
client_location: &ClientLocation,
clients: &Clients,
item_state: &ItemState)
-> Result<AddToLobby, ShipError> {
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<LeaveLobby, ShipError> {
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<LeaveLobby, ShipError> {
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,

28
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<JoinRoom, ShipError> {
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<JoinRoom, ShipError> {
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::<Result<_, ShipError>, _, _>(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(),

20
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<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> {
pub async fn player_chat(id: ClientId,
msg: &PlayerChat,
client_location: &ClientLocation,
clients: &Clients) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
let area_clients = client_location.get_client_neighbors(id).unwrap();
pub async fn request_infoboard(id: ClientId,
client_location: &ClientLocation,
clients: &Clients)
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
let area_clients = client_location.get_client_neighbors(id).await.unwrap();
let r = area_clients.iter()
.filter_map(|c| {
clients.get(&c.client)

68
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<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
pub async fn guildcard_send(id: ClientId,
guildcard_send: &GuildcardSend,
target: u32,
client_location: &ClientLocation,
clients: &Clients)
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<EG>(id: ClientId,
@ -79,7 +82,7 @@ pub async fn request_item<EG>(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<EG>(id: ClientId,
box_drop_request: &BoxDropRequest,
mut entity_gateway: EG,
client_location: &ClientLocation,
clients: &mut Clients,
rooms: &mut Rooms,
item_state: &mut ItemState)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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()))))

66
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<Vec<(ClientId, SendShipPacket)>, 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<Vec<(ClientId, SendShipPacket)>, 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<EG: EntityGateway>(id: ClientId,
mut entity_gateway: EG)
-> Result<Vec<(ClientId, SendShipPacket)>, 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<EG: EntityGateway>(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<EG: EntityGateway>(id: ClientId,
.collect())
}
pub fn remove_from_lobby(id: ClientId,
client_location: &mut ClientLocation)
-> Result<Vec<(ClientId, SendShipPacket)>, 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<Vec<(ClientId, SendShipPacket)>, 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<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
pub async fn get_room_tab_info(id: ClientId,
pkt: &MenuDetail,
client_location: &mut ClientLocation,
clients: &Clients,
rooms: &mut Rooms)
-> Result<Vec<(ClientId, SendShipPacket)>, 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;

56
src/ship/packet/handler/message.rs

@ -19,8 +19,8 @@ pub async fn request_exp<EG: EntityGateway>(id: ClientId,
rooms: &mut Rooms)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<EG: EntityGateway>(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<dyn Iterator<Item = _> + 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<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
pub async fn drop_coordinates(id: ClientId,
drop_coordinates: &DropCoordinates,
client_location: &ClientLocation,
clients: &mut Clients,
rooms: &Rooms)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
pub async fn update_player_position(id: ClientId,
message: &Message,
clients: &mut Clients,
client_location: &ClientLocation,
rooms: &Rooms)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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()))))
})))

32
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<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> {
pub async fn done_loading_quest(id: ClientId, clients: &mut Clients, client_location: &ClientLocation)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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| {

111
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<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<dyn Iterator<Item=(ClientId, SendShipPacket)> + 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<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
let area = client_location.get_area(id).unwrap();
pub async fn room_name_request(id: ClientId,
client_location: &ClientLocation,
rooms: &Rooms)
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<Box<dyn Iterator<Item=(ClientId, SendShipPacket)> + 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<Box<dyn Iterator<Item=(ClientId, SendShipPacket)> + 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<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
let area = client_location.get_area(id).unwrap();
pub async fn done_bursting(id: ClientId,
client_location: &ClientLocation,
rooms: &mut Rooms)
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
let area = client_location.get_area(id).await.unwrap();
let mut rare_monster_list: Option<Vec<u16>> = 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<dyn Iterator<Item=(ClientId, SendShipPacket)> + 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<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
let active_room_list = rooms.iter()
pub async fn request_room_list(id: ClientId,
client_location: &ClientLocation,
rooms: &Rooms)
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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::<Vec<_>>().await).await
}))].into_iter())
}
pub fn cool_62(id: ClientId,
cool_62: &Like62ButCooler,
client_location: &ClientLocation)
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
pub async fn cool_62(id: ClientId,
cool_62: &Like62ButCooler,
client_location: &ClientLocation)
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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()))

612
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<F>(id: ClientId,
pkt: TradeRequest,
client_location: &ClientLocation,
target: u32,
this: &mut ClientTradeState,
other: &mut ClientTradeState,
action: F) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError>
trade_request: &TradeRequest,
target: u32,
client_location: &ClientLocation,
clients: &mut Clients,
item_state: &mut ItemState,
trades: &mut TradeState)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError>
{
Ok(trades
.with(&id, |this, other| -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
let pkts: Result<Box<dyn Iterator<Item=_> + 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::<Result<Vec<_>, anyhow::Error>>()?;
.collect::<Result<Vec<_>, 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<dyn Iterator<Item=_> + Send>)
}
else {
Ok(Box::new(None.into_iter()))
Ok(Box::new(Vec::new().into_iter()) as Box<dyn Iterator<Item=_> + 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<EG>(id: ClientId,
mut entity_gateway: EG,
client_location: &ClientLocation,
clients: &mut Clients,
item_state: &mut ItemState,
trades: &mut TradeState)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
pub async fn trade_confirmed_inner<EG>(id: ClientId,
mut entity_gateway: EG,
client_location: &ClientLocation,
clients: &mut Clients,
item_state: &mut ItemState,
trades: &mut TradeState)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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<EG>(id: ClientId,
entity_gateway: EG,
client_location: &ClientLocation,
clients: &mut Clients,
item_state: &mut ItemState,
trades: &mut TradeState)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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 {}))

66
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<EG: EntityGateway + Clone> ShipServerState<EG> {
},
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<EG: EntityGateway + Clone> ShipServerState<EG> {
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<EG: EntityGateway + Clone> ShipServerState<EG> {
_ => {
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<EG: EntityGateway + Clone> ShipServerState<EG> {
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<EG: EntityGateway + Clone> ShipServerState<EG> {
},
_ => {
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<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> {
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<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> {
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<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> {
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<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> {
},
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<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> {
},
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<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> {
},
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<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> {
},
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<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> {
async fn on_disconnect(&mut self, id: ClientId) -> Result<Vec<(ClientId, SendShipPacket)>, 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<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> {
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) {

24
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<ClientId, RefCell<ClientTradeState>>,
trades: HashMap<ClientId, Mutex<ClientTradeState>>,
}
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<T, F> (&self, client: &ClientId, func: F) -> Result<T, TradeStateError>
pub async fn with<'a, T, F, Fut> (&'a self, client: &ClientId, func: F) -> Result<T, TradeStateError>
where
F: Fn(&mut ClientTradeState, &mut ClientTradeState) -> T
F: FnOnce(MutexGuard<'a, ClientTradeState>, MutexGuard<'a, ClientTradeState>) -> Fut + 'a,
Fut: Future<Output=T>
{
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?

37
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::<ItemStateError>().unwrap(), ItemStateError::InventoryError(InventoryError::InventoryFull)));
})).await.unwrap().collect::<Vec<_>>();
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::<ItemStateError>().unwrap(), ItemStateError::InventoryError(InventoryError::InventoryFull)));
})).await.unwrap().collect::<Vec<_>>();
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::<ItemStateError>().unwrap(), ItemStateError::InventoryError(InventoryError::StackFull)));
})).await.unwrap().collect::<Vec<_>>();
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::<ItemStateError>().unwrap(), ItemStateError::InventoryError(InventoryError::InventoryFull)));
})).await.unwrap().collect::<Vec<_>>();
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);

Loading…
Cancel
Save