Browse Source

Merge pull request 'holiday rappies (and other events)' (#122) from holidays into master

Reviewed-on: #122
pull/123/head
jake 2 years ago
parent
commit
32dddfd9bd
  1. 21
      Cargo.lock
  2. 1
      Cargo.toml
  3. 6
      src/bin/main.rs
  4. 1
      src/common/mainloop/client.rs
  5. 2
      src/lib.rs
  6. 4
      src/ship/location.rs
  7. 59
      src/ship/map/enemy.rs
  8. 62
      src/ship/map/maps.rs
  9. 12
      src/ship/packet/builder/lobby.rs
  10. 12
      src/ship/packet/builder/room.rs
  11. 17
      src/ship/packet/handler/lobby.rs
  12. 7
      src/ship/packet/handler/quest.rs
  13. 16
      src/ship/packet/handler/room.rs
  14. 6
      src/ship/room.rs
  15. 87
      src/ship/ship.rs

21
Cargo.lock

@ -609,7 +609,6 @@ dependencies = [
"derive_more", "derive_more",
"enum-utils", "enum-utils",
"fern", "fern",
"fix-hidden-lifetime-bug",
"futures", "futures",
"lazy_static", "lazy_static",
"libpso", "libpso",
@ -692,26 +691,6 @@ dependencies = [
"log", "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]] [[package]]
name = "foreign-types" name = "foreign-types"
version = "0.3.2" version = "0.3.2"

1
Cargo.toml

@ -33,4 +33,3 @@ sqlx = { version = "0.5.10", features = ["runtime-async-std-native-tls", "postgr
strum = "0.19.5" strum = "0.19.5"
strum_macros = "0.19" strum_macros = "0.19"
anyhow = { version = "1.0.47", features = ["backtrace"] } anyhow = { version = "1.0.47", features = ["backtrace"] }
fix-hidden-lifetime-bug = "0.2.4"

6
src/bin/main.rs

@ -5,7 +5,7 @@ use elseware::common::interserver::AuthToken;
use elseware::login::login::LoginServerState; use elseware::login::login::LoginServerState;
use elseware::login::character::CharacterServerState; use elseware::login::character::CharacterServerState;
use elseware::patch::patch::{PatchServerState, generate_patch_tree, load_config, load_motd}; 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)] #[allow(unused_imports)]
use elseware::entity::gateway::{EntityGateway, InMemoryGateway, PostgresGateway}; use elseware::entity::gateway::{EntityGateway, InMemoryGateway, PostgresGateway};
@ -364,6 +364,7 @@ fn main() {
.name("US/Sona-Nyl".into()) .name("US/Sona-Nyl".into())
.ip(Ipv4Addr::new(127,0,0,1)) .ip(Ipv4Addr::new(127,0,0,1))
.port(elseware::ship::ship::SHIP_PORT) .port(elseware::ship::ship::SHIP_PORT)
.event(ShipEvent::Halloween)
.gateway(entity_gateway.clone()) .gateway(entity_gateway.clone())
.build(); .build();
let sub_ship_state = ship_state.clone(); let sub_ship_state = ship_state.clone();
@ -378,7 +379,8 @@ fn main() {
let ship_state = ShipServerStateBuilder::default() let ship_state = ShipServerStateBuilder::default()
.name("EU/Dylath-Leen".into()) .name("EU/Dylath-Leen".into())
.ip(Ipv4Addr::new(127,0,0,1)) .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()) .gateway(entity_gateway.clone())
.build(); .build();
let sub_ship_state = ship_state.clone(); let sub_ship_state = ship_state.clone();

1
src/common/mainloop/client.rs

@ -205,6 +205,7 @@ where
loop { loop {
match packet_queue.recv().await { match packet_queue.recv().await {
Ok(pkt) => { Ok(pkt) => {
info!("[send to {:?}] {:#?}", client_id, pkt);
if let Err(err) = send_pkt(&mut socket, &mut cipher, &pkt).await { if let Err(err) = send_pkt(&mut socket, &mut cipher, &pkt).await {
warn!("error sending pkt {:#?} to {:?} {:?}", pkt, client_id, err); warn!("error sending pkt {:#?} to {:?} {:?}", pkt, client_id, err);
} }

2
src/lib.rs

@ -7,8 +7,6 @@
#![feature(test)] #![feature(test)]
extern crate test; extern crate test;
extern crate fix_hidden_lifetime_bug;
pub mod common; pub mod common;
pub mod entity; pub mod entity;

4
src/ship/location.rs

@ -153,13 +153,9 @@ pub struct ClientLocation {
impl Default for ClientLocation { impl Default for ClientLocation {
fn default() -> ClientLocation { fn default() -> ClientLocation {
//const RNONE: Option<Arc<RwLock<Room>>> = None;
//const LINIT: Arc<RwLock<Lobby>> = Arc::new(RwLock::new(Lobby([None; 12])));
ClientLocation { ClientLocation {
//lobbies: [LINIT; 15],
lobbies: core::array::from_fn(|_| Arc::new(RwLock::new(Lobby([None; 12])))), lobbies: core::array::from_fn(|_| Arc::new(RwLock::new(Lobby([None; 12])))),
rooms: core::array::from_fn(|_| None), rooms: core::array::from_fn(|_| None),
//rooms: [RNONE; MAX_ROOMS],
client_location: Arc::new(RwLock::new(HashMap::new())), client_location: Arc::new(RwLock::new(HashMap::new())),
} }
} }

59
src/ship/map/enemy.rs

@ -5,6 +5,7 @@ use std::collections::HashMap;
use byteorder::{LittleEndian, ReadBytesExt}; use byteorder::{LittleEndian, ReadBytesExt};
use thiserror::Error; use thiserror::Error;
use crate::ship::ship::ShipEvent;
use crate::ship::monster::MonsterType; use crate::ship::monster::MonsterType;
use crate::ship::room::Episode; use crate::ship::room::Episode;
@ -87,10 +88,9 @@ impl RareMonsterAppearTable {
let appear_rates: HashMap<MonsterType, f32> = cfg let appear_rates: HashMap<MonsterType, f32> = cfg
.into_iter() .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(); .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, matches!(self.monster,
MonsterType::RagRappy | MonsterType::Hildebear | MonsterType::RagRappy | MonsterType::Hildebear |
MonsterType::PoisonLily | MonsterType::PofuillySlime | MonsterType::PoisonLily | MonsterType::PofuillySlime |
@ -339,32 +336,26 @@ impl MapEnemy {
guaranteed rare monsters don't count towards the limit guaranteed rare monsters don't count towards the limit
*/ */
#[must_use] #[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}, _ => {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
}
} }

62
src/ship/map/maps.rs

@ -6,6 +6,7 @@ use std::fs::File;
use thiserror::Error; use thiserror::Error;
use crate::ship::ship::ShipEvent;
use crate::ship::monster::MonsterType; use crate::ship::monster::MonsterType;
use crate::ship::room::{Episode, RoomMode}; use crate::ship::room::{Episode, RoomMode};
@ -186,7 +187,7 @@ pub struct Maps {
} }
impl 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()) { let map_variants = match (room_mode.episode(), room_mode.single_player()) {
(Episode::One, 0) => { (Episode::One, 0) => {
vec![MapVariant::new(MapArea::Pioneer2Ep1, MapVariantMode::Online), vec![MapVariant::new(MapArea::Pioneer2Ep1, MapVariantMode::Online),
@ -278,20 +279,19 @@ impl Maps {
_ => unreachable!() _ => unreachable!()
}; };
let mut maps = Maps {
Maps {
enemy_data: map_variants.iter() 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() object_data: map_variants.iter()
.flat_map(|map_variant| { .flat_map(|map_variant| {
objects_from_map_data(map_variant.obj_file().into(), &room_mode.episode(), &map_variant.map) objects_from_map_data(map_variant.obj_file().into(), &room_mode.episode(), &map_variant.map)
}).collect(), }).collect(),
map_variants, map_variants,
};
maps.roll_monster_appearance(rare_monster_table);
maps
}
} }
pub fn enemy_by_id(&self, id: usize) -> Result<MapEnemy, MapsError> { 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; self.object_data = objects;
} }
@ -325,10 +332,11 @@ impl Maps {
let shiny: Vec<(usize, &Option<MapEnemy>)> = self.enemy_data.iter() let shiny: Vec<(usize, &Option<MapEnemy>)> = self.enemy_data.iter()
.enumerate() .enumerate()
.filter(|(_,m)| { .filter(|(_,m)| {
if m.is_some() {
m.unwrap().shiny
} else {
false
match m {
Some(m) => {
m.shiny
},
None => false,
} }
}) })
.collect(); .collect();
@ -341,21 +349,15 @@ impl Maps {
} }
rare_monsters 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
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)
} }
)
.collect();
else {
enemy
} }
})
} }

12
src/ship/packet/builder/lobby.rs

@ -1,6 +1,6 @@
use libpso::packet::ship::*; use libpso::packet::ship::*;
use crate::common::serverstate::ClientId; 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::location::{ClientLocation, LobbyId, ClientLocationError};
use crate::ship::packet::builder::{player_info}; use crate::ship::packet::builder::{player_info};
use crate::ship::items::state::ItemState; use crate::ship::items::state::ItemState;
@ -11,7 +11,8 @@ pub async fn join_lobby(id: ClientId,
lobby: LobbyId, lobby: LobbyId,
client_location: &ClientLocation, client_location: &ClientLocation,
clients: &Clients, clients: &Clients,
item_state: &ItemState)
item_state: &ItemState,
event: ShipEvent)
-> Result<JoinLobby, ShipError> { -> Result<JoinLobby, ShipError> {
let lobby_clients = client_location.get_clients_in_lobby(lobby).await.map_err(|err| -> ClientLocationError { err.into() })?; 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, one: 1,
lobby: lobby.id(), lobby: lobby.id(),
block: client_block, block: client_block,
event: 0,
event: event.into(),
padding: 0, padding: 0,
playerinfo, playerinfo,
}) })
@ -51,7 +52,8 @@ pub async fn add_to_lobby(id: ClientId,
lobby: LobbyId, lobby: LobbyId,
client_location: &ClientLocation, client_location: &ClientLocation,
clients: &Clients, clients: &Clients,
item_state: &ItemState)
item_state: &ItemState,
event: ShipEvent)
-> Result<AddToLobby, ShipError> { -> Result<AddToLobby, ShipError> {
let area_client = client_location.get_local_client(id).await.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() })?; 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, one: 1,
lobby: lobby.id(), lobby: lobby.id(),
block: client.block as u16, block: client.block as u16,
event: 0,
event: event.into(),
padding: 0, padding: 0,
playerinfo: player_info(0x100, client, &area_client, &inventory).await, playerinfo: player_info(0x100, client, &area_client, &inventory).await,
}) })

