Merge pull request 'holiday rappies (and other events)' (#122) from holidays into master
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				continuous-integration/drone/push Build is passing
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	continuous-integration/drone/push Build is passing
				
			Reviewed-on: #122
This commit is contained in:
		
						commit
						32dddfd9bd
					
				
							
								
								
									
										21
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										21
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -609,7 +609,6 @@ dependencies = [ | ||||
|  "derive_more", | ||||
|  "enum-utils", | ||||
|  "fern", | ||||
|  "fix-hidden-lifetime-bug", | ||||
|  "futures", | ||||
|  "lazy_static", | ||||
|  "libpso", | ||||
| @ -692,26 +691,6 @@ dependencies = [ | ||||
|  "log", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "fix-hidden-lifetime-bug" | ||||
| version = "0.2.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d4ae9c2016a663983d4e40a9ff967d6dcac59819672f0b47f2b17574e99c33c8" | ||||
| dependencies = [ | ||||
|  "fix-hidden-lifetime-bug-proc_macros", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "fix-hidden-lifetime-bug-proc_macros" | ||||
| version = "0.2.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "e4c81935e123ab0741c4c4f0d9b8377e5fb21d3de7e062fa4b1263b1fbcba1ea" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "foreign-types" | ||||
| version = "0.3.2" | ||||
|  | ||||
| @ -33,4 +33,3 @@ sqlx = { version = "0.5.10", features = ["runtime-async-std-native-tls", "postgr | ||||
| strum = "0.19.5" | ||||
| strum_macros = "0.19" | ||||
| anyhow = { version = "1.0.47", features = ["backtrace"] } | ||||
| fix-hidden-lifetime-bug = "0.2.4" | ||||
|  | ||||
| @ -5,7 +5,7 @@ use elseware::common::interserver::AuthToken; | ||||
| use elseware::login::login::LoginServerState; | ||||
| use elseware::login::character::CharacterServerState; | ||||
| use elseware::patch::patch::{PatchServerState, generate_patch_tree, load_config, load_motd}; | ||||
| use elseware::ship::ship::ShipServerStateBuilder; | ||||
| use elseware::ship::ship::{ShipServerStateBuilder, ShipEvent}; | ||||
| 
 | ||||
| #[allow(unused_imports)] | ||||
| use elseware::entity::gateway::{EntityGateway, InMemoryGateway, PostgresGateway}; | ||||
| @ -364,6 +364,7 @@ fn main() { | ||||
|             .name("US/Sona-Nyl".into()) | ||||
|             .ip(Ipv4Addr::new(127,0,0,1)) | ||||
|             .port(elseware::ship::ship::SHIP_PORT) | ||||
|             .event(ShipEvent::Halloween) | ||||
|             .gateway(entity_gateway.clone()) | ||||
|             .build(); | ||||
|         let sub_ship_state = ship_state.clone(); | ||||
| @ -378,7 +379,8 @@ fn main() { | ||||
|         let ship_state = ShipServerStateBuilder::default() | ||||
|             .name("EU/Dylath-Leen".into()) | ||||
|             .ip(Ipv4Addr::new(127,0,0,1)) | ||||
|             .port(elseware::ship::ship::SHIP_PORT+200) | ||||
|             .port(elseware::ship::ship::SHIP_PORT+2000) | ||||
|             .event(ShipEvent::Christmas) | ||||
|             .gateway(entity_gateway.clone()) | ||||
|             .build(); | ||||
|         let sub_ship_state = ship_state.clone(); | ||||
|  | ||||
| @ -205,6 +205,7 @@ where | ||||
|     loop { | ||||
|         match packet_queue.recv().await { | ||||
|             Ok(pkt) => { | ||||
|                 info!("[send to {:?}] {:#?}", client_id, pkt); | ||||
|                 if let Err(err) = send_pkt(&mut socket, &mut cipher, &pkt).await { | ||||
|                     warn!("error sending pkt {:#?} to {:?} {:?}", pkt, client_id, err); | ||||
|                 } | ||||
|  | ||||
| @ -7,8 +7,6 @@ | ||||
| #![feature(test)] | ||||
| 
 | ||||
| extern crate test; | ||||
| extern crate fix_hidden_lifetime_bug; | ||||
| 
 | ||||
| 
 | ||||
| pub mod common; | ||||
| pub mod entity; | ||||
|  | ||||
| @ -153,13 +153,9 @@ pub struct ClientLocation { | ||||
| 
 | ||||
| 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: [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())), | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -5,6 +5,7 @@ use std::collections::HashMap; | ||||
| use byteorder::{LittleEndian, ReadBytesExt}; | ||||
| use thiserror::Error; | ||||
| 
 | ||||
| use crate::ship::ship::ShipEvent; | ||||
| use crate::ship::monster::MonsterType; | ||||
| use crate::ship::room::Episode; | ||||
| 
 | ||||
| @ -87,10 +88,9 @@ impl RareMonsterAppearTable { | ||||
| 
 | ||||
|         let appear_rates: HashMap<MonsterType, f32> = cfg | ||||
|             .into_iter() | ||||
|             .map(|(monster, rate)| { | ||||
|                 let monster: MonsterType = monster.parse().unwrap(); // TODO: don't unwrap!
 | ||||
|                 let appear_rate = rate; | ||||
|                 (monster, appear_rate) | ||||
|             .filter_map(|(monster, rate)| { | ||||
|                 let monster: MonsterType = monster.parse().ok()?; | ||||
|                 Some((monster, rate)) | ||||
|             }) | ||||
|             .collect(); | ||||
| 
 | ||||
| @ -99,11 +99,8 @@ impl RareMonsterAppearTable { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn roll_appearance(&self, monster: &MonsterType) -> bool { | ||||
|         if rand_chacha::ChaChaRng::from_entropy().gen::<f32>() < *self.appear_rate.get(monster).unwrap_or(&0.0f32) { | ||||
|             return true | ||||
|         } | ||||
|         false | ||||
|     pub fn roll_is_rare(&self, monster: &MonsterType) -> bool { | ||||
|         rand_chacha::ChaChaRng::from_entropy().gen::<f32>() < *self.appear_rate.get(monster).unwrap_or(&0.0f32) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -324,7 +321,7 @@ impl MapEnemy { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn has_rare_appearance(self) -> bool { | ||||
|     pub fn can_be_rare(&self) -> bool { | ||||
|         matches!(self.monster, | ||||
|             MonsterType::RagRappy | MonsterType::Hildebear | | ||||
|             MonsterType::PoisonLily | MonsterType::PofuillySlime | | ||||
| @ -339,32 +336,26 @@ impl MapEnemy { | ||||
|     guaranteed rare monsters don't count towards the limit | ||||
|     */ | ||||
|     #[must_use] | ||||
|     pub fn set_rare_appearance(self) -> MapEnemy { | ||||
|         match (self.monster, self.map_area.to_episode()) { | ||||
|             (MonsterType::RagRappy, Episode::One) => {MapEnemy {monster: MonsterType::AlRappy, shiny:true, ..self}}, | ||||
|             (MonsterType::RagRappy, Episode::Two) => {MapEnemy {monster: MonsterType::EventRappy, shiny:true, ..self}}, | ||||
|             (MonsterType::Hildebear, _) => {MapEnemy {monster: MonsterType::Hildeblue, shiny:true, ..self}}, | ||||
|             (MonsterType::PoisonLily, _) => {MapEnemy {monster: MonsterType::NarLily, shiny:true, ..self}}, | ||||
|             (MonsterType::PofuillySlime, _) => {MapEnemy {monster: MonsterType::PouillySlime, shiny:true, ..self}}, | ||||
|             (MonsterType::SandRappyCrater, _) => {MapEnemy {monster: MonsterType::DelRappyCrater, shiny:true, ..self}}, | ||||
|             (MonsterType::ZuCrater, _) => {MapEnemy {monster: MonsterType::PazuzuCrater, shiny:true, ..self}}, | ||||
|             (MonsterType::Dorphon, _) => {MapEnemy {monster: MonsterType::DorphonEclair, shiny:true, ..self}}, | ||||
|             (MonsterType::SandRappyDesert, _) => {MapEnemy {monster: MonsterType::DelRappyDesert, shiny:true, ..self}}, | ||||
|             (MonsterType::ZuDesert, _) => {MapEnemy {monster: MonsterType::PazuzuDesert, shiny:true, ..self}}, | ||||
|             (MonsterType::MerissaA, _) => {MapEnemy {monster: MonsterType::MerissaAA, shiny:true, ..self}}, | ||||
|             (MonsterType::SaintMillion, _) => {MapEnemy {monster: MonsterType::Kondrieu, shiny:true, ..self}}, | ||||
|             (MonsterType::Shambertin, _) => {MapEnemy {monster: MonsterType::Kondrieu, shiny:true, ..self}}, | ||||
|     pub fn into_rare(self, event: ShipEvent) -> MapEnemy { | ||||
|         match (self.monster, self.map_area.to_episode(), event) { | ||||
|             (MonsterType::RagRappy, Episode::One, _) => {MapEnemy {monster: MonsterType::AlRappy, shiny:true, ..self}}, | ||||
|             (MonsterType::RagRappy, Episode::Two, ShipEvent::Easter) => {MapEnemy {monster: MonsterType::EasterRappy, shiny:true, ..self}}, | ||||
|             (MonsterType::RagRappy, Episode::Two, ShipEvent::Halloween) => {MapEnemy {monster: MonsterType::HalloRappy, shiny:true, ..self}}, | ||||
|             (MonsterType::RagRappy, Episode::Two, ShipEvent::Christmas) => {MapEnemy {monster: MonsterType::StRappy, shiny:true, ..self}}, | ||||
|             (MonsterType::RagRappy, Episode::Two, _) => {MapEnemy {monster: MonsterType::LoveRappy, shiny:true, ..self}}, | ||||
|             (MonsterType::Hildebear, _, _) => {MapEnemy {monster: MonsterType::Hildeblue, shiny:true, ..self}}, | ||||
|             (MonsterType::PoisonLily, _, _) => {MapEnemy {monster: MonsterType::NarLily, shiny:true, ..self}}, | ||||
|             (MonsterType::PofuillySlime, _, _) => {MapEnemy {monster: MonsterType::PouillySlime, shiny:true, ..self}}, | ||||
|             (MonsterType::SandRappyCrater, _, _) => {MapEnemy {monster: MonsterType::DelRappyCrater, shiny:true, ..self}}, | ||||
|             (MonsterType::ZuCrater, _, _) => {MapEnemy {monster: MonsterType::PazuzuCrater, shiny:true, ..self}}, | ||||
|             (MonsterType::Dorphon, _, _) => {MapEnemy {monster: MonsterType::DorphonEclair, shiny:true, ..self}}, | ||||
|             (MonsterType::SandRappyDesert, _, _) => {MapEnemy {monster: MonsterType::DelRappyDesert, shiny:true, ..self}}, | ||||
|             (MonsterType::ZuDesert, _, _) => {MapEnemy {monster: MonsterType::PazuzuDesert, shiny:true, ..self}}, | ||||
|             (MonsterType::MerissaA, _, _) => {MapEnemy {monster: MonsterType::MerissaAA, shiny:true, ..self}}, | ||||
|             (MonsterType::SaintMillion, _, _) => {MapEnemy {monster: MonsterType::Kondrieu, shiny:true, ..self}}, | ||||
|             (MonsterType::Shambertin, _, _) => {MapEnemy {monster: MonsterType::Kondrieu, shiny:true, ..self}}, | ||||
|             _ => {self}, | ||||
|         } | ||||
|     } | ||||
|     
 | ||||
|     // in theory this should only be called on monsters we know can have rare types
 | ||||
|     #[must_use] | ||||
|     pub fn roll_appearance_for_mission(self, rare_monster_table: &RareMonsterAppearTable) -> MapEnemy { | ||||
|         if rare_monster_table.roll_appearance(&self.monster) { | ||||
|             return self.set_rare_appearance() | ||||
|         } | ||||
|         self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -6,6 +6,7 @@ use std::fs::File; | ||||
| 
 | ||||
| use thiserror::Error; | ||||
| 
 | ||||
| use crate::ship::ship::ShipEvent; | ||||
| use crate::ship::monster::MonsterType; | ||||
| use crate::ship::room::{Episode, RoomMode}; | ||||
| 
 | ||||
| @ -186,7 +187,7 @@ pub struct Maps { | ||||
| } | ||||
| 
 | ||||
| impl Maps { | ||||
|     pub fn new(room_mode: RoomMode, rare_monster_table: &enemy::RareMonsterAppearTable) -> Maps { | ||||
|     pub fn new(room_mode: RoomMode, rare_monster_table: &enemy::RareMonsterAppearTable, event: ShipEvent) -> Maps { | ||||
|         let map_variants = match (room_mode.episode(), room_mode.single_player()) { | ||||
|             (Episode::One, 0) => { | ||||
|                 vec![MapVariant::new(MapArea::Pioneer2Ep1, MapVariantMode::Online), | ||||
| @ -278,20 +279,19 @@ impl Maps { | ||||
|             _ => unreachable!() | ||||
|         }; | ||||
| 
 | ||||
|         let mut maps = Maps { | ||||
|         Maps { | ||||
|             enemy_data: map_variants.iter() | ||||
|                         .fold(Vec::new(), |mut enemy_data, map_variant| { | ||||
|                             enemy_data.append(&mut enemy_data_from_map_data(map_variant, &room_mode.episode())); | ||||
|                             enemy_data | ||||
|                         }), | ||||
|                 .flat_map(|map_variant| { | ||||
|                     enemy_data_from_map_data(map_variant, &room_mode.episode()) | ||||
|                 }) | ||||
|                 .map(|enemy| apply_rare_enemy(enemy, rare_monster_table, event)) | ||||
|                 .collect(), | ||||
|             object_data: map_variants.iter() | ||||
|                 .flat_map(|map_variant| { | ||||
|                     objects_from_map_data(map_variant.obj_file().into(), &room_mode.episode(), &map_variant.map) | ||||
|                 }).collect(), | ||||
|             map_variants, | ||||
|         }; | ||||
|         maps.roll_monster_appearance(rare_monster_table); | ||||
|         maps | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn enemy_by_id(&self, id: usize) -> Result<MapEnemy, MapsError> { | ||||
| @ -314,9 +314,16 @@ impl Maps { | ||||
|             }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn set_quest_data(&mut self, enemies: Vec<Option<MapEnemy>>, objects: Vec<Option<MapObject>>, rare_monster_appear_table: &RareMonsterAppearTable) { | ||||
|         self.enemy_data = enemies; | ||||
|         self.roll_monster_appearance(rare_monster_appear_table); | ||||
|     pub fn set_quest_data(&mut self, | ||||
|                           enemies: Vec<Option<MapEnemy>>, | ||||
|                           objects: Vec<Option<MapObject>>, | ||||
|                           rare_monster_table: &RareMonsterAppearTable, | ||||
|                           event: ShipEvent) | ||||
|     { | ||||
|         self.enemy_data = enemies | ||||
|             .into_iter() | ||||
|             .map(|enemy| apply_rare_enemy(enemy, rare_monster_table, event)) | ||||
|             .collect(); | ||||
|         self.object_data = objects; | ||||
|     } | ||||
| 
 | ||||
| @ -325,10 +332,11 @@ impl Maps { | ||||
|         let shiny: Vec<(usize, &Option<MapEnemy>)> = self.enemy_data.iter() | ||||
|             .enumerate() | ||||
|             .filter(|(_,m)| { | ||||
|                 if m.is_some() { | ||||
|                     m.unwrap().shiny | ||||
|                 } else { | ||||
|                     false | ||||
|                 match m { | ||||
|                     Some(m) => { | ||||
|                         m.shiny | ||||
|                     }, | ||||
|                     None => false, | ||||
|                 } | ||||
|             }) | ||||
|             .collect(); | ||||
| @ -341,21 +349,15 @@ impl Maps { | ||||
|         } | ||||
|         rare_monsters | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|     pub fn roll_monster_appearance(&mut self, rare_monster_table: &RareMonsterAppearTable) { | ||||
|         self.enemy_data = self.enemy_data | ||||
|                             .iter() | ||||
|                             .map(|&x| | ||||
|                                 if let Some(monster) = x { | ||||
|                                     if monster.has_rare_appearance() { | ||||
|                                         Some(monster.roll_appearance_for_mission(rare_monster_table)) | ||||
|                                     } else { | ||||
|                                         Some(monster) | ||||
| fn apply_rare_enemy(enemy: Option<MapEnemy>, rare_enemy_table: &RareMonsterAppearTable, event: ShipEvent) -> Option<MapEnemy> { | ||||
|     enemy.map(|enemy| { | ||||
|         if enemy.can_be_rare() && rare_enemy_table.roll_is_rare(&enemy.monster) { | ||||
|             enemy.into_rare(event) | ||||
|         } | ||||
|                                 } else { | ||||
|                                     x | ||||
|                                 } | ||||
|                             ) | ||||
|                             .collect(); | ||||
|         else { | ||||
|             enemy | ||||
|         } | ||||
|     }) | ||||
| } | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| use libpso::packet::ship::*; | ||||
| use crate::common::serverstate::ClientId; | ||||
| use crate::ship::ship::{ShipError, Clients}; | ||||
| use crate::ship::ship::{ShipError, Clients, ShipEvent}; | ||||
| use crate::ship::location::{ClientLocation, LobbyId, ClientLocationError}; | ||||
| use crate::ship::packet::builder::{player_info}; | ||||
| use crate::ship::items::state::ItemState; | ||||
| @ -11,7 +11,8 @@ pub async fn join_lobby(id: ClientId, | ||||
|                         lobby: LobbyId, | ||||
|                         client_location: &ClientLocation, | ||||
|                         clients: &Clients, | ||||
|                         item_state: &ItemState) | ||||
|                         item_state: &ItemState, | ||||
|                         event: ShipEvent) | ||||
|                         -> Result<JoinLobby, ShipError> { | ||||
|     let lobby_clients = client_location.get_clients_in_lobby(lobby).await.map_err(|err| -> ClientLocationError { err.into() })?; | ||||
| 
 | ||||
| @ -41,7 +42,7 @@ pub async fn join_lobby(id: ClientId, | ||||
|         one: 1, | ||||
|         lobby: lobby.id(), | ||||
|         block: client_block, | ||||
|         event: 0, | ||||
|         event: event.into(), | ||||
|         padding: 0, | ||||
|         playerinfo, | ||||
|     }) | ||||
| @ -51,7 +52,8 @@ pub async fn add_to_lobby(id: ClientId, | ||||
|                           lobby: LobbyId, | ||||
|                           client_location: &ClientLocation, | ||||
|                           clients: &Clients, | ||||
|                           item_state: &ItemState) | ||||
|                           item_state: &ItemState, | ||||
|                           event: ShipEvent) | ||||
|                           -> Result<AddToLobby, ShipError> { | ||||
|     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() })?; | ||||
| @ -66,7 +68,7 @@ pub async fn add_to_lobby(id: ClientId, | ||||
|                 one: 1, | ||||
|                 lobby: lobby.id(), | ||||
|                 block: client.block as u16, | ||||
|                 event: 0, | ||||
|                 event: event.into(), | ||||
|                 padding: 0, | ||||
|                 playerinfo: player_info(0x100, client, &area_client, &inventory).await, | ||||
|             }) | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| use libpso::packet::ship::*; | ||||
| use crate::common::serverstate::ClientId; | ||||
| use crate::ship::ship::{ShipError, ClientState, Clients}; | ||||
| use crate::ship::ship::{ShipError, ClientState, Clients, ShipEvent}; | ||||
| use crate::ship::location::{ClientLocation, RoomId, AreaClient, ClientLocationError}; | ||||
| use crate::ship::room::RoomState; | ||||
| use crate::ship::items::state::ItemState; | ||||
| @ -13,7 +13,8 @@ pub async fn join_room(id: ClientId, | ||||
|                        clients: &Clients, | ||||
|                        client_location: &ClientLocation, | ||||
|                        room_id: RoomId, | ||||
|                        room: &RoomState) | ||||
|                        room: &RoomState, | ||||
|                        event: ShipEvent) | ||||
|                        -> 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()) | ||||
| @ -40,7 +41,7 @@ pub async fn join_room(id: ClientId, | ||||
|         one: 1, | ||||
|         difficulty: room.mode.difficulty().into(), | ||||
|         battle: room.mode.battle(), | ||||
|         event: 0, | ||||
|         event: event.into(), | ||||
|         section: room.section_id.into(), | ||||
|         challenge: room.mode.challenge(), | ||||
|         random_seed: room.random_seed, | ||||
| @ -57,8 +58,7 @@ pub async fn add_to_room(_id: ClientId, | ||||
|                          area_client: &AreaClient, | ||||
|                          leader: &AreaClient, | ||||
|                          item_state: &ItemState, | ||||
|                    _room_id: RoomId, | ||||
| ) | ||||
|                          event: ShipEvent) | ||||
|                          -> Result<AddToRoom, ShipError> { | ||||
|     let inventory = item_state.get_character_inventory(&client.character).await?; | ||||
|     Ok(AddToRoom { | ||||
| @ -68,7 +68,7 @@ pub async fn add_to_room(_id: ClientId, | ||||
|         one: 0, // TODO: ????????
 | ||||
|         lobby: 0xFF, | ||||
|         block: 0, | ||||
|         event: 0, | ||||
|         event: event.into(), | ||||
|         padding: 0, | ||||
|         playerinfo: player_info(0x10000, client, area_client, &inventory).await, | ||||
|     }) | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| use libpso::packet::ship::*; | ||||
| use crate::common::serverstate::ClientId; | ||||
| use crate::common::leveltable::LEVEL_TABLE; | ||||
| use crate::ship::ship::{SendShipPacket, ShipError, Clients}; | ||||
| use crate::ship::ship::{SendShipPacket, ShipError, Clients, ShipEvent}; | ||||
| use crate::ship::room::Rooms; | ||||
| use crate::ship::character::{FullCharacterBytesBuilder}; | ||||
| use crate::ship::location::{ClientLocation, LobbyId, RoomLobby, ClientLocationError, RoomId}; | ||||
| @ -55,11 +55,12 @@ pub async fn send_player_to_lobby(id: ClientId, | ||||
|                                   _pkt: CharData, | ||||
|                                   client_location: &mut ClientLocation, | ||||
|                                   clients: &Clients, | ||||
|                                   item_state: &ItemState) | ||||
|                                   item_state: &ItemState, | ||||
|                                   event: ShipEvent) | ||||
|                                   -> Result<Vec<(ClientId, SendShipPacket)>, ShipError> { | ||||
|     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 join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_state, event).await?; | ||||
|     let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state, event).await?; | ||||
|     let neighbors = client_location.get_client_neighbors(id).await.unwrap(); | ||||
|     Ok(vec![(id, SendShipPacket::JoinLobby(join_lobby))] | ||||
|        .into_iter() | ||||
| @ -74,7 +75,8 @@ pub async fn change_lobby<EG>(id: ClientId, | ||||
|                               clients: &Clients, | ||||
|                               item_state: &mut ItemState, | ||||
|                               rooms: &Rooms, | ||||
|                               entity_gateway: &mut EG) | ||||
|                               entity_gateway: &mut EG, | ||||
|                               event: ShipEvent) | ||||
|                               -> Result<Vec<(ClientId, SendShipPacket)>, ShipError> | ||||
| where | ||||
|     EG: EntityGateway + Clone + 'static, | ||||
| @ -117,8 +119,8 @@ where | ||||
|         Box::pin(async move { | ||||
|             item_state.load_character(&mut entity_gateway, &client.character).await | ||||
|         })}).await??; | ||||
|     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 join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_state, event).await?; | ||||
|     let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state, event).await?; | ||||
|     let neighbors = client_location.get_client_neighbors(id).await?; | ||||
|     Ok(vec![(id, SendShipPacket::JoinLobby(join_lobby))] | ||||
|        .into_iter() | ||||
| @ -126,6 +128,7 @@ where | ||||
|               .map(|c| (c.client, SendShipPacket::AddToLobby(addto.clone())))) | ||||
|        .chain(old_neighbors.into_iter() | ||||
|               .map(|c| (c.client, SendShipPacket::LeaveLobby(leave_lobby.clone())))) | ||||
|        .chain(std::iter::once((id, SendShipPacket::LobbyEvent(LobbyEvent{ event: event.into()})))) | ||||
|        .collect()) | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -2,7 +2,7 @@ use std::io::{Cursor, Read, Seek, SeekFrom}; | ||||
| use futures::stream::{FuturesOrdered, StreamExt}; | ||||
| use libpso::packet::ship::*; | ||||
| use crate::common::serverstate::ClientId; | ||||
| use crate::ship::ship::{SendShipPacket, ShipError, Clients}; | ||||
| use crate::ship::ship::{SendShipPacket, ShipError, Clients, ShipEvent}; | ||||
| use crate::ship::room::Rooms; | ||||
| use crate::ship::location::{ClientLocation, ClientLocationError}; | ||||
| use crate::ship::packet::builder::quest; | ||||
| @ -94,7 +94,8 @@ pub async fn player_chose_quest(id: ClientId, | ||||
|                                 questmenuselect: QuestMenuSelect, | ||||
|                                 clients: &Clients, | ||||
|                                 client_location: &ClientLocation, | ||||
|                                 rooms: &Rooms) | ||||
|                                 rooms: &Rooms, | ||||
|                                 event: ShipEvent) | ||||
|                                 -> Result<Vec<(ClientId, SendShipPacket)>, ShipError> { | ||||
|     let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?; | ||||
| 
 | ||||
| @ -115,7 +116,7 @@ pub async fn player_chose_quest(id: ClientId, | ||||
|                 .clone(); | ||||
| 
 | ||||
|             let rare_monster_drops = room.rare_monster_table.clone(); | ||||
|             room.maps.set_quest_data(quest.enemies.clone(), quest.objects.clone(), &rare_monster_drops); | ||||
|             room.maps.set_quest_data(quest.enemies.clone(), quest.objects.clone(), &rare_monster_drops, event); | ||||
|             room.map_areas = quest.map_areas.clone(); | ||||
| 
 | ||||
|             let bin = quest::quest_header(&questmenuselect, &quest.bin_blob, "bin"); | ||||
|  | ||||
| @ -5,7 +5,7 @@ use libpso::packet::ship::*; | ||||
| use libpso::packet::messages::*; | ||||
| use crate::common::serverstate::ClientId; | ||||
| use crate::common::leveltable::LEVEL_TABLE; | ||||
| use crate::ship::ship::{SendShipPacket, ShipError, Clients}; | ||||
| use crate::ship::ship::{SendShipPacket, ShipError, Clients, ShipEvent}; | ||||
| use crate::ship::room::Rooms; | ||||
| use crate::ship::location::{ClientLocation, RoomId, RoomLobby, GetAreaError}; | ||||
| use crate::ship::packet::builder; | ||||
| @ -17,7 +17,8 @@ pub async fn create_room(id: ClientId, | ||||
|                          client_location: &mut ClientLocation, | ||||
|                          clients: &Clients, | ||||
|                          item_state: &mut ItemState, | ||||
|                          rooms: &Rooms) | ||||
|                          rooms: &Rooms, | ||||
|                          event: ShipEvent) | ||||
|                          -> Result<Vec<(ClientId, SendShipPacket)>, ShipError> { | ||||
|     let level = clients.with(id, |client| Box::pin(async move { | ||||
|         LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp) | ||||
| @ -44,12 +45,12 @@ pub async fn create_room(id: ClientId, | ||||
|         let mut item_state = item_state.clone(); | ||||
|         Box::pin(async move { | ||||
|             item_state.add_character_to_room(room_id, &client.character, area_client).await; | ||||
|             let mut room = room::RoomState::from_create_room(&create_room, client.character.section_id)?; | ||||
|             let mut room = room::RoomState::from_create_room(&create_room, client.character.section_id, event)?; | ||||
|             room.bursting = true; | ||||
|             Ok::<_, ShipError>(room) | ||||
|         })}).await??; | ||||
| 
 | ||||
|     let join_room = builder::room::join_room(id, clients, client_location, room_id, &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))]; | ||||
| @ -87,7 +88,8 @@ pub async fn join_room(id: ClientId, | ||||
|                        client_location: &mut ClientLocation, | ||||
|                        clients: &Clients, | ||||
|                        item_state: &mut ItemState, | ||||
|                        rooms: &Rooms) | ||||
|                        rooms: &Rooms, | ||||
|                        event: ShipEvent) | ||||
|                        -> Result<Vec<(ClientId, SendShipPacket)>, ShipError> { | ||||
|     let room_id = RoomId(pkt.item as usize); | ||||
|     if !rooms.exists(room_id).await { | ||||
| @ -134,12 +136,12 @@ pub async fn join_room(id: ClientId, | ||||
|         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).await | ||||
|             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, room_id).await | ||||
|             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 { | ||||
|  | ||||
| @ -15,7 +15,7 @@ use crate::ship::monster::{load_monster_stats_table, MonsterType, MonsterStats}; | ||||
| use crate::ship::map::area::MapAreaLookup; | ||||
| use crate::ship::map::enemy::RareMonsterAppearTable; | ||||
| use crate::ship::quests; | ||||
| use crate::ship::ship::ShipError; | ||||
| use crate::ship::ship::{ShipError, ShipEvent}; | ||||
| use crate::ship::location::{MAX_ROOMS, RoomId}; | ||||
| 
 | ||||
| 
 | ||||
| @ -343,7 +343,7 @@ impl RoomState { | ||||
|         self.quest_group = QuestCategoryType::from(group); | ||||
|     } | ||||
| 
 | ||||
|     pub fn from_create_room(create_room: &libpso::packet::ship::CreateRoom, section_id: SectionID) -> Result<RoomState, RoomCreationError> { | ||||
|     pub fn from_create_room(create_room: &libpso::packet::ship::CreateRoom, section_id: SectionID, event: ShipEvent) -> Result<RoomState, RoomCreationError> { | ||||
|         if [create_room.battle, create_room.challenge, create_room.single_player].iter().sum::<u8>() > 1 { | ||||
|             return Err(RoomCreationError::InvalidMode) | ||||
|         } | ||||
| @ -410,7 +410,7 @@ impl RoomState { | ||||
|             rare_monster_table: Box::new(rare_monster_table.clone()), | ||||
|             name: String::from_utf16_lossy(&create_room.name).trim_matches(char::from(0)).into(), | ||||
|             password: create_room.password, | ||||
|             maps: Maps::new(room_mode, &rare_monster_table), // TODO: rare_monster_table here feels janky. is there some way to call the the RoomState.rare_monster_table we already created?
 | ||||
|             maps: Maps::new(room_mode, &rare_monster_table, event), | ||||
|             section_id, | ||||
|             drop_table: Box::new(DropTable::new(room_mode.episode(), room_mode.difficulty(), section_id)), | ||||
|             bursting: false, | ||||
|  | ||||
| @ -42,6 +42,57 @@ pub const SHIP_PORT: u16 = 23423; | ||||
| pub const QUEST_CATEGORY_MENU_ID: u32 = 0xA2; | ||||
| pub const QUEST_SELECT_MENU_ID: u32 = 0xA3; | ||||
| 
 | ||||
| #[derive(Clone, Copy)] | ||||
| pub enum ShipEvent { | ||||
|     None, | ||||
|     Christmas, | ||||
|     Valentines, | ||||
|     Easter, | ||||
|     Halloween, | ||||
|     Sonic, | ||||
|     NewYear, | ||||
|     Summer, | ||||
|     White, | ||||
|     Wedding, | ||||
|     Fall, | ||||
|     Spring, | ||||
|     Summer2, | ||||
|     Spring2, | ||||
| } | ||||
| 
 | ||||
| impl From<ShipEvent> for u32 { | ||||
|     fn from(other: ShipEvent) -> u32 { | ||||
|         u16::from(other) as u32 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<ShipEvent> for u16 { | ||||
|     fn from(other: ShipEvent) -> u16 { | ||||
|         u8::from(other) as u16 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<ShipEvent> for u8 { | ||||
|     fn from(other: ShipEvent) -> u8 { | ||||
|         match other { | ||||
|             ShipEvent::None => 0, | ||||
|             ShipEvent::Christmas => 1, | ||||
|             ShipEvent::Valentines => 3, | ||||
|             ShipEvent::Easter => 4, | ||||
|             ShipEvent::Halloween => 5, | ||||
|             ShipEvent::Sonic => 6, | ||||
|             ShipEvent::NewYear => 7, | ||||
|             ShipEvent::Summer => 8, | ||||
|             ShipEvent::White => 9, | ||||
|             ShipEvent::Wedding => 10, | ||||
|             ShipEvent::Fall => 11, | ||||
|             ShipEvent::Spring => 12, | ||||
|             ShipEvent::Summer2 => 13, | ||||
|             ShipEvent::Spring2 => 14, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #[derive(Error, Debug)] | ||||
| pub enum ShipError { | ||||
| @ -240,6 +291,7 @@ pub enum SendShipPacket { | ||||
|     AcknowledgeTrade(AcknowledgeTrade), | ||||
|     CancelTrade(CancelTrade), | ||||
|     TradeSuccessful(TradeSuccessful), | ||||
|     LobbyEvent(LobbyEvent), | ||||
| } | ||||
| 
 | ||||
| impl SendServerPacket for SendShipPacket { | ||||
| @ -282,6 +334,7 @@ impl SendServerPacket for SendShipPacket { | ||||
|             SendShipPacket::AcknowledgeTrade(pkt) => pkt.as_bytes(), | ||||
|             SendShipPacket::CancelTrade(pkt) => pkt.as_bytes(), | ||||
|             SendShipPacket::TradeSuccessful(pkt) => pkt.as_bytes(), | ||||
|             SendShipPacket::LobbyEvent(pkt) => pkt.as_bytes(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -321,6 +374,7 @@ pub struct ShipServerStateBuilder<EG: EntityGateway + Clone + 'static> { | ||||
|     ip: Option<Ipv4Addr>, | ||||
|     port: Option<u16>, | ||||
|     auth_token: Option<AuthToken>, | ||||
|     event: Option<ShipEvent>, | ||||
|     num_blocks: usize, | ||||
| } | ||||
| 
 | ||||
| @ -332,6 +386,7 @@ impl<EG: EntityGateway + Clone + 'static> Default for ShipServerStateBuilder<EG> | ||||
|             ip: None, | ||||
|             port: None, | ||||
|             auth_token: None, | ||||
|             event: None, | ||||
|             num_blocks: 2, | ||||
|         } | ||||
|     } | ||||
| @ -368,6 +423,12 @@ impl<EG: EntityGateway + Clone + 'static> ShipServerStateBuilder<EG> { | ||||
|         self | ||||
|     } | ||||
| 
 | ||||
|     #[must_use] | ||||
|     pub fn event(mut self, event: ShipEvent) -> ShipServerStateBuilder<EG> { | ||||
|         self.event = Some(event); | ||||
|         self | ||||
|     } | ||||
| 
 | ||||
|     #[must_use] | ||||
|     pub fn blocks(mut self, num_blocks: usize) -> ShipServerStateBuilder<EG> { | ||||
|         self.num_blocks = num_blocks; | ||||
| @ -385,6 +446,7 @@ impl<EG: EntityGateway + Clone + 'static> ShipServerStateBuilder<EG> { | ||||
|             port: self.port.unwrap_or(SHIP_PORT), | ||||
|             shops: ItemShops::default(), | ||||
|             blocks: Blocks(blocks), | ||||
|             event: self.event.unwrap_or(ShipEvent::None), | ||||
| 
 | ||||
|             auth_token: self.auth_token.unwrap_or_else(|| AuthToken("".into())), | ||||
|             ship_list: Vec::new(), | ||||
| @ -424,6 +486,7 @@ pub struct ShipServerState<EG: EntityGateway + Clone + 'static> { | ||||
|     item_state: items::state::ItemState, | ||||
|     shops: ItemShops, | ||||
|     pub blocks: Blocks, | ||||
|     event: ShipEvent, | ||||
| 
 | ||||
|     ip: Ipv4Addr, | ||||
|     port: u16, | ||||
| @ -611,14 +674,14 @@ impl<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> { | ||||
|                         let select_block = handler::lobby::block_selected(id, menuselect, &self.clients, &self.item_state).await?.into_iter(); | ||||
|                         leave_lobby.chain(select_block).collect() | ||||
|                     } | ||||
|                     ROOM_MENU_ID => handler::room::join_room(id, menuselect, &mut block.client_location, &self.clients, &mut self.item_state, &block.rooms).await?, | ||||
|                     ROOM_MENU_ID => handler::room::join_room(id, menuselect, &mut block.client_location, &self.clients, &mut self.item_state, &block.rooms, self.event).await?, | ||||
|                     QUEST_CATEGORY_MENU_ID => handler::quest::select_quest_category(id, menuselect, &block.client_location, &block.rooms).await?, | ||||
|                     _ => unreachable!(), | ||||
|                 } | ||||
|             }, | ||||
|             RecvShipPacket::QuestMenuSelect(questmenuselect) => { | ||||
|                 let block = self.blocks.get_from_client(id, &self.clients).await?; | ||||
|                 handler::quest::player_chose_quest(id, questmenuselect, &self.clients, &block.client_location, &block.rooms).await? | ||||
|                 handler::quest::player_chose_quest(id, questmenuselect, &self.clients, &block.client_location, &block.rooms, self.event).await? | ||||
|             }, | ||||
|             RecvShipPacket::MenuDetail(menudetail) => { | ||||
|                 let block = self.blocks.get_from_client(id, &self.clients).await?; | ||||
| @ -636,7 +699,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, &self.clients, &mut self.item_state, &block.rooms).await? | ||||
|                     handler::room::join_room(id, menuselect, &mut block.client_location, &self.clients, &mut self.item_state, &block.rooms, self.event).await? | ||||
|                 } | ||||
|                 else { | ||||
|                     vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("Incorrect password".into())))] | ||||
| @ -644,7 +707,7 @@ impl<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> { | ||||
|             }, | ||||
|             RecvShipPacket::CharData(chardata) => { | ||||
|                 let block = self.blocks.get_from_client(id, &self.clients).await?; | ||||
|                 handler::lobby::send_player_to_lobby(id, chardata, &mut block.client_location, &self.clients, &self.item_state).await? | ||||
|                 handler::lobby::send_player_to_lobby(id, chardata, &mut block.client_location, &self.clients, &self.item_state, self.event).await? | ||||
|             }, | ||||
|             RecvShipPacket::Message(msg) => { | ||||
|                 self.message(id, msg).await? | ||||
| @ -658,7 +721,7 @@ impl<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> { | ||||
|             }, | ||||
|             RecvShipPacket::CreateRoom(create_room) => { | ||||
|                 let block = self.blocks.get_from_client(id, &self.clients).await?; | ||||
|                 handler::room::create_room(id, create_room, &mut block.client_location, &self.clients, &mut self.item_state, &block.rooms).await? | ||||
|                 handler::room::create_room(id, create_room, &mut block.client_location, &self.clients, &mut self.item_state, &block.rooms, self.event).await? | ||||
|             }, | ||||
|             RecvShipPacket::RoomNameRequest(_req) => { | ||||
|                 let block = self.blocks.get_from_client(id, &self.clients).await?; | ||||
| @ -696,7 +759,7 @@ impl<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> { | ||||
|             }, | ||||
|             RecvShipPacket::LobbySelect(pkt) => { | ||||
|                 let block = self.blocks.get_from_client(id, &self.clients).await?; | ||||
|                 handler::lobby::change_lobby(id, pkt.lobby, &mut block.client_location, &self.clients, &mut self.item_state, &block.rooms, &mut self.entity_gateway).await? | ||||
|                 handler::lobby::change_lobby(id, pkt.lobby, &mut block.client_location, &self.clients, &mut self.item_state, &block.rooms, &mut self.entity_gateway, self.event).await? | ||||
|             }, | ||||
|             RecvShipPacket::RequestQuestList(rql) => { | ||||
|                 let block = self.blocks.get_from_client(id, &self.clients).await?; | ||||
| @ -744,7 +807,6 @@ 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.get_from_client(id, &self.clients).await?; | ||||
|         let area_client = block.client_location.get_local_client(id).await?; | ||||
|         let neighbors = block.client_location.get_client_neighbors(id).await?; | ||||
| @ -763,17 +825,6 @@ impl<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> { | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         /* | ||||
|         if let Some(shipgate_sender) = self.shipgate_sender.as_ref() { | ||||
|             shipgate_sender.send(ShipMessage::RemoveUser(client.user.id)).await; | ||||
|         } | ||||
| 
 | ||||
|         block.client_location.remove_client_from_area(id).await; | ||||
|         self.clients.with(id, |client| Box::pin(async move { | ||||
|             self.item_state.remove_character_from_room(&client.character).await | ||||
|         })).await?; | ||||
|          */ | ||||
| 
 | ||||
|         if let Some(mut client) = self.clients.remove(&id).await { | ||||
|             client.user.at_ship = false; | ||||
|             self.entity_gateway.save_user(&client.user).await; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user