306 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			306 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use std::convert::Into;
 | |
| use futures::stream::StreamExt;
 | |
| 
 | |
| use async_std::sync::Arc;
 | |
| 
 | |
| use libpso::packet::ship::*;
 | |
| use libpso::packet::messages::*;
 | |
| use crate::common::serverstate::ClientId;
 | |
| use stats::leveltable::LEVEL_TABLE;
 | |
| use entity::gateway::EntityGateway;
 | |
| use entity::character::SectionID;
 | |
| use entity::room::{NewRoomEntity, RoomEntityMode, RoomNote};
 | |
| use crate::ship::drops::DropTable;
 | |
| use crate::ship::ship::{SendShipPacket, Clients, ShipEvent};
 | |
| use crate::ship::room::{Rooms, RoomState, RoomCreationError};
 | |
| use maps::room::{Episode, Difficulty, RoomMode};
 | |
| use maps::enemy::RareEnemyEvent;
 | |
| use maps::maps::Maps;
 | |
| use crate::ship::location::{ClientLocation, RoomId, RoomLobby, GetAreaError};
 | |
| use crate::ship::packet::builder;
 | |
| use crate::ship::items::state::ItemState;
 | |
| 
 | |
| #[allow(clippy::too_many_arguments)]
 | |
| pub async fn create_room<EG>(id: ClientId,
 | |
|                              create_room: CreateRoom,
 | |
|                              entity_gateway: &mut EG,
 | |
|                              client_location: &mut ClientLocation,
 | |
|                              clients: &Clients,
 | |
|                              item_state: &mut ItemState,
 | |
|                              rooms: &Rooms,
 | |
|                              map_builder: Arc<Box<dyn Fn(RoomMode, Option<RareEnemyEvent>) -> Maps + Send + Sync>>,
 | |
|                              drop_table_builder: Arc<Box<dyn Fn(Episode, Difficulty, SectionID) -> DropTable + Send + Sync>>,
 | |
|                              event: ShipEvent)
 | |
|                              -> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
 | |
| where
 | |
|     EG: EntityGateway + Clone + 'static,
 | |
| {
 | |
|     let level = clients.with(id, |client| Box::pin(async move {
 | |
|         LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp)
 | |
|     })).await?;
 | |
|     let difficulty = create_room.difficulty.try_into()
 | |
|         .map_err(|()| RoomCreationError::InvalidDifficulty(create_room.difficulty))?;
 | |
|     match difficulty {
 | |
|         Difficulty::Ultimate if level < 80 => {
 | |
|             return Ok(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 80 \nto create Ultimate rooms.".into())))])
 | |
|         },
 | |
|         Difficulty::VeryHard if level < 40 => {
 | |
|             return Ok(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 40 \nto create Very Hard rooms.".into())))])
 | |
|         },
 | |
|         Difficulty::Hard if level < 20 => {
 | |
|             return Ok(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 20 \nto create Hard rooms.".into())))])
 | |
|         },
 | |
|         _ => {},
 | |
|     };
 | |
| 
 | |
|     let area = client_location.get_area(id).await?;
 | |
|     let old_area_client = client_location.get_local_client(id).await?;
 | |
|     let lobby_neighbors = client_location.get_client_neighbors(id).await?;
 | |
| 
 | |
|     let room_id = client_location.create_new_room(id).await?;
 | |
|     let new_area_client = client_location.get_local_client(id).await?;
 | |
| 
 | |
|     let name = String::from_utf16_lossy(&create_room.name).trim_matches(char::from(0)).to_string();
 | |
|     let mode = match (create_room.battle, create_room.challenge, create_room.single_player) {
 | |
|         (1, 0, 0) => RoomEntityMode::Battle,
 | |
|         (0, 1, 0) => RoomEntityMode::Challenge,
 | |
|         (0, 0, 1) => RoomEntityMode::Single,
 | |
|         _ => RoomEntityMode::Multi,
 | |
|     };
 | |
|     let episode = create_room.episode.try_into()
 | |
|         .map_err(|()| RoomCreationError::InvalidEpisode(create_room.episode))?;
 | |
|     //let difficulty = create_room.difficulty.try_into()?;
 | |
| 
 | |
|     let room = clients.with(id, |client| {
 | |
|         let mut item_state = item_state.clone();
 | |
|         let mut entity_gateway = entity_gateway.clone();
 | |
|         Box::pin(async move {
 | |
|             item_state.add_character_to_room(room_id, &client.character, new_area_client).await;
 | |
|             let room_entity = entity_gateway.create_room(NewRoomEntity {
 | |
|                 name: name.clone(),
 | |
|                 section_id: client.character.section_id,
 | |
|                 mode,
 | |
|                 episode,
 | |
|                 difficulty,
 | |
|             }).await?;
 | |
| 
 | |
|             entity_gateway.add_room_note(room_entity.id, RoomNote::Create {
 | |
|                 character_id: client.character.id,
 | |
|             }).await?;
 | |
| 
 | |
|             let mut room = RoomState::new(room_entity.id, mode, episode, difficulty,
 | |
|                                           client.character.section_id, name, create_room.password, event,
 | |
|                                           map_builder, drop_table_builder)?;
 | |
|             room.bursting = true;
 | |
|             Ok::<_, anyhow::Error>(room)
 | |
|         })}).await??;
 | |
| 
 | |
|     let join_room = builder::room::join_room(id, clients, client_location, room_id, &room, event).await?;
 | |
|     rooms.add(room_id, room).await?;
 | |
| 
 | |
|     let mut result = vec![(id, SendShipPacket::JoinRoom(join_room))];
 | |
|     if let Ok(leader) = client_location.get_area_leader(area).await {
 | |
|         let leave_lobby = SendShipPacket::LeaveLobby(LeaveLobby::new(old_area_client.local_client.id(), leader.local_client.id()));
 | |
|         result.extend(lobby_neighbors
 | |
|                       .into_iter()
 | |
|                       .map(move |c| {
 | |
|                           (c.client, leave_lobby.clone())
 | |
|                       }));
 | |
|     }
 | |
| 
 | |
|     Ok(result)
 | |
| }
 | |
| 
 | |
| pub async fn room_name_request(id: ClientId,
 | |
|                                client_location: &ClientLocation,
 | |
|                                rooms: &Rooms)
 | |
|                                -> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
 | |
|     let area = client_location.get_area(id).await?;
 | |
|     match area {
 | |
|         RoomLobby::Room(room) => {
 | |
|             rooms.with(room, |room| Box::pin(async move {
 | |
|                 vec![(id, SendShipPacket::RoomNameResponse(RoomNameResponse {
 | |
|                     name: room.name.clone()
 | |
|                 }))]
 | |
|             })).await
 | |
|         },
 | |
|         RoomLobby::Lobby(_) => Err(GetAreaError::NotInRoom.into())
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[allow(clippy::too_many_arguments)]
 | |
| pub async fn join_room<EG>(id: ClientId,
 | |
|                            pkt: MenuSelect,
 | |
|                            entity_gateway: &mut EG,
 | |
|                            client_location: &mut ClientLocation,
 | |
|                            clients: &Clients,
 | |
|                            item_state: &mut ItemState,
 | |
|                            rooms: &Rooms,
 | |
|                            event: ShipEvent)
 | |
|                            -> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
 | |
| where
 | |
|     EG: EntityGateway + Clone + 'static,
 | |
| {
 | |
|     let room_id = RoomId(pkt.item as usize);
 | |
|     if !rooms.exists(room_id).await {
 | |
|         return Ok(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("This room no longer exists!".into())))])
 | |
|     }
 | |
|     let level = clients.with(id, |client| Box::pin(async move {
 | |
|         LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp)
 | |
|     })).await?;
 | |