12
src/ship/packet/builder/room.rs

@ -1,6 +1,6 @@
use libpso::packet::ship::*; use libpso::packet::ship::*;
use crate::common::serverstate::ClientId; 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::location::{ClientLocation, RoomId, AreaClient, ClientLocationError};
use crate::ship::room::RoomState; use crate::ship::room::RoomState;
use crate::ship::items::state::ItemState; use crate::ship::items::state::ItemState;
@ -13,7 +13,8 @@ pub async fn join_room(id: ClientId,
clients: &Clients, clients: &Clients,
client_location: &ClientLocation, client_location: &ClientLocation,
room_id: RoomId, room_id: RoomId,
room: &RoomState)
room: &RoomState,
event: ShipEvent)
-> Result<JoinRoom, ShipError> { -> Result<JoinRoom, ShipError> {
let all_clients = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?; 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()) let players = futures::stream::iter(all_clients.iter())
@ -40,7 +41,7 @@ pub async fn join_room(id: ClientId,
one: 1, one: 1,
difficulty: room.mode.difficulty().into(), difficulty: room.mode.difficulty().into(),
battle: room.mode.battle(), battle: room.mode.battle(),
event: 0,
event: event.into(),
section: room.section_id.into(), section: room.section_id.into(),
challenge: room.mode.challenge(), challenge: room.mode.challenge(),
random_seed: room.random_seed, random_seed: room.random_seed,
@ -57,8 +58,7 @@ pub async fn add_to_room(_id: ClientId,
area_client: &AreaClient, area_client: &AreaClient,
leader: &AreaClient, leader: &AreaClient,
item_state: &ItemState, item_state: &ItemState,
_room_id: RoomId,
)
event: ShipEvent)
-> Result<AddToRoom, ShipError> { -> Result<AddToRoom, ShipError> {
let inventory = item_state.get_character_inventory(&client.character).await?; let inventory = item_state.get_character_inventory(&client.character).await?;
Ok(AddToRoom { Ok(AddToRoom {
@ -68,7 +68,7 @@ pub async fn add_to_room(_id: ClientId,
one: 0, // TODO: ???????? one: 0, // TODO: ????????
lobby: 0xFF, lobby: 0xFF,
block: 0, block: 0,
event: 0,
event: event.into(),
padding: 0, padding: 0,
playerinfo: player_info(0x10000, client, area_client, &inventory).await, playerinfo: player_info(0x10000, client, area_client, &inventory).await,
}) })

17
src/ship/packet/handler/lobby.rs

@ -1,7 +1,7 @@
use libpso::packet::ship::*; use libpso::packet::ship::*;
use crate::common::serverstate::ClientId; use crate::common::serverstate::ClientId;
use crate::common::leveltable::LEVEL_TABLE; 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::room::Rooms;
use crate::ship::character::{FullCharacterBytesBuilder}; use crate::ship::character::{FullCharacterBytesBuilder};
use crate::ship::location::{ClientLocation, LobbyId, RoomLobby, ClientLocationError, RoomId}; use crate::ship::location::{ClientLocation, LobbyId, RoomLobby, ClientLocationError, RoomId};
@ -55,11 +55,12 @@ pub async fn send_player_to_lobby(id: ClientId,
_pkt: CharData, _pkt: CharData,
client_location: &mut ClientLocation, client_location: &mut ClientLocation,
clients: &Clients, clients: &Clients,
item_state: &ItemState)
item_state: &ItemState,
event: ShipEvent)
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> { -> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
let lobby = client_location.add_client_to_next_available_lobby(id, LobbyId(0)).await.map_err(|_| ShipError::TooManyClients)?; 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(); let neighbors = client_location.get_client_neighbors(id).await.unwrap();
Ok(vec![(id, SendShipPacket::JoinLobby(join_lobby))] Ok(vec![(id, SendShipPacket::JoinLobby(join_lobby))]
.into_iter() .into_iter()
@ -74,7 +75,8 @@ pub async fn change_lobby<EG>(id: ClientId,
clients: &Clients, clients: &Clients,
item_state: &mut ItemState, item_state: &mut ItemState,
rooms: &Rooms, rooms: &Rooms,
entity_gateway: &mut EG)
entity_gateway: &mut EG,
event: ShipEvent)
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> -> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
where where
EG: EntityGateway + Clone + 'static, EG: EntityGateway + Clone + 'static,
@ -117,8 +119,8 @@ where
Box::pin(async move { Box::pin(async move {
item_state.load_character(&mut entity_gateway, &client.character).await item_state.load_character(&mut entity_gateway, &client.character).await
})}).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?; let neighbors = client_location.get_client_neighbors(id).await?;
Ok(vec![(id, SendShipPacket::JoinLobby(join_lobby))] Ok(vec![(id, SendShipPacket::JoinLobby(join_lobby))]
.into_iter() .into_iter()
@ -126,6 +128,7 @@ where
.map(|c| (c.client, SendShipPacket::AddToLobby(addto.clone())))) .map(|c| (c.client, SendShipPacket::AddToLobby(addto.clone()))))
.chain(old_neighbors.into_iter() .chain(old_neighbors.into_iter()
.map(|c| (c.client, SendShipPacket::LeaveLobby(leave_lobby.clone())))) .map(|c| (c.client, SendShipPacket::LeaveLobby(leave_lobby.clone()))))
.chain(std::iter::once((id, SendShipPacket::LobbyEvent(LobbyEvent{ event: event.into()}))))
.collect()) .collect())
} }

7
src/ship/packet/handler/quest.rs

@ -2,7 +2,7 @@ use std::io::{Cursor, Read, Seek, SeekFrom};
use futures::stream::{FuturesOrdered, StreamExt}; use futures::stream::{FuturesOrdered, StreamExt};
use libpso::packet::ship::*; use libpso::packet::ship::*;
use crate::common::serverstate::ClientId; 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::room::Rooms;
use crate::ship::location::{ClientLocation, ClientLocationError}; use crate::ship::location::{ClientLocation, ClientLocationError};
use crate::ship::packet::builder::quest; use crate::ship::packet::builder::quest;
@ -94,7 +94,8 @@ pub async fn player_chose_quest(id: ClientId,
questmenuselect: QuestMenuSelect, questmenuselect: QuestMenuSelect,
clients: &Clients, clients: &Clients,
client_location: &ClientLocation, client_location: &ClientLocation,
rooms: &Rooms)
rooms: &Rooms,
event: ShipEvent)
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> { -> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?; 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(); .clone();
let rare_monster_drops = room.rare_monster_table.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(); room.map_areas = quest.map_areas.clone();
let bin = quest::quest_header(&questmenuselect, &quest.bin_blob, "bin"); let bin = quest::quest_header(&questmenuselect, &quest.bin_blob, "bin");

16
src/ship/packet/handler/room.rs

@ -5,7 +5,7 @@ use libpso::packet::ship::*;
use libpso::packet::messages::*; use libpso::packet::messages::*;
use crate::common::serverstate::ClientId; use crate::common::serverstate::ClientId;
use crate::common::leveltable::LEVEL_TABLE; 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::room::Rooms;
use crate::ship::location::{ClientLocation, RoomId, RoomLobby, GetAreaError}; use crate::ship::location::{ClientLocation, RoomId, RoomLobby, GetAreaError};
use crate::ship::packet::builder; use crate::ship::packet::builder;
@ -17,7 +17,8 @@ pub async fn create_room(id: ClientId,
client_location: &mut ClientLocation, client_location: &mut ClientLocation,
clients: &Clients, clients: &Clients,
item_state: &mut ItemState, item_state: &mut ItemState,
rooms: &Rooms)
rooms: &Rooms,
event: ShipEvent)
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> { -> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
let level = clients.with(id, |client| Box::pin(async move { let level = clients.with(id, |client| Box::pin(async move {
LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp) 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(); let mut item_state = item_state.clone();
Box::pin(async move { Box::pin(async move {
item_state.add_character_to_room(room_id, &client.character, area_client).await; 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; room.bursting = true;
Ok::<_, ShipError>(room) Ok::<_, ShipError>(room)
})}).await??; })}).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?; rooms.add(room_id, room).await?;
let mut result = vec![(id, SendShipPacket::JoinRoom(join_room))]; let mut result = vec![(id, SendShipPacket::JoinRoom(join_room))];
@ -87,7 +88,8 @@ pub async fn join_room(id: ClientId,
client_location: &mut ClientLocation, client_location: &mut ClientLocation,
clients: &Clients, clients: &Clients,
item_state: &mut ItemState, item_state: &mut ItemState,
rooms: &Rooms)
rooms: &Rooms,
event: ShipEvent)
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> { -> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
let room_id = RoomId(pkt.item as usize); let room_id = RoomId(pkt.item as usize);
if !rooms.exists(room_id).await { if !rooms.exists(room_id).await {
@ -134,12 +136,12 @@ pub async fn join_room(id: ClientId,
let clients = clients.clone(); let clients = clients.clone();
let client_location = client_location.clone(); let client_location = client_location.clone();
Box::pin(async move { 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??; })}).await??;
let add_to = clients.with(id, |client| { let add_to = clients.with(id, |client| {
let item_state = item_state.clone(); let item_state = item_state.clone();
Box::pin(async move { 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??; })}).await??;
rooms.with_mut(room_id, |room| Box::pin(async move { rooms.with_mut(room_id, |room| Box::pin(async move {

6
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::area::MapAreaLookup;
use crate::ship::map::enemy::RareMonsterAppearTable; use crate::ship::map::enemy::RareMonsterAppearTable;
use crate::ship::quests; use crate::ship::quests;
use crate::ship::ship::ShipError;
use crate::ship::ship::{ShipError, ShipEvent};
use crate::ship::location::{MAX_ROOMS, RoomId}; use crate::ship::location::{MAX_ROOMS, RoomId};
@ -343,7 +343,7 @@ impl RoomState {
self.quest_group = QuestCategoryType::from(group); 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 { if [create_room.battle, create_room.challenge, create_room.single_player].iter().sum::<u8>() > 1 {
return Err(RoomCreationError::InvalidMode) return Err(RoomCreationError::InvalidMode)
} }
@ -410,7 +410,7 @@ impl RoomState {
rare_monster_table: Box::new(rare_monster_table.clone()), rare_monster_table: Box::new(rare_monster_table.clone()),
name: String::from_utf16_lossy(&create_room.name).trim_matches(char::from(0)).into(), name: String::from_utf16_lossy(&create_room.name).trim_matches(char::from(0)).into(),
password: create_room.password, 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, section_id,
drop_table: Box::new(DropTable::new(room_mode.episode(), room_mode.difficulty(), section_id)), drop_table: Box::new(DropTable::new(room_mode.episode(), room_mode.difficulty(), section_id)),
bursting: false, bursting: false,

87
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_CATEGORY_MENU_ID: u32 = 0xA2;
pub const QUEST_SELECT_MENU_ID: u32 = 0xA3; 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)] #[derive(Error, Debug)]
pub enum ShipError { pub enum ShipError {
@ -240,6 +291,7 @@ pub enum SendShipPacket {
AcknowledgeTrade(AcknowledgeTrade), AcknowledgeTrade(AcknowledgeTrade),
CancelTrade(CancelTrade), CancelTrade(CancelTrade),
TradeSuccessful(TradeSuccessful), TradeSuccessful(TradeSuccessful),
LobbyEvent(LobbyEvent),
} }
impl SendServerPacket for SendShipPacket { impl SendServerPacket for SendShipPacket {
@ -282,6 +334,7 @@ impl SendServerPacket for SendShipPacket {
SendShipPacket::AcknowledgeTrade(pkt) => pkt.as_bytes(), SendShipPacket::AcknowledgeTrade(pkt) => pkt.as_bytes(),
SendShipPacket::CancelTrade(pkt) => pkt.as_bytes(), SendShipPacket::CancelTrade(pkt) => pkt.as_bytes(),
SendShipPacket::TradeSuccessful(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>, ip: Option<Ipv4Addr>,
port: Option<u16>, port: Option<u16>,
auth_token: Option<AuthToken>, auth_token: Option<AuthToken>,
event: Option<ShipEvent>,
num_blocks: usize, num_blocks: usize,
} }
@ -332,6 +386,7 @@ impl<EG: EntityGateway + Clone + 'static> Default for ShipServerStateBuilder<EG>
ip: None, ip: None,
port: None, port: None,
auth_token: None, auth_token: None,
event: None,
num_blocks: 2, num_blocks: 2,
} }
} }
@ -368,6 +423,12 @@ impl<EG: EntityGateway + Clone + 'static> ShipServerStateBuilder<EG> {
self self
} }
#[must_use]
pub fn event(mut self, event: ShipEvent) -> ShipServerStateBuilder<EG> {
self.event = Some(event);
self
}
#[must_use] #[must_use]
pub fn blocks(mut self, num_blocks: usize) -> ShipServerStateBuilder<EG> { pub fn blocks(mut self, num_blocks: usize) -> ShipServerStateBuilder<EG> {
self.num_blocks = num_blocks; self.num_blocks = num_blocks;
@ -385,6 +446,7 @@ impl<EG: EntityGateway + Clone + 'static> ShipServerStateBuilder<EG> {
port: self.port.unwrap_or(SHIP_PORT), port: self.port.unwrap_or(SHIP_PORT),
shops: ItemShops::default(), shops: ItemShops::default(),
blocks: Blocks(blocks), blocks: Blocks(blocks),
event: self.event.unwrap_or(ShipEvent::None),
auth_token: self.auth_token.unwrap_or_else(|| AuthToken("".into())), auth_token: self.auth_token.unwrap_or_else(|| AuthToken("".into())),
ship_list: Vec::new(), ship_list: Vec::new(),
@ -424,6 +486,7 @@ pub struct ShipServerState<EG: EntityGateway + Clone + 'static> {
item_state: items::state::ItemState, item_state: items::state::ItemState,
shops: ItemShops, shops: ItemShops,
pub blocks: Blocks, pub blocks: Blocks,
event: ShipEvent,
ip: Ipv4Addr, ip: Ipv4Addr,
port: u16, 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(); let select_block = handler::lobby::block_selected(id, menuselect, &self.clients, &self.item_state).await?.into_iter();
leave_lobby.chain(select_block).collect() 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?, QUEST_CATEGORY_MENU_ID => handler::quest::select_quest_category(id, menuselect, &block.client_location, &block.rooms).await?,
_ => unreachable!(), _ => unreachable!(),
} }
}, },
RecvShipPacket::QuestMenuSelect(questmenuselect) => { RecvShipPacket::QuestMenuSelect(questmenuselect) => {
let block = self.blocks.get_from_client(id, &self.clients).await?; 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) => { RecvShipPacket::MenuDetail(menudetail) => {
let block = self.blocks.get_from_client(id, &self.clients).await?; 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, menu: room_password_req.menu,
item: room_password_req.item, 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 { else {
vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("Incorrect password".into())))] vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("Incorrect password".into())))]
@ -644,7 +707,7 @@ impl<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> {
}, },
RecvShipPacket::CharData(chardata) => { RecvShipPacket::CharData(chardata) => {
let block = self.blocks.get_from_client(id, &self.clients).await?; 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) => { RecvShipPacket::Message(msg) => {
self.message(id, msg).await? self.message(id, msg).await?
@ -658,7 +721,7 @@ impl<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> {
}, },
RecvShipPacket::CreateRoom(create_room) => { RecvShipPacket::CreateRoom(create_room) => {
let block = self.blocks.get_from_client(id, &self.clients).await?; 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) => { RecvShipPacket::RoomNameRequest(_req) => {
let block = self.blocks.get_from_client(id, &self.clients).await?; 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) => { RecvShipPacket::LobbySelect(pkt) => {
let block = self.blocks.get_from_client(id, &self.clients).await?; 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) => { RecvShipPacket::RequestQuestList(rql) => {
let block = self.blocks.get_from_client(id, &self.clients).await?; 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> { 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 block = self.blocks.get_from_client(id, &self.clients).await?;
let area_client = block.client_location.get_local_client(id).await?; let area_client = block.client_location.get_local_client(id).await?;
let neighbors = block.client_location.get_client_neighbors(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 { if let Some(mut client) = self.clients.remove(&id).await {
client.user.at_ship = false; client.user.at_ship = false;
self.entity_gateway.save_user(&client.user).await; self.entity_gateway.save_user(&client.user).await;

Loading…
Cancel
Save