diff --git a/Cargo.lock b/Cargo.lock index 8b5c4c5..f343af7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 391e99a..e746d70 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/bin/main.rs b/src/bin/main.rs index 3195373..eb7c687 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -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(); diff --git a/src/common/mainloop/client.rs b/src/common/mainloop/client.rs index 6cf7de4..758d075 100644 --- a/src/common/mainloop/client.rs +++ b/src/common/mainloop/client.rs @@ -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); } diff --git a/src/lib.rs b/src/lib.rs index 068f6e9..176a9fe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,8 +7,6 @@ #![feature(test)] extern crate test; -extern crate fix_hidden_lifetime_bug; - pub mod common; pub mod entity; diff --git a/src/ship/location.rs b/src/ship/location.rs index 037df94..f58a6f9 100644 --- a/src/ship/location.rs +++ b/src/ship/location.rs @@ -153,13 +153,9 @@ pub struct ClientLocation { impl Default for ClientLocation { fn default() -> ClientLocation { - //const RNONE: Option>> = None; - //const LINIT: Arc> = 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())), } } diff --git a/src/ship/map/enemy.rs b/src/ship/map/enemy.rs index b5efd7c..79dc3b8 100644 --- a/src/ship/map/enemy.rs +++ b/src/ship/map/enemy.rs @@ -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 = 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::() < *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::() < *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 - } } diff --git a/src/ship/map/maps.rs b/src/ship/map/maps.rs index 6c971fc..5d04291 100644 --- a/src/ship/map/maps.rs +++ b/src/ship/map/maps.rs @@ -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 + .flat_map(|map_variant| { + objects_from_map_data(map_variant.obj_file().into(), &room_mode.episode(), &map_variant.map) + }).collect(), + map_variants, + } } pub fn enemy_by_id(&self, id: usize) -> Result { @@ -314,9 +314,16 @@ impl Maps { }) } - pub fn set_quest_data(&mut self, enemies: Vec>, objects: Vec>, 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>, + objects: Vec>, + 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)> = 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) - } - } else { - x - } - ) - .collect(); - } +fn apply_rare_enemy(enemy: Option, rare_enemy_table: &RareMonsterAppearTable, event: ShipEvent) -> Option { + enemy.map(|enemy| { + if enemy.can_be_rare() && rare_enemy_table.roll_is_rare(&enemy.monster) { + enemy.into_rare(event) + } + else { + enemy + } + }) } diff --git a/src/ship/packet/builder/lobby.rs b/src/ship/packet/builder/lobby.rs index 2571766..666d9be 100644 --- a/src/ship/packet/builder/lobby.rs +++ b/src/ship/packet/builder/lobby.rs @@ -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 { 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 { 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, }) diff --git a/src/ship/packet/builder/room.rs b/src/ship/packet/builder/room.rs index 4ff4696..b50c7fb 100644 --- a/src/ship/packet/builder/room.rs +++ b/src/ship/packet/builder/room.rs @@ -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 { 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, @@ -53,13 +54,12 @@ pub async fn join_room(id: ClientId, pub async fn add_to_room(_id: ClientId, - client: &ClientState, - area_client: &AreaClient, - leader: &AreaClient, - item_state: &ItemState, - _room_id: RoomId, -) - -> Result { + client: &ClientState, + area_client: &AreaClient, + leader: &AreaClient, + item_state: &ItemState, + event: ShipEvent) + -> Result { let inventory = item_state.get_character_inventory(&client.character).await?; Ok(AddToRoom { flag: 1, @@ -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, }) diff --git a/src/ship/packet/handler/lobby.rs b/src/ship/packet/handler/lobby.rs index 287ea41..f0caf23 100644 --- a/src/ship/packet/handler/lobby.rs +++ b/src/ship/packet/handler/lobby.rs @@ -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, 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(id: ClientId, clients: &Clients, item_state: &mut ItemState, rooms: &Rooms, - entity_gateway: &mut EG) + entity_gateway: &mut EG, + event: ShipEvent) -> Result, ShipError> where EG: EntityGateway + Clone + 'static, @@ -117,16 +119,17 @@ 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() - .chain(neighbors.into_iter() - .map(|c| (c.client, SendShipPacket::AddToLobby(addto.clone())))) - .chain(old_neighbors.into_iter() - .map(|c| (c.client, SendShipPacket::LeaveLobby(leave_lobby.clone())))) - .collect()) + .into_iter() + .chain(neighbors.into_iter() + .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()) } pub async fn remove_from_lobby(id: ClientId, diff --git a/src/ship/packet/handler/quest.rs b/src/ship/packet/handler/quest.rs index 9df89b9..a9a5cb2 100644 --- a/src/ship/packet/handler/quest.rs +++ b/src/ship/packet/handler/quest.rs @@ -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, 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"); diff --git a/src/ship/packet/handler/room.rs b/src/ship/packet/handler/room.rs index 7653abc..435d4b8 100644 --- a/src/ship/packet/handler/room.rs +++ b/src/ship/packet/handler/room.rs @@ -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, 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, 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 { diff --git a/src/ship/room.rs b/src/ship/room.rs index 9758064..ed41b8d 100644 --- a/src/ship/room.rs +++ b/src/ship/room.rs @@ -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 { + pub fn from_create_room(create_room: &libpso::packet::ship::CreateRoom, section_id: SectionID, event: ShipEvent) -> Result { if [create_room.battle, create_room.challenge, create_room.single_player].iter().sum::() > 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, diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 4dcd8c7..d183ebf 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -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 for u32 { + fn from(other: ShipEvent) -> u32 { + u16::from(other) as u32 + } +} + +impl From for u16 { + fn from(other: ShipEvent) -> u16 { + u8::from(other) as u16 + } +} + +impl From 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 { ip: Option, port: Option, auth_token: Option, + event: Option, num_blocks: usize, } @@ -332,6 +386,7 @@ impl Default for ShipServerStateBuilder ip: None, port: None, auth_token: None, + event: None, num_blocks: 2, } } @@ -368,6 +423,12 @@ impl ShipServerStateBuilder { self } + #[must_use] + pub fn event(mut self, event: ShipEvent) -> ShipServerStateBuilder { + self.event = Some(event); + self + } + #[must_use] pub fn blocks(mut self, num_blocks: usize) -> ShipServerStateBuilder { self.num_blocks = num_blocks; @@ -385,6 +446,7 @@ impl ShipServerStateBuilder { 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 { item_state: items::state::ItemState, shops: ItemShops, pub blocks: Blocks, + event: ShipEvent, ip: Ipv4Addr, port: u16, @@ -611,14 +674,14 @@ impl ServerState for ShipServerState { 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 ServerState for ShipServerState { 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 ServerState for ShipServerState { }, 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 ServerState for ShipServerState { }, 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 ServerState for ShipServerState { }, 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 ServerState for ShipServerState { } async fn on_disconnect(&mut self, id: ClientId) -> Result, anyhow::Error> { - //let client = self.clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; let block = self.blocks.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 ServerState for ShipServerState { } }; - /* - 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;