|     let (difficulty, bursting, room_entity_id) = rooms.with(room_id, |room| Box::pin(async move {
 | |
|         (room.mode.difficulty(), room.bursting, room.room_id)
 | |
|     })).await?;
 | |
| 
 | |
|     match difficulty {
 | |
|         Difficulty::Ultimate if level < 80 => {
 | |
|             return Ok(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 80 \nto join Ultimate rooms.".into())))])
 | |
|         },
 | |
|         Difficulty::VeryHard if level < 40 => {
 | |
|             return Ok(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 40 \nto join Very Hard rooms.".into())))])
 | |
|         },
 | |
|         Difficulty::Hard if level < 20 => {
 | |
|             return Ok(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 20 \nto join Hard rooms.".into())))])
 | |
|         },
 | |
|         _ => {},
 | |
|     }
 | |
| 
 | |
|     if bursting {
 | |
|         return Ok(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("player is bursting\nplease wait".into())))])
 | |
|     }
 | |
| 
 | |
|     let original_area = client_location.get_area(id).await?;
 | |
|     let original_neighbors = client_location.get_client_neighbors(id).await?;
 | |
|     let original_room_clients = client_location.get_clients_in_room(room_id).await?;
 | |
|     client_location.add_client_to_room(id, room_id).await?;
 | |
|     let area_client = client_location.get_local_client(id).await?;
 | |
|     let room_leader = client_location.get_room_leader(room_id).await?;
 | |
| 
 | |
|     clients.with(id, |client| {
 | |
|         let mut item_state = item_state.clone();
 | |
|         let mut entity_gateway = entity_gateway.clone();
 | |
|         Box::pin(async move {
 | |
|             entity_gateway.add_room_note(room_entity_id, RoomNote::PlayerJoin {
 | |
|                 character_id: client.character.id,
 | |
|             }).await?;
 | |
|             item_state.add_character_to_room(room_id, &client.character, area_client).await;
 | |
|             Ok::<_, anyhow::Error>(())
 | |
|         })}).await??;
 | |
| 
 | |
|     let join_room = rooms.with(room_id, |room| {
 | |
|         let clients = clients.clone();
 | |
|         let client_location = client_location.clone();
 | |
|         Box::pin(async move {
 | |
|             builder::room::join_room(id, &clients, &client_location, room_id, room, event).await
 | |
|         })}).await??;
 | |
|     let add_to = clients.with(id, |client| {
 | |
|         let item_state = item_state.clone();
 | |
|         Box::pin(async move {
 | |
|             builder::room::add_to_room(id, client, &area_client, &room_leader, &item_state, event).await
 | |
|         })}).await??;
 | |
| 
 | |
|     rooms.with_mut(room_id, |room| Box::pin(async move {
 | |
|         room.bursting = true;
 | |
|     })).await?;
 | |
| 
 | |
|     Ok(vec![(id, SendShipPacket::JoinRoom(join_room))]
 | |
|        .into_iter()
 | |
|        .chain(original_room_clients.into_iter()
 | |
|               .map(move |c| (c.client, SendShipPacket::AddToRoom(add_to.clone()))))
 | |
|        .chain(futures::stream::iter(original_neighbors.into_iter())
 | |
|               .filter_map(|c| {
 | |
|                   let client_location = client_location.clone();
 | |
|                   async move {
 | |
|                       client_location.get_area_leader(original_area).await.ok().map(|leader| {
 | |
|                           let leave_lobby = SendShipPacket::LeaveLobby(LeaveLobby::new(area_client.local_client.id(), leader.local_client.id()));
 | |
|                           (c.client, leave_lobby)
 | |
|                       })
 | |
|                   }
 | |
|               })
 | |
|               .collect::<Vec<_>>()
 | |
|               .await
 | |
|        )
 | |
|        .collect())
 | |
| }
 | |
| 
 | |
| pub async fn done_bursting(id: ClientId,
 | |
|                            client_location: &ClientLocation,
 | |
|                            rooms: &Rooms)
 | |
|                            -> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
 | |
|     let room_id = client_location.get_room(id).await?;
 | |
|     let rare_monster_list = rooms.with_mut(room_id, |room| Box::pin(async move {
 | |
|         room.bursting = false;
 | |
|         room.maps.get_rare_monster_list()
 | |
|     })).await?;
 | |
| 
 | |
|     let area_client = client_location.get_local_client(id).await?;
 | |
|     Ok(client_location.get_client_neighbors(id).await?.into_iter()
 | |
|         .map(move |client| {
 | |
|             (client.client, SendShipPacket::Message(Message::new(GameMessage::BurstDone(BurstDone {
 | |
|                 client: area_client.local_client.id(),
 | |
|                 target: 0
 | |
|             }))))
 | |
|         })
 | |
|         .chain(std::iter::once_with(move || {
 | |
|             (id, SendShipPacket::RareMonsterList(builder::room::build_rare_monster_list(rare_monster_list)))
 | |
|         }))
 | |
|         .collect())
 | |
| }
 | |
| 
 | |
| pub async fn request_room_list(id: ClientId,
 | |
|                                client_location: &ClientLocation,
 | |
|                                rooms: &Rooms)
 | |
|                                -> Vec<(ClientId, SendShipPacket)> {
 | |
|     let active_room_list = rooms.stream()
 | |
|         .enumerate()
 | |
|         .filter_map(|(i, r)| async move {
 | |
|             r.as_ref().map(|room| {
 | |
|                 let difficulty = room.get_difficulty_for_room_list();
 | |
|                 let name = libpso::utf8_to_utf16_array!(room.name, 16);
 | |
|                 let episode = room.get_episode_for_room_list();
 | |
|                 let flags = room.get_flags_for_room_list();
 | |
|                 async move {
 | |
|                     RoomList {
 | |
|                         menu_id: ROOM_MENU_ID,
 | |
|                         item_id: i as u32,
 | |
|                         difficulty,
 | |
|                         players: client_location.get_clients_in_room(RoomId(i)).await.unwrap().len() as u8,
 | |
|                         name,
 | |
|                         episode,
 | |
|                         flags,
 | |
|                     }
 | |
|                 }})
 | |
|         });
 | |
|     let baseroom: RoomList = RoomList {
 | |
|         menu_id: ROOM_MENU_ID,
 | |
|         item_id: ROOM_MENU_ID,
 | |
|         difficulty: 0x00,
 | |
|         players: 0x00,
 | |
|         name: libpso::utf8_to_utf16_array!("Room list menu", 16),
 | |
|         episode: 0,
 | |
|         flags: 0,
 | |
|     };
 | |
| 
 | |
|     vec![(id, SendShipPacket::RoomListResponse(RoomListResponse {
 | |
|         baseroom,
 | |
|         rooms: futures::future::join_all(active_room_list.collect::<Vec<_>>().await).await
 | |
|     }))]
 | |
| }
 | |
| 
 | |
| pub async fn cool_62(id: ClientId,
 | |
|                      cool_62: Like62ButCooler,
 | |
|                      client_location: &ClientLocation)
 | |
|                      -> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
 | |
|     let target = cool_62.flag as u8;
 | |
|     let cool_62 = cool_62.clone();
 | |
|     Ok(client_location
 | |
|         .get_client_neighbors(id)
 | |
|         .await?
 | |
|         .into_iter()
 | |
|         .filter(move |client| client.local_client.id() == target)
 | |
|         .map(move |client| {
 | |
|             (client.client, SendShipPacket::Like62ButCooler(cool_62.clone()))
 | |
|         })
 | |
|         .collect())
 | |
| }
 |