diff --git a/.drone.yml b/.drone.yml index ae267b5..49ef008 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,12 +1,24 @@ --- kind: pipeline type: docker -name: test elseware +name: test elseware concurrency: limit: 1 +environment: + CARGO_INCREMENTAL: false + steps: +- name: clean cache + image: rustlang/rust:nightly + volumes: + - name: cache + path: /usr/local/cargo + - name: target-cache + path: /drone/src/target + commands: + - cargo prune - name: build image: rustlang/rust:nightly volumes: @@ -33,7 +45,7 @@ steps: - name: target-cache path: /drone/src/target commands: - - cargo test + - cargo test --jobs 1 volumes: - name: cache diff --git a/Cargo.toml b/Cargo.toml index c2e511d..5737f97 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,8 +4,40 @@ version = "0.1.0" authors = ["Jake Probst "] edition = "2021" -[dependencies] +[workspace] +members = [ + "client", + "drops", + "entity", + "items", + "location", + "maps", + "networking", + "pktbuilder", + "quests", + "room", + "shops", + "stats", + "trade", +] + +[workspace.dependencies] +entity = { path = "./entity" } +maps = { path = "./maps" } +networking = { path = "./networking" } +shops = { path = "./shops" } +stats = { path = "./stats" } +items = { path = "./items" } +pktbuilder = { path = "./pktbuilder" } +quests = { path = "./quests" } +location = { path = "./location" } +client = { path = "./client" } +drops = { path = "./drops" } +trade = { path = "./trade" } +room = { path = "./room" } + libpso = { git = "http://git.sharnoth.com/jake/libpso" } + async-std = { version = "1.9.0", features = ["unstable", "attributes"] } futures = "0.3.5" rand = "0.7.3" @@ -33,3 +65,42 @@ sqlx = { version = "0.6.2", features = ["runtime-async-std-native-tls", "postgre strum = "0.19.5" strum_macros = "0.19" anyhow = { version = "1.0.68", features = ["backtrace"] } + +[dependencies] +entity = { workspace = true } +maps = { workspace = true } +networking = { workspace = true } +shops = { workspace = true } +stats = { workspace = true } +items = { workspace = true } +pktbuilder = { workspace = true } +quests = { workspace = true } +location = { workspace = true } +client = { workspace = true } +drops = { workspace = true } +trade = { workspace = true } +room = { workspace = true } + +libpso = { workspace = true } + +ages-prs = { workspace = true } +anyhow = { workspace = true } +async-recursion = { workspace = true } +async-std = { workspace = true } +async-trait = { workspace = true } +bcrypt = { workspace = true } +byteorder = { workspace = true } +chrono = { workspace = true } +crc = { workspace = true } +derive_more = { workspace = true } +enum-utils = { workspace = true } +fern = { workspace = true } +futures = { workspace = true } +log = { workspace = true } +rand = { workspace = true } +rand_chacha = { workspace = true } +ron = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +toml = { workspace = true } +thiserror = { workspace = true } \ No newline at end of file diff --git a/client/Cargo.toml b/client/Cargo.toml new file mode 100644 index 0000000..12f9698 --- /dev/null +++ b/client/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "client" +version = "0.1.0" +edition = "2021" + + +[dependencies] +entity = { workspace = true } +maps = { workspace = true } +networking = { workspace = true } +shops = { workspace = true } +items = { workspace = true } + +libpso = { workspace = true } + +async-std = { workspace = true } +futures = { workspace = true } +anyhow = { workspace = true } +thiserror = { workspace = true } +chrono = { workspace = true } diff --git a/src/ship/client.rs b/client/src/lib.rs similarity index 89% rename from src/ship/client.rs rename to client/src/lib.rs index 5495309..43c7a0f 100644 --- a/src/ship/client.rs +++ b/client/src/lib.rs @@ -6,15 +6,20 @@ use futures::future::BoxFuture; use libpso::packet::ship::*; use libpso::packet::login::Session; -use crate::common::serverstate::ClientId; -use crate::entity::account::{UserAccountEntity, UserSettingsEntity}; -use crate::entity::character::CharacterEntity; -use crate::entity::item; +use networking::serverstate::ClientId; +use entity::account::{UserAccountEntity, UserSettingsEntity}; +use entity::character::CharacterEntity; +use entity::item; -use crate::ship::ship::ShipError; -use crate::ship::items; -use crate::ship::map::MapArea; -use crate::ship::shops::{WeaponShopItem, ToolShopItem, ArmorShopItem}; +use maps::area::MapArea; +use shops::{WeaponShopItem, ToolShopItem, ArmorShopItem}; + + +#[derive(thiserror::Error, Debug)] +pub enum ClientError { + #[error("not found {0}")] + NotFound(ClientId), +} #[derive(Clone, Default)] @@ -46,7 +51,7 @@ impl Clients { .await; let client = clients .get(&client_id) - .ok_or_else(|| ShipError::ClientNotFound(client_id))? + .ok_or(ClientError::NotFound(client_id))? .read() .await; @@ -69,7 +74,7 @@ impl Clients { for (cindex, client_id) in client_ids.iter().enumerate() { let c = clients .get(client_id) - .ok_or_else(|| ShipError::ClientNotFound(*client_id))? + .ok_or(ClientError::NotFound(*client_id))? .read() .await; client_states[cindex].write(c); @@ -95,7 +100,7 @@ impl Clients { .await; let mut client = clients .get(&client_id) - .ok_or_else(|| ShipError::ClientNotFound(client_id))? + .ok_or(ClientError::NotFound(client_id))? .write() .await; diff --git a/drops/Cargo.toml b/drops/Cargo.toml new file mode 100644 index 0000000..199e7c6 --- /dev/null +++ b/drops/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "drops" +version = "0.1.0" +edition = "2021" + + +[dependencies] +entity = { workspace = true } +maps = { workspace = true } +stats = { workspace = true } + +rand = { workspace = true } +rand_chacha = { workspace = true } +serde = { workspace = true } +enum-utils = { workspace = true } +toml = { workspace = true } +chrono = { workspace = true } \ No newline at end of file diff --git a/src/ship/drops/box_drop_table.rs b/drops/src/box_drop_table.rs similarity index 90% rename from src/ship/drops/box_drop_table.rs rename to drops/src/box_drop_table.rs index f507d27..01f41eb 100644 --- a/src/ship/drops/box_drop_table.rs +++ b/drops/src/box_drop_table.rs @@ -2,18 +2,17 @@ use rand::{Rng}; use rand::distributions::{WeightedIndex, Distribution}; use serde::{Serialize, Deserialize}; -use crate::entity::character::SectionID; -use crate::ship::room::{Difficulty, Episode}; -use crate::ship::map::MapArea; -use crate::ship::drops::{ItemDropType, load_data_file}; -use crate::ship::map::{MapObject, MapObjectType, FixedBoxDropType}; -use crate::ship::drops::rare_drop_table::{RareDropTable, RareDropItem}; -use crate::ship::drops::generic_weapon::GenericWeaponTable; -use crate::ship::drops::generic_armor::GenericArmorTable; -use crate::ship::drops::generic_shield::GenericShieldTable; -use crate::ship::drops::generic_unit::GenericUnitTable; -use crate::ship::drops::tool_table::ToolTable; -use crate::entity::item::ItemDetail; +use entity::character::SectionID; +use maps::room::{Difficulty, Episode}; +use maps::area::MapArea; +use crate::{ItemDropType, load_data_file}; +use maps::object::{MapObject, MapObjectType, FixedBoxDropType}; +use crate::rare_drop_table::{RareDropTable, RareDropItem}; +use crate::generic_weapon::GenericWeaponTable; +use crate::generic_armor::GenericArmorTable; +use crate::generic_shield::GenericShieldTable; +use crate::generic_unit::GenericUnitTable; +use crate::tool_table::ToolTable; #[derive(Debug, Serialize, Deserialize)] struct BoxDropRate { @@ -176,8 +175,8 @@ impl BoxDropTable { fn random_box_drop(&self, map_area: &MapArea, rng: &mut R) -> Option { self.rare_drop(map_area, rng).or_else(|| { let rate = self.box_rates.rates_by_area(map_area); - let type_weights = WeightedIndex::new(&[rate.weapon_rate, rate.armor_rate, rate.shield_rate, rate.unit_rate, - rate.tool_rate, rate.meseta_rate, rate.nothing_rate]).unwrap(); + let type_weights = WeightedIndex::new([rate.weapon_rate, rate.armor_rate, rate.shield_rate, rate.unit_rate, + rate.tool_rate, rate.meseta_rate, rate.nothing_rate]).unwrap(); let btype = type_weights.sample(rng); match btype { 0 => self.weapon_table.get_drop(map_area, rng), @@ -204,7 +203,7 @@ impl BoxDropTable { FixedBoxDropType::Specific(value) => { let mut buf: [u8; 16] = [0; 16]; buf[0..4].copy_from_slice(&u32::to_be_bytes(value)); - ItemDetail::parse_item_from_bytes(buf) + ItemDropType::parse_item_from_bytes(buf) }, } } diff --git a/src/ship/drops/generic_armor.rs b/drops/src/generic_armor.rs similarity index 85% rename from src/ship/drops/generic_armor.rs rename to drops/src/generic_armor.rs index 2bf5895..82e9a58 100644 --- a/src/ship/drops/generic_armor.rs +++ b/drops/src/generic_armor.rs @@ -1,14 +1,14 @@ use std::collections::HashMap; use serde::{Serialize, Deserialize}; -use rand::{Rng}; +use rand::Rng; use rand::distributions::{WeightedIndex, Distribution}; -use crate::entity::item::armor::{ArmorType, Armor}; -use crate::ship::room::{Difficulty, Episode}; -use crate::ship::map::MapArea; -use crate::entity::character::SectionID; -use crate::ship::drops::{ItemDropType, load_data_file}; -use crate::ship::item_stats::{armor_stats, ArmorStats}; +use entity::character::SectionID; +use entity::item::armor::{ArmorType, Armor}; +use maps::room::{Difficulty, Episode}; +use maps::area::MapArea; +use crate::{ItemDropType, load_data_file}; +use stats::items::{armor_stats, ArmorStats}; #[derive(Debug, Serialize, Deserialize)] @@ -46,8 +46,8 @@ impl GenericArmorTable { } fn armor_type(&self, area_map: &MapArea, rng: &mut R) -> ArmorType { - let rank_weights = WeightedIndex::new(&[self.rank_rates.rank0, self.rank_rates.rank1, self.rank_rates.rank2, - self.rank_rates.rank3, self.rank_rates.rank4]).unwrap(); + let rank_weights = WeightedIndex::new([self.rank_rates.rank0, self.rank_rates.rank1, self.rank_rates.rank2, + self.rank_rates.rank3, self.rank_rates.rank4]).unwrap(); let rank = rank_weights.sample(rng) as i32; let armor_level = std::cmp::max(0i32, self.armor_set as i32 - 3i32 + rank + area_map.drop_area_value().unwrap_or(0) as i32); match armor_level { @@ -80,8 +80,8 @@ impl GenericArmorTable { } pub fn slots(&self, _area_map: &MapArea, rng: &mut R) -> usize { - let slot_weights = WeightedIndex::new(&[self.slot_rates.slot0, self.slot_rates.slot1, self.slot_rates.slot2, - self.slot_rates.slot3, self.slot_rates.slot4]).unwrap(); + let slot_weights = WeightedIndex::new([self.slot_rates.slot0, self.slot_rates.slot1, self.slot_rates.slot2, + self.slot_rates.slot3, self.slot_rates.slot4]).unwrap(); slot_weights.sample(rng) } diff --git a/src/ship/drops/generic_shield.rs b/drops/src/generic_shield.rs similarity index 88% rename from src/ship/drops/generic_shield.rs rename to drops/src/generic_shield.rs index 9eec585..c7bbf1f 100644 --- a/src/ship/drops/generic_shield.rs +++ b/drops/src/generic_shield.rs @@ -1,14 +1,14 @@ use std::collections::HashMap; use serde::{Serialize, Deserialize}; -use rand::{Rng}; +use rand::Rng; use rand::distributions::{WeightedIndex, Distribution}; -use crate::entity::item::shield::{ShieldType, Shield}; -use crate::ship::room::{Difficulty, Episode}; -use crate::ship::map::MapArea; -use crate::entity::character::SectionID; -use crate::ship::drops::{ItemDropType, load_data_file}; -use crate::ship::item_stats::{shield_stats, ShieldStats}; +use entity::item::shield::{ShieldType, Shield}; +use entity::character::SectionID; +use maps::room::{Difficulty, Episode}; +use maps::area::MapArea; +use crate::{ItemDropType, load_data_file}; +use stats::items::{shield_stats, ShieldStats}; #[derive(Debug, Serialize, Deserialize)] @@ -36,8 +36,8 @@ impl GenericShieldTable { } fn shield_type(&self, area_map: &MapArea, rng: &mut R) -> ShieldType { - let rank_weights = WeightedIndex::new(&[self.rank_rates.rank0, self.rank_rates.rank1, self.rank_rates.rank2, - self.rank_rates.rank3, self.rank_rates.rank4]).unwrap(); + let rank_weights = WeightedIndex::new([self.rank_rates.rank0, self.rank_rates.rank1, self.rank_rates.rank2, + self.rank_rates.rank3, self.rank_rates.rank4]).unwrap(); let rank = rank_weights.sample(rng) as i32; let shield_level = std::cmp::max(0i32, self.shield_set as i32 - 3i32 + rank + area_map.drop_area_value().unwrap_or(0) as i32); match shield_level { diff --git a/src/ship/drops/generic_unit.rs b/drops/src/generic_unit.rs similarity index 92% rename from src/ship/drops/generic_unit.rs rename to drops/src/generic_unit.rs index 73bc4a6..cc1caa0 100644 --- a/src/ship/drops/generic_unit.rs +++ b/drops/src/generic_unit.rs @@ -1,14 +1,14 @@ use std::collections::BTreeMap; use serde::{Serialize, Deserialize}; -use rand::{Rng}; +use rand::Rng; use rand::seq::IteratorRandom; -use crate::entity::item::unit::{UnitType, Unit, UnitModifier}; -use crate::ship::room::{Difficulty, Episode}; -use crate::ship::map::MapArea; -use crate::entity::character::SectionID; -use crate::ship::drops::{ItemDropType, load_data_file}; -use crate::ship::item_stats::{unit_stats, UnitStats}; +use entity::character::SectionID; +use entity::item::unit::{UnitType, Unit, UnitModifier}; +use maps::room::{Difficulty, Episode}; +use maps::area::MapArea; +use crate::{ItemDropType, load_data_file}; +use stats::items::{unit_stats, UnitStats}; diff --git a/src/ship/drops/generic_weapon.rs b/drops/src/generic_weapon.rs similarity index 97% rename from src/ship/drops/generic_weapon.rs rename to drops/src/generic_weapon.rs index d1fb2bb..557130c 100644 --- a/src/ship/drops/generic_weapon.rs +++ b/drops/src/generic_weapon.rs @@ -1,14 +1,14 @@ use std::collections::{HashMap, BTreeMap}; use serde::{Serialize, Deserialize}; -use rand::{Rng}; +use rand::Rng; use rand::distributions::{WeightedIndex, Distribution}; use rand::seq::SliceRandom; -use crate::entity::item::weapon::{Weapon, WeaponType, Attribute, WeaponAttribute, WeaponSpecial}; -use crate::ship::room::{Difficulty, Episode}; -use crate::ship::map::MapArea; -use crate::entity::character::SectionID; -use crate::ship::drops::{ItemDropType, load_data_file}; +use entity::character::SectionID; +use entity::item::weapon::{Weapon, WeaponType, Attribute, WeaponAttribute, WeaponSpecial}; +use maps::room::{Difficulty, Episode}; +use maps::area::MapArea; +use crate::{ItemDropType, load_data_file}; @@ -240,7 +240,7 @@ impl AttributeTable { fn generate_attribute(&self, pattern: &PercentPatternType, rates: &AttributeRate, rng: &mut R) -> Option { - let attribute_weights = WeightedIndex::new(&[rates.none, rates.native, rates.abeast, rates.machine, rates.dark, rates.hit]).unwrap(); + let attribute_weights = WeightedIndex::new([rates.none, rates.native, rates.abeast, rates.machine, rates.dark, rates.hit]).unwrap(); let attr = match attribute_weights.sample(rng) { 0 => return None, 1 => Attribute::Native, @@ -253,7 +253,7 @@ impl AttributeTable { let percents = self.percent_rates.get_by_pattern(pattern); - let value_weights = WeightedIndex::new(&percents.as_array()).unwrap(); + let value_weights = WeightedIndex::new(percents.as_array()).unwrap(); let value = value_weights.sample(rng); let percent = ((value + 1) * 5) as i8; @@ -477,7 +477,7 @@ impl GenericWeaponTable { let pattern = std::cmp::min(area % ratio.inc, 3); let weights = self.grind_rates.grind_rate[pattern as usize]; - let grind_choice = WeightedIndex::new(&weights).unwrap(); + let grind_choice = WeightedIndex::new(weights).unwrap(); grind_choice.sample(rng) } diff --git a/src/ship/drops/mod.rs b/drops/src/lib.rs similarity index 76% rename from src/ship/drops/mod.rs rename to drops/src/lib.rs index 5d8062c..32a14db 100644 --- a/src/ship/drops/mod.rs +++ b/drops/src/lib.rs @@ -5,7 +5,6 @@ // to their drops -mod drop_table; pub mod rare_drop_table; mod generic_weapon; mod generic_armor; @@ -22,19 +21,19 @@ use std::io::Read; use serde::{Serialize, Deserialize}; use rand::{Rng, SeedableRng}; -use crate::ship::monster::MonsterType; -use crate::ship::room::{Difficulty, Episode}; -use crate::ship::map::MapArea; -use crate::entity::character::SectionID; -use crate::ship::drops::generic_weapon::GenericWeaponTable; -use crate::ship::drops::generic_armor::GenericArmorTable; -use crate::ship::drops::generic_shield::GenericShieldTable; -use crate::ship::drops::generic_unit::GenericUnitTable; -use crate::ship::drops::tool_table::ToolTable; -use crate::ship::drops::rare_drop_table::RareDropTable; -use crate::ship::drops::box_drop_table::BoxDropTable; -use crate::ship::map::MapObject; -use crate::entity::item::{weapon, armor, shield, unit, mag, tool, tech}; +use maps::monster::MonsterType; +use maps::room::{Difficulty, Episode}; +use maps::area::MapArea; +use entity::character::SectionID; +use crate::generic_weapon::GenericWeaponTable; +use crate::generic_armor::GenericArmorTable; +use crate::generic_shield::GenericShieldTable; +use crate::generic_unit::GenericUnitTable; +use crate::tool_table::ToolTable; +use crate::rare_drop_table::RareDropTable; +use crate::box_drop_table::BoxDropTable; +use maps::object::MapObject; +use entity::item::{ItemType, weapon, armor, shield, unit, mag, tool, tech, esweapon}; fn data_file_path(episode: Episode, difficulty: Difficulty, section_id: SectionID, filename: &str) -> PathBuf { @@ -55,18 +54,6 @@ pub fn load_data_file(episode: Episode, difficul toml::from_str::(s.as_str()).unwrap() } -// this is just copypaste -pub fn load_rare_monster_file(episode: Episode) -> T { - // TODO: where does the rare monster toml file actually live - let mut path = PathBuf::from("data/battle_param/"); - path.push(episode.to_string().to_lowercase() + "_rare_monster.toml"); - - let mut f = File::open(path).unwrap(); - let mut s = String::new(); - f.read_to_string(&mut s); - toml::from_str::(s.as_str()).unwrap() -} - #[derive(Debug, Serialize, Deserialize, Copy, Clone)] pub enum MonsterDropType { #[serde(rename = "weapon")] @@ -102,6 +89,28 @@ pub enum ItemDropType { Meseta(u32), } +impl ItemDropType { + pub fn parse_item_from_bytes(data: [u8; 16]) -> Option { + let item_type = weapon::WeaponType::parse_type([data[0],data[1],data[2]]).map(ItemType::Weapon) + .or_else(|_| armor::ArmorType::parse_type([data[0],data[1],data[2]]).map(ItemType::Armor)) + .or_else(|_| shield::ShieldType::parse_type([data[0],data[1],data[2]]).map(ItemType::Shield)) + .or_else(|_| unit::UnitType::parse_type([data[0],data[1],data[2]]).map(ItemType::Unit)) + .or_else(|_| mag::MagType::parse_type([data[0],data[1],data[2]]).map(ItemType::Mag)) + .or_else(|_| tool::ToolType::parse_type([data[0],data[1],data[2]]).map(ItemType::Tool)) + .or_else(|_| esweapon::ESWeaponType::parse_type([data[0],data[1],data[2]]).map(ItemType::ESWeapon)).ok()?; + + match item_type { + ItemType::Weapon(_w) => Some(ItemDropType::Weapon(weapon::Weapon::from_bytes(data).ok()?)), + ItemType::Armor(_a) => Some(ItemDropType::Armor(armor::Armor::from_bytes(data).ok()?)), + ItemType::Shield(_s) => Some(ItemDropType::Shield(shield::Shield::from_bytes(data).ok()?)), + ItemType::Unit(_u) => Some(ItemDropType::Unit(unit::Unit::from_bytes(data).ok()?)), + ItemType::Mag(_m) => Some(ItemDropType::Mag(mag::Mag::from_bytes(data).ok()?)), + ItemType::Tool(_t) => Some(ItemDropType::Tool(tool::Tool::from_bytes(data).ok()?)), + _ => None, + } + } +} + #[derive(Clone, Debug)] pub struct ItemDrop { pub map_area: MapArea, @@ -112,7 +121,12 @@ pub struct ItemDrop { } -pub struct DropTable { +pub trait DropTable { + fn get_drop(&mut self, map_area: &MapArea, monster: &MonsterType) -> Option; + fn get_box_drop(&mut self, map_area: &MapArea, object: &MapObject) -> Option; +} + +pub struct StandardDropTable { monster_stats: HashMap, rare_table: RareDropTable, weapon_table: GenericWeaponTable, @@ -124,11 +138,12 @@ pub struct DropTable { rng: rand_chacha::ChaCha20Rng, } -impl DropTable { - pub fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> DropTable { +impl StandardDropTable { + #[allow(clippy::new_ret_no_self)] + pub fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> Box { let monster_stats: HashMap = load_data_file(episode, difficulty, section_id, "monster_dar.toml"); - - DropTable { + + Box::new(StandardDropTable { monster_stats: monster_stats.into_iter().map(|(m, s)| (m.parse().unwrap(), s)).collect(), rare_table: RareDropTable::new(episode, difficulty, section_id), weapon_table: GenericWeaponTable::new(episode, difficulty, section_id), @@ -138,7 +153,7 @@ impl DropTable { tool_table: ToolTable::new(episode, difficulty, section_id), box_table: BoxDropTable::new(episode, difficulty, section_id), rng: rand_chacha::ChaCha20Rng::from_entropy(), - } + }) } pub fn builder() -> DropTableBuilder { @@ -168,8 +183,10 @@ impl DropTable { MonsterDropType::None => None, } } +} - pub fn get_drop(&mut self, map_area: &MapArea, monster: &MonsterType) -> Option { +impl DropTable for StandardDropTable { + fn get_drop(&mut self, map_area: &MapArea, monster: &MonsterType) -> Option { let monster_stat = *self.monster_stats.get(monster)?; let drop_anything = self.rng.gen_range(0, 100); @@ -182,7 +199,7 @@ impl DropTable { } let drop_type = self.rng.gen_range(0, 3); - + match drop_type { 0 => { self.generate_meseta(&monster_stat) @@ -197,7 +214,7 @@ impl DropTable { } } - pub fn get_box_drop(&mut self, map_area: &MapArea, object: &MapObject) -> Option { + fn get_box_drop(&mut self, map_area: &MapArea, object: &MapObject) -> Option { self.box_table.get_drop(map_area, object, &mut self.rng) } } @@ -244,8 +261,8 @@ impl DropTableBuilder { self } - pub fn build(self, episode: Episode, difficulty: Difficulty, section_id: SectionID) -> DropTable { - DropTable { + pub fn build(self, episode: Episode, difficulty: Difficulty, section_id: SectionID) -> Box { + Box::new(StandardDropTable { monster_stats: self.monster_stats.unwrap_or_else(|| { let monster_stats: HashMap = load_data_file(episode, difficulty, section_id, "monster_dar.toml"); monster_stats.into_iter().map(|(m, s)| (m.parse().unwrap(), s)).collect() @@ -258,11 +275,10 @@ impl DropTableBuilder { tool_table: self.tool_table.unwrap_or_else(|| ToolTable::new(episode, difficulty, section_id)), box_table: self.box_table.unwrap_or_else(|| BoxDropTable::new(episode, difficulty, section_id)), rng: self.rng.unwrap_or_else(rand_chacha::ChaCha20Rng::from_entropy), - } + }) } } - #[cfg(test)] mod test { use super::*; diff --git a/src/ship/drops/rare_drop_table.rs b/drops/src/rare_drop_table.rs similarity index 90% rename from src/ship/drops/rare_drop_table.rs rename to drops/src/rare_drop_table.rs index 51151ba..b9ab745 100644 --- a/src/ship/drops/rare_drop_table.rs +++ b/drops/src/rare_drop_table.rs @@ -1,20 +1,20 @@ use std::collections::HashMap; use rand::Rng; use serde::{Serialize, Deserialize}; -use crate::entity::item::weapon::{Weapon, WeaponType}; -use crate::entity::item::armor::{Armor, ArmorType}; -use crate::entity::item::shield::{Shield, ShieldType}; -use crate::entity::item::unit::{Unit, UnitType}; -use crate::entity::item::tool::{Tool, ToolType}; -use crate::entity::item::mag::{Mag, MagType}; -use crate::entity::character::SectionID; -use crate::ship::monster::MonsterType; -use crate::ship::room::{Difficulty, Episode}; -use crate::ship::map::MapArea; -use crate::ship::drops::{ItemDropType, load_data_file}; -use crate::ship::drops::generic_weapon::AttributeTable; -use crate::ship::drops::generic_armor::GenericArmorTable; -use crate::ship::drops::generic_shield::GenericShieldTable; +use entity::item::weapon::{Weapon, WeaponType}; +use entity::item::armor::{Armor, ArmorType}; +use entity::item::shield::{Shield, ShieldType}; +use entity::item::unit::{Unit, UnitType}; +use entity::item::tool::{Tool, ToolType}; +use entity::item::mag::{Mag, MagType}; +use entity::character::SectionID; +use maps::monster::MonsterType; +use maps::room::{Difficulty, Episode}; +use maps::area::MapArea; +use crate::{ItemDropType, load_data_file}; +use crate::generic_weapon::AttributeTable; +use crate::generic_armor::GenericArmorTable; +use crate::generic_shield::GenericShieldTable; type ItemParseFn = Box Option>; diff --git a/src/ship/drops/tech_table.rs b/drops/src/tech_table.rs similarity index 95% rename from src/ship/drops/tech_table.rs rename to drops/src/tech_table.rs index 6e6396f..9b1101b 100644 --- a/src/ship/drops/tech_table.rs +++ b/drops/src/tech_table.rs @@ -3,11 +3,11 @@ use serde::{Serialize, Deserialize}; use rand::{Rng}; use rand::distributions::{WeightedIndex, Distribution}; -use crate::entity::item::tech::{Technique, TechniqueDisk}; -use crate::ship::room::{Difficulty, Episode}; -use crate::ship::map::MapArea; -use crate::entity::character::SectionID; -use crate::ship::drops::{ItemDropType, load_data_file}; +use entity::item::tech::{Technique, TechniqueDisk}; +use maps::room::{Difficulty, Episode}; +use maps::area::MapArea; +use entity::character::SectionID; +use crate::{ItemDropType, load_data_file}; diff --git a/src/ship/drops/tool_table.rs b/drops/src/tool_table.rs similarity index 94% rename from src/ship/drops/tool_table.rs rename to drops/src/tool_table.rs index 69a14cd..9645c34 100644 --- a/src/ship/drops/tool_table.rs +++ b/drops/src/tool_table.rs @@ -1,14 +1,14 @@ -use std::collections::{BTreeMap}; +use std::collections::BTreeMap; use serde::{Serialize, Deserialize}; -use rand::{Rng}; +use rand::Rng; use rand::distributions::{WeightedIndex, Distribution}; -use crate::entity::item::tool::{Tool, ToolType}; -use crate::ship::room::{Difficulty, Episode}; -use crate::ship::map::MapArea; -use crate::entity::character::SectionID; -use crate::ship::drops::{ItemDropType, load_data_file}; -use crate::ship::drops::tech_table::TechniqueTable; +use entity::item::tool::{Tool, ToolType}; +use maps::room::{Difficulty, Episode}; +use maps::area::MapArea; +use entity::character::SectionID; +use crate::{ItemDropType, load_data_file}; +use crate::tech_table::TechniqueTable; #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, enum_utils::FromStr)] diff --git a/entity/Cargo.toml b/entity/Cargo.toml new file mode 100644 index 0000000..ce90e98 --- /dev/null +++ b/entity/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "entity" +version = "0.1.0" +edition = "2021" + +[dependencies] +libpso = { workspace = true } +maps = { workspace = true } +chrono = { workspace = true } +anyhow = { workspace = true } +async-std = { workspace = true } +sqlx = { workspace = true } +thiserror = { workspace = true } +serde = { workspace = true } +async-trait = { workspace = true } +enum-utils = { workspace = true } +derive_more = { workspace = true } +refinery = { workspace = true } +lazy_static = { workspace = true } +futures = { workspace = true } +strum = { workspace = true } +strum_macros = { workspace = true } +toml = { workspace = true } diff --git a/src/entity/account.rs b/entity/src/account.rs similarity index 100% rename from src/entity/account.rs rename to entity/src/account.rs diff --git a/src/entity/character.rs b/entity/src/character.rs similarity index 98% rename from src/entity/character.rs rename to entity/src/character.rs index 080bb1b..643e009 100644 --- a/src/entity/character.rs +++ b/entity/src/character.rs @@ -4,8 +4,8 @@ use serde::{Serialize, Deserialize}; use libpso::packet::ship::{UpdateConfig, WriteInfoboard}; use libpso::character::settings::{DEFAULT_PALETTE_CONFIG, DEFAULT_TECH_MENU}; -use crate::entity::item::tech::Technique; -use crate::entity::account::UserAccountId; +use crate::item::tech::Technique; +use crate::account::UserAccountId; #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, enum_utils::FromStr, derive_more::Display, Serialize, Deserialize, Default)] pub enum CharacterClass { diff --git a/src/entity/gateway/entitygateway.rs b/entity/src/gateway/entitygateway.rs similarity index 98% rename from src/entity/gateway/entitygateway.rs rename to entity/src/gateway/entitygateway.rs index df922ac..4f7ae67 100644 --- a/src/entity/gateway/entitygateway.rs +++ b/entity/src/gateway/entitygateway.rs @@ -1,10 +1,10 @@ use thiserror::Error; use futures::future::{Future, BoxFuture}; -use crate::entity::account::*; -use crate::entity::character::*; -use crate::entity::item::*; -use crate::entity::room::*; +use crate::account::*; +use crate::character::*; +use crate::item::*; +use crate::room::*; // TODO: better granularity? diff --git a/src/entity/gateway/inmemory.rs b/entity/src/gateway/inmemory.rs similarity index 99% rename from src/entity/gateway/inmemory.rs rename to entity/src/gateway/inmemory.rs index 50a650e..970beed 100644 --- a/src/entity/gateway/inmemory.rs +++ b/entity/src/gateway/inmemory.rs @@ -2,11 +2,11 @@ use std::collections::BTreeMap; use std::convert::TryInto; use futures::future::{Future, BoxFuture}; -use crate::entity::account::*; -use crate::entity::character::*; -use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction, GatewayError}; -use crate::entity::item::*; -use crate::entity::room::*; +use crate::account::*; +use crate::character::*; +use crate::gateway::{EntityGateway, EntityGatewayTransaction, GatewayError}; +use crate::item::*; +use crate::room::*; use async_std::sync::{Arc, Mutex}; diff --git a/src/entity/gateway/mod.rs b/entity/src/gateway/mod.rs similarity index 100% rename from src/entity/gateway/mod.rs rename to entity/src/gateway/mod.rs diff --git a/src/entity/gateway/postgres/migrations/V0001__initial.sql b/entity/src/gateway/postgres/migrations/V0001__initial.sql similarity index 100% rename from src/entity/gateway/postgres/migrations/V0001__initial.sql rename to entity/src/gateway/postgres/migrations/V0001__initial.sql diff --git a/src/entity/gateway/postgres/migrations/V0002__equips.sql b/entity/src/gateway/postgres/migrations/V0002__equips.sql similarity index 100% rename from src/entity/gateway/postgres/migrations/V0002__equips.sql rename to entity/src/gateway/postgres/migrations/V0002__equips.sql diff --git a/src/entity/gateway/postgres/migrations/V0003__item_notes.sql b/entity/src/gateway/postgres/migrations/V0003__item_notes.sql similarity index 100% rename from src/entity/gateway/postgres/migrations/V0003__item_notes.sql rename to entity/src/gateway/postgres/migrations/V0003__item_notes.sql diff --git a/src/entity/gateway/postgres/migrations/V0004__meseta.sql b/entity/src/gateway/postgres/migrations/V0004__meseta.sql similarity index 100% rename from src/entity/gateway/postgres/migrations/V0004__meseta.sql rename to entity/src/gateway/postgres/migrations/V0004__meseta.sql diff --git a/src/entity/gateway/postgres/migrations/V0005__trade.sql b/entity/src/gateway/postgres/migrations/V0005__trade.sql similarity index 100% rename from src/entity/gateway/postgres/migrations/V0005__trade.sql rename to entity/src/gateway/postgres/migrations/V0005__trade.sql diff --git a/src/entity/gateway/postgres/migrations/V0006__playtime.sql b/entity/src/gateway/postgres/migrations/V0006__playtime.sql similarity index 100% rename from src/entity/gateway/postgres/migrations/V0006__playtime.sql rename to entity/src/gateway/postgres/migrations/V0006__playtime.sql diff --git a/src/entity/gateway/postgres/migrations/V0007__player_keyconfig.sql b/entity/src/gateway/postgres/migrations/V0007__player_keyconfig.sql similarity index 100% rename from src/entity/gateway/postgres/migrations/V0007__player_keyconfig.sql rename to entity/src/gateway/postgres/migrations/V0007__player_keyconfig.sql diff --git a/src/entity/gateway/postgres/migrations/V0008__playtime_not_null.sql b/entity/src/gateway/postgres/migrations/V0008__playtime_not_null.sql similarity index 100% rename from src/entity/gateway/postgres/migrations/V0008__playtime_not_null.sql rename to entity/src/gateway/postgres/migrations/V0008__playtime_not_null.sql diff --git a/src/entity/gateway/postgres/migrations/V0009__no_player_keyconfig.sql b/entity/src/gateway/postgres/migrations/V0009__no_player_keyconfig.sql similarity index 100% rename from src/entity/gateway/postgres/migrations/V0009__no_player_keyconfig.sql rename to entity/src/gateway/postgres/migrations/V0009__no_player_keyconfig.sql diff --git a/src/entity/gateway/postgres/migrations/V0010__char_create_timestamp.sql b/entity/src/gateway/postgres/migrations/V0010__char_create_timestamp.sql similarity index 100% rename from src/entity/gateway/postgres/migrations/V0010__char_create_timestamp.sql rename to entity/src/gateway/postgres/migrations/V0010__char_create_timestamp.sql diff --git a/src/entity/gateway/postgres/migrations/V0011__shared_bank.sql b/entity/src/gateway/postgres/migrations/V0011__shared_bank.sql similarity index 100% rename from src/entity/gateway/postgres/migrations/V0011__shared_bank.sql rename to entity/src/gateway/postgres/migrations/V0011__shared_bank.sql diff --git a/src/entity/gateway/postgres/migrations/V0012__room.sql b/entity/src/gateway/postgres/migrations/V0012__room.sql similarity index 100% rename from src/entity/gateway/postgres/migrations/V0012__room.sql rename to entity/src/gateway/postgres/migrations/V0012__room.sql diff --git a/src/entity/gateway/postgres/migrations/V0013__room2.sql b/entity/src/gateway/postgres/migrations/V0013__room2.sql similarity index 100% rename from src/entity/gateway/postgres/migrations/V0013__room2.sql rename to entity/src/gateway/postgres/migrations/V0013__room2.sql diff --git a/entity/src/gateway/postgres/migrations/mod.rs b/entity/src/gateway/postgres/migrations/mod.rs new file mode 100644 index 0000000..fd67f6f --- /dev/null +++ b/entity/src/gateway/postgres/migrations/mod.rs @@ -0,0 +1,3 @@ +use refinery::include_migration_mods; + +include_migration_mods!("src/gateway/postgres/migrations"); diff --git a/src/entity/gateway/postgres/mod.rs b/entity/src/gateway/postgres/mod.rs similarity index 100% rename from src/entity/gateway/postgres/mod.rs rename to entity/src/gateway/postgres/mod.rs diff --git a/src/entity/gateway/postgres/models.rs b/entity/src/gateway/postgres/models.rs similarity index 99% rename from src/entity/gateway/postgres/models.rs rename to entity/src/gateway/postgres/models.rs index f3a2f39..13c2a6b 100644 --- a/src/entity/gateway/postgres/models.rs +++ b/entity/src/gateway/postgres/models.rs @@ -4,13 +4,13 @@ use std::convert::Into; use serde::{Serialize, Deserialize}; use libpso::character::settings; use libpso::util::vec_to_array; -use crate::entity::account::*; -use crate::entity::character::*; -use crate::entity::item::*; -use crate::entity::room::*; -use crate::ship::map::MapArea; -use crate::ship::room::{Episode, Difficulty}; -use crate::ship::monster::MonsterType; +use crate::account::*; +use crate::character::*; +use crate::item::*; +use crate::room::*; +use maps::area::MapArea; +use maps::room::{Episode, Difficulty}; +use maps::monster::MonsterType; #[derive(Debug, sqlx::FromRow)] pub struct PgUserAccount { diff --git a/src/entity/gateway/postgres/postgres.rs b/entity/src/gateway/postgres/postgres.rs similarity index 99% rename from src/entity/gateway/postgres/postgres.rs rename to entity/src/gateway/postgres/postgres.rs index c31217f..f1219ba 100644 --- a/src/entity/gateway/postgres/postgres.rs +++ b/entity/src/gateway/postgres/postgres.rs @@ -1,16 +1,16 @@ // this lint is currently bugged and suggests incorrect code https://github.com/rust-lang/rust-clippy/issues/9123 #![allow(clippy::explicit_auto_deref)] -use std::convert::{From, TryFrom, Into}; +use std::convert::{From, Into}; use futures::future::{Future, BoxFuture}; use futures::stream::{StreamExt, FuturesOrdered}; use async_std::sync::{Arc, Mutex}; use libpso::character::guildcard; -use crate::entity::account::*; -use crate::entity::character::*; -use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction, GatewayError}; -use crate::entity::item::*; -use crate::entity::room::*; +use crate::account::*; +use crate::character::*; +use crate::gateway::{EntityGateway, EntityGatewayTransaction, GatewayError}; +use crate::item::*; +use crate::room::*; use super::models::*; use sqlx::postgres::PgPoolOptions; @@ -19,7 +19,7 @@ use sqlx::Connection; mod embedded { use refinery::embed_migrations; - embed_migrations!("src/entity/gateway/postgres/migrations"); + embed_migrations!("src/gateway/postgres/migrations"); } diff --git a/src/entity/item/armor.rs b/entity/src/item/armor.rs similarity index 99% rename from src/entity/item/armor.rs rename to entity/src/item/armor.rs index 45e9ed4..bd0cc48 100644 --- a/src/entity/item/armor.rs +++ b/entity/src/item/armor.rs @@ -1,5 +1,5 @@ use serde::{Serialize, Deserialize}; -use crate::entity::item::ItemEntityId; +use crate::item::ItemEntityId; #[derive(Debug, Copy, Clone)] pub enum ItemParseError { diff --git a/src/entity/item/esweapon.rs b/entity/src/item/esweapon.rs similarity index 100% rename from src/entity/item/esweapon.rs rename to entity/src/item/esweapon.rs diff --git a/src/entity/item/mag.rs b/entity/src/item/mag.rs similarity index 99% rename from src/entity/item/mag.rs rename to entity/src/item/mag.rs index 17fa1b4..45d424a 100644 --- a/src/entity/item/mag.rs +++ b/entity/src/item/mag.rs @@ -1,9 +1,9 @@ -use thiserror::Error; use std::collections::HashMap; +use thiserror::Error; use serde::{Serialize, Deserialize}; -use crate::entity::item::tool::ToolType; -use crate::entity::character::{CharacterClass, SectionID}; -use crate::entity::item::ItemEntityId; +use crate::item::tool::ToolType; +use crate::character::{CharacterClass, SectionID}; +use crate::item::ItemEntityId; use std::io::Read; use std::cmp::Ordering::{Less, Greater, Equal}; diff --git a/src/entity/item/mod.rs b/entity/src/item/mod.rs similarity index 84% rename from src/entity/item/mod.rs rename to entity/src/item/mod.rs index 7023f7c..d418579 100644 --- a/src/entity/item/mod.rs +++ b/entity/src/item/mod.rs @@ -9,11 +9,11 @@ pub mod mag; pub mod esweapon; use serde::{Serialize, Deserialize}; -use crate::entity::character::CharacterEntityId; -use crate::entity::room::RoomEntityId; -use crate::ship::map::MapArea; -use crate::ship::monster::MonsterType; -use crate::ship::drops::ItemDropType; +use crate::character::CharacterEntityId; +use crate::room::RoomEntityId; +use maps::area::MapArea; +use maps::monster::MonsterType; +//use crate::ship::drops::ItemDropType; #[derive(PartialEq, Eq, Copy, Clone, Debug, Hash, PartialOrd, Ord, Serialize, Deserialize)] pub struct ItemEntityId(pub u32); @@ -156,26 +156,6 @@ impl ItemDetail { } } - pub fn parse_item_from_bytes(data: [u8; 16]) -> Option { - let item_type = weapon::WeaponType::parse_type([data[0],data[1],data[2]]).map(ItemType::Weapon) - .or_else(|_| armor::ArmorType::parse_type([data[0],data[1],data[2]]).map(ItemType::Armor)) - .or_else(|_| shield::ShieldType::parse_type([data[0],data[1],data[2]]).map(ItemType::Shield)) - .or_else(|_| unit::UnitType::parse_type([data[0],data[1],data[2]]).map(ItemType::Unit)) - .or_else(|_| mag::MagType::parse_type([data[0],data[1],data[2]]).map(ItemType::Mag)) - .or_else(|_| tool::ToolType::parse_type([data[0],data[1],data[2]]).map(ItemType::Tool)) - .or_else(|_| esweapon::ESWeaponType::parse_type([data[0],data[1],data[2]]).map(ItemType::ESWeapon)).ok()?; - - match item_type { - ItemType::Weapon(_w) => Some(ItemDropType::Weapon(weapon::Weapon::from_bytes(data).ok()?)), - ItemType::Armor(_a) => Some(ItemDropType::Armor(armor::Armor::from_bytes(data).ok()?)), - ItemType::Shield(_s) => Some(ItemDropType::Shield(shield::Shield::from_bytes(data).ok()?)), - ItemType::Unit(_u) => Some(ItemDropType::Unit(unit::Unit::from_bytes(data).ok()?)), - ItemType::Mag(_m) => Some(ItemDropType::Mag(mag::Mag::from_bytes(data).ok()?)), - ItemType::Tool(_t) => Some(ItemDropType::Tool(tool::Tool::from_bytes(data).ok()?)), - _ => None, - } - } - pub fn as_client_bytes(&self) -> [u8; 16] { match self { ItemDetail::Weapon(w) => w.as_bytes(), diff --git a/src/entity/item/shield.rs b/entity/src/item/shield.rs similarity index 100% rename from src/entity/item/shield.rs rename to entity/src/item/shield.rs diff --git a/src/entity/item/tech.rs b/entity/src/item/tech.rs similarity index 100% rename from src/entity/item/tech.rs rename to entity/src/item/tech.rs diff --git a/src/entity/item/tool.rs b/entity/src/item/tool.rs similarity index 100% rename from src/entity/item/tool.rs rename to entity/src/item/tool.rs diff --git a/src/entity/item/unit.rs b/entity/src/item/unit.rs similarity index 100% rename from src/entity/item/unit.rs rename to entity/src/item/unit.rs diff --git a/src/entity/item/weapon.rs b/entity/src/item/weapon.rs similarity index 99% rename from src/entity/item/weapon.rs rename to entity/src/item/weapon.rs index 29a9357..a5753d6 100644 --- a/src/entity/item/weapon.rs +++ b/entity/src/item/weapon.rs @@ -1,4 +1,4 @@ -use crate::entity::item::ItemEntityId; +use crate::item::ItemEntityId; use serde::{Serialize, Deserialize}; #[derive(Debug, Copy, Clone)] diff --git a/src/entity/mod.rs b/entity/src/lib.rs similarity index 100% rename from src/entity/mod.rs rename to entity/src/lib.rs diff --git a/src/entity/room.rs b/entity/src/room.rs similarity index 93% rename from src/entity/room.rs rename to entity/src/room.rs index a5eeea4..30d3b6b 100644 --- a/src/entity/room.rs +++ b/entity/src/room.rs @@ -1,8 +1,8 @@ use serde::{Serialize, Deserialize}; -use crate::entity::character::{CharacterEntityId, SectionID}; -use crate::ship::room::{Episode, Difficulty}; +use crate::character::{CharacterEntityId, SectionID}; +use maps::room::{Episode, Difficulty}; #[derive(PartialEq, Eq, Copy, Clone, Debug, Hash, PartialOrd, Ord, Serialize, Deserialize)] diff --git a/entity/src/team.rs b/entity/src/team.rs new file mode 100644 index 0000000..2a5bfac --- /dev/null +++ b/entity/src/team.rs @@ -0,0 +1,31 @@ +use serde::{Serialize, Deserialize}; + +use super::account::UserAccountId; + +// [2022-10-23 00:11:18][elseware::common::mainloop::client][WARN] error RecvServerPacket::from_bytes: WrongPacketForServerType(490, [40, 0, 234, 1, 0, 0, 0, 0, 9, 0, 74, 0, 97, 0, 115, 0, 100, 0, 102, 0, 0, 0, 0, 0, 192, 52, 67, 3, 60, 159, 129, 0, 32, 64, 233, 10, 196, 156, 152, 0]) +// [2022-10-23 00:20:14][elseware::common::mainloop::client][WARN] error RecvServerPacket::from_bytes: WrongPacketForServerType(490, [40, 0, 234, 1, 0, 0, 0, 0, 9, 0, 74, 0, 97, 0, 97, 0, 97, 0, 97, 0, 97, 0, 97, 0, 97, 0, 97, 0, 97, 0, 97, 0, 97, 0, 97, 0, 0, 0, 152, 0]) + + +#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash, PartialOrd, Ord, Serialize, Deserialize)] +pub struct TeamEntityId(pub u32); + +pub struct NewTeamEntity { + pub created_by: UserAccountId, + pub name: String, +} + + +#[derive(Debug, Clone)] +pub struct TeamEntity { + pub id: TeamEntityId, + pub owner: UserAccountId, + pub name: String, + + pub team_flag: [u8; 2048], +} + + + + + + diff --git a/items/Cargo.toml b/items/Cargo.toml new file mode 100644 index 0000000..84ce90c --- /dev/null +++ b/items/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "items" +version = "0.1.0" +edition = "2021" + +[dependencies] +entity = { workspace = true } +maps = { workspace = true } +shops = { workspace = true } +location = { workspace = true } +drops = { workspace = true } + +libpso = { workspace = true } + +enum-utils = { workspace = true } +derive_more = { workspace = true } +serde = { workspace = true } +rand = { workspace = true } +rand_chacha = { workspace = true } +async-recursion = { workspace = true } +async-std = { workspace = true } +async-trait = { workspace = true } +futures = { workspace = true } +anyhow = { workspace = true } +thiserror = { workspace = true } \ No newline at end of file diff --git a/src/ship/items/actions.rs b/items/src/actions.rs similarity index 96% rename from src/ship/items/actions.rs rename to items/src/actions.rs index 0c06691..0b55d6d 100644 --- a/src/ship/items/actions.rs +++ b/items/src/actions.rs @@ -1,6 +1,6 @@ // TODO: replace various u32s and usizes denoting item amounts for ItemAmount(u32) for consistency -use crate::ship::items::ClientItemId; -use crate::entity::item::{Meseta, ItemNote}; +use crate::ClientItemId; +use entity::item::{Meseta, ItemNote}; use async_std::sync::Arc; use std::future::Future; use futures::future::BoxFuture; @@ -8,30 +8,33 @@ use std::pin::Pin; use std::iter::IntoIterator; use anyhow::Context; -use libpso::packet::{ship::Message, messages::GameMessage}; -use crate::entity::character::{CharacterEntity, CharacterEntityId}; -use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction}; -use crate::entity::item::{ItemDetail, NewItemEntity, TradeId, ItemModifier}; -use crate::entity::item::tool::Tool; -use crate::entity::room::RoomEntityId; -use crate::ship::map::MapArea; -use crate::ship::ship::SendShipPacket; -use crate::ship::items::state::{ItemStateProxy, ItemStateError, AddItemResult, StackedItemDetail, IndividualItemDetail}; -use crate::ship::items::bank::{BankItem, BankItemDetail}; -use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail}; -use crate::ship::items::floor::{FloorItem, FloorItemDetail}; -use crate::ship::items::apply_item::{apply_item, ApplyItemAction}; -use crate::ship::shops::ShopItem; -use crate::ship::drops::{ItemDrop, ItemDropType}; -use crate::ship::packet::builder; -use crate::ship::location::AreaClient; -use crate::ship::monster::MonsterType; +use entity::character::{CharacterEntity, CharacterEntityId}; +use entity::gateway::{EntityGateway, EntityGatewayTransaction}; +use entity::item::{ItemDetail, NewItemEntity, TradeId, ItemModifier}; +use entity::item::tool::Tool; +use entity::room::RoomEntityId; +use maps::area::MapArea; +use crate::state::{ItemStateProxy, ItemStateError, AddItemResult, StackedItemDetail, IndividualItemDetail}; +use crate::bank::{BankItem, BankItemDetail}; +use crate::inventory::{InventoryItem, InventoryItemDetail}; +use crate::floor::{FloorItem, FloorItemDetail}; +use crate::apply_item::{apply_item, ApplyItemAction}; +use shops::ShopItem; +use drops::{ItemDrop, ItemDropType}; +use location::AreaClient; +use maps::monster::MonsterType; pub enum TriggerCreateItem { Yes, No } +#[derive(Clone)] +pub enum CreateItem { + Individual(AreaClient, ClientItemId, IndividualItemDetail), + Stacked(AreaClient, ClientItemId, Tool, usize), +} + pub(super) fn take_item_from_floor<'a, EG, TR>( character_id: CharacterEntityId, item_id: ClientItemId @@ -1144,7 +1147,7 @@ pub(super) fn apply_item_action_packets<'a, EG, TR>( character_id: CharacterEntityId, area_client: AreaClient, ) -> impl Fn((ItemStateProxy, TR), ApplyItemAction) - -> BoxFuture<'a, Result<((ItemStateProxy, TR), Vec), anyhow::Error>> + -> BoxFuture<'a, Result<((ItemStateProxy, TR), Vec), anyhow::Error>> where EG: EntityGateway, TR: EntityGatewayTransaction + 'a, @@ -1161,7 +1164,7 @@ where let (inventory_item_detail, create_item) = if item_detail.is_stackable() { let tool = item_detail.as_tool().ok_or_else(|| ItemStateError::NotATool(ClientItemId(0xFFFFFFFF)))?; - let create_item = builder::message::create_stacked_item(area_client, item_id, &tool, 1).map_err(|_err| ItemStateError::Dummy)?; + let create_item = CreateItem::Stacked(area_client, item_id, tool, 1); let item_detail = StackedItemDetail { entity_ids: vec![new_item.id], tool @@ -1173,7 +1176,7 @@ where entity_id: new_item.id, item: item_detail, }; - let create_item = builder::message::create_individual_item(area_client, item_id, &item_detail).map_err(|_err| ItemStateError::Dummy)?; + let create_item = CreateItem::Individual(area_client, item_id, item_detail.clone()); (InventoryItemDetail::Individual(item_detail), create_item) }; @@ -1187,7 +1190,8 @@ where transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?; item_state.set_inventory(inventory).await; - vec![SendShipPacket::Message(Message::new(GameMessage::CreateItem(create_item)))] + //vec![SendShipPacket::Message(Message::new(GameMessage::CreateItem(create_item)))] + vec![create_item] } else { Vec::new() diff --git a/src/ship/items/apply_item.rs b/items/src/apply_item.rs similarity index 96% rename from src/ship/items/apply_item.rs rename to items/src/apply_item.rs index 4c1a636..0f74627 100644 --- a/src/ship/items/apply_item.rs +++ b/items/src/apply_item.rs @@ -4,15 +4,15 @@ use thiserror::Error; use anyhow::Context; use rand::SeedableRng; use rand::distributions::{WeightedIndex, Distribution}; -use crate::entity::gateway::{EntityGateway, GatewayError}; -use crate::entity::character::{CharacterEntity, TechLevel}; -use crate::entity::item::mag::{MagCell, MagCellError}; -use crate::entity::item::tool::{Tool, ToolType}; -use crate::entity::item::tech::TechniqueDisk; -use crate::entity::item::{ItemDetail, ItemEntityId}; -use crate::entity::item::weapon::WeaponModifier; -use crate::ship::items::state::ItemStateProxy; -use crate::ship::items::inventory::InventoryItemDetail; +use entity::gateway::{EntityGateway, GatewayError}; +use entity::character::{CharacterEntity, TechLevel}; +use entity::item::mag::{MagCell, MagCellError}; +use entity::item::tool::{Tool, ToolType}; +use entity::item::tech::TechniqueDisk; +use entity::item::{ItemDetail, ItemEntityId}; +use entity::item::weapon::WeaponModifier; +use crate::state::ItemStateProxy; +use crate::inventory::InventoryItemDetail; #[derive(Error, Debug)] @@ -226,7 +226,7 @@ pub async fn liberta_kit(entity_gateway: &mut EG, used_cell: fn jack_o_lantern() -> Result, anyhow::Error> { - let mag_rate = WeightedIndex::new(&[13, 13, 13, 13, 12, 12, 12, 12]).unwrap(); + let mag_rate = WeightedIndex::new([13, 13, 13, 13, 12, 12, 12, 12]).unwrap(); let mag_type = match mag_rate.sample(&mut rand_chacha::ChaChaRng::from_entropy()) { 0 => ToolType::CellOfMag502, 1 => ToolType::CellOfMag213, diff --git a/src/ship/items/bank.rs b/items/src/bank.rs similarity index 93% rename from src/ship/items/bank.rs rename to items/src/bank.rs index 73f0e10..e68700a 100644 --- a/src/ship/items/bank.rs +++ b/items/src/bank.rs @@ -1,15 +1,15 @@ use std::cmp::Ordering; use libpso::character::character; -use crate::ship::items::ClientItemId; -use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, BankEntity, BankItemEntity}; +use crate::ClientItemId; +use entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, BankEntity, BankItemEntity}; use std::future::Future; use async_std::sync::{Arc, Mutex}; -use crate::entity::character::CharacterEntityId; -use crate::entity::item::BankIdentifier; -use crate::ship::items::state::ItemStateError; -use crate::ship::items::state::{IndividualItemDetail, StackedItemDetail, AddItemResult}; -use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail}; +use entity::character::CharacterEntityId; +use entity::item::BankIdentifier; +use crate::state::ItemStateError; +use crate::state::{IndividualItemDetail, StackedItemDetail, AddItemResult}; +use crate::inventory::{InventoryItem, InventoryItemDetail}; #[derive(thiserror::Error, Debug)] @@ -325,15 +325,7 @@ impl std::cmp::Eq for BankItemDetail {} impl std::cmp::PartialOrd for BankItemDetail { fn partial_cmp(&self, other: &BankItemDetail) -> Option { - let mut self_bytes = [0u8; 4]; - let mut other_bytes = [0u8; 4]; - self_bytes.copy_from_slice(&self.as_client_bytes()[0..4]); - other_bytes.copy_from_slice(&other.as_client_bytes()[0..4]); - - let self_value = u32::from_be_bytes(self_bytes); - let other_value = u32::from_be_bytes(other_bytes); - - self_value.partial_cmp(&other_value) + Some(self.cmp(other)) } } @@ -344,8 +336,8 @@ impl std::cmp::Ord for BankItemDetail { self_bytes.copy_from_slice(&self.as_client_bytes()[0..4]); other_bytes.copy_from_slice(&other.as_client_bytes()[0..4]); - let self_value = u32::from_le_bytes(self_bytes); - let other_value = u32::from_le_bytes(other_bytes); + let self_value = u32::from_be_bytes(self_bytes); + let other_value = u32::from_be_bytes(other_bytes); self_value.cmp(&other_value) } @@ -362,7 +354,7 @@ impl std::cmp::Eq for BankItem {} impl std::cmp::PartialOrd for BankItem { fn partial_cmp(&self, other: &BankItem) -> Option { - self.item.partial_cmp(&other.item) + Some(self.cmp(other)) } } diff --git a/src/ship/items/floor.rs b/items/src/floor.rs similarity index 88% rename from src/ship/items/floor.rs rename to items/src/floor.rs index c71b7fd..29b79fa 100644 --- a/src/ship/items/floor.rs +++ b/items/src/floor.rs @@ -1,14 +1,14 @@ -use crate::ship::items::ClientItemId; -use crate::entity::item::{Meseta, ItemEntityId, ItemDetail}; +use crate::ClientItemId; +use entity::item::{Meseta, ItemEntityId, ItemDetail}; use std::future::Future; -use crate::ship::map::MapArea; -use crate::entity::character::CharacterEntityId; -use crate::entity::item::mag::Mag; +use maps::area::MapArea; +use entity::character::CharacterEntityId; +use entity::item::mag::Mag; -use crate::ship::items::state::ItemStateError; -use crate::ship::items::state::{IndividualItemDetail, StackedItemDetail}; -use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail}; +use crate::state::ItemStateError; +use crate::state::{IndividualItemDetail, StackedItemDetail}; +use crate::inventory::{InventoryItem, InventoryItemDetail}; pub enum FloorType { Local, @@ -96,13 +96,13 @@ pub struct FloorState { impl FloorState { pub fn take_item(&mut self, item_id: &ClientItemId) -> Option { let item = self.local.0 - .drain_filter(|item| { + .extract_if(|item| { item.item_id == *item_id }) .next(); item.or_else(|| { self.shared.0 - .drain_filter(|item| { + .extract_if(|item| { item.item_id == *item_id }) .next() diff --git a/src/ship/items/inventory.rs b/items/src/inventory.rs similarity index 97% rename from src/ship/items/inventory.rs rename to items/src/inventory.rs index da131d8..d8c0da9 100644 --- a/src/ship/items/inventory.rs +++ b/items/src/inventory.rs @@ -1,18 +1,18 @@ use std::cmp::Ordering; use libpso::character::character; -use crate::ship::items::ClientItemId; -use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, InventoryEntity, InventoryItemEntity, EquippedEntity}; +use crate::ClientItemId; +use entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, InventoryEntity, InventoryItemEntity, EquippedEntity}; use std::future::Future; use async_std::sync::{Arc, Mutex}; -use crate::entity::character::CharacterEntityId; -use crate::entity::item::tool::ToolType; -use crate::entity::item::mag::Mag; -use crate::entity::item::weapon::Weapon; -use crate::ship::shops::{ShopItem, ArmorShopItem, ToolShopItem, WeaponShopItem}; -use crate::ship::items::state::ItemStateError; -use crate::ship::items::state::{IndividualItemDetail, StackedItemDetail, AddItemResult}; -use crate::ship::items::floor::{FloorItem, FloorItemDetail}; +use entity::character::CharacterEntityId; +use entity::item::tool::ToolType; +use entity::item::mag::Mag; +use entity::item::weapon::Weapon; +use shops::{ShopItem, ArmorShopItem, ToolShopItem, WeaponShopItem}; +use crate::state::ItemStateError; +use crate::state::{IndividualItemDetail, StackedItemDetail, AddItemResult}; +use crate::floor::{FloorItem, FloorItemDetail}; #[derive(Clone, Debug)] pub enum InventoryItemDetail { diff --git a/src/ship/items/itemstateaction.rs b/items/src/itemstateaction.rs similarity index 100% rename from src/ship/items/itemstateaction.rs rename to items/src/itemstateaction.rs diff --git a/src/ship/items/mod.rs b/items/src/lib.rs similarity index 87% rename from src/ship/items/mod.rs rename to items/src/lib.rs index 549ee51..7f4251f 100644 --- a/src/ship/items/mod.rs +++ b/items/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(extract_if)] + pub mod state; pub mod actions; pub mod apply_item; @@ -6,6 +8,7 @@ pub mod inventory; pub mod floor; pub mod bank; pub mod tasks; +pub mod trade; #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize, derive_more::Display)] pub struct ClientItemId(pub u32); diff --git a/src/ship/items/manager.rs b/items/src/manager.rs similarity index 99% rename from src/ship/items/manager.rs rename to items/src/manager.rs index e9251c4..d368ec6 100644 --- a/src/ship/items/manager.rs +++ b/items/src/manager.rs @@ -1,4 +1,4 @@ -use crate::ship::items::ClientItemId; +use crate::ClientItemId; use std::collections::HashMap; use std::cmp::Ordering; use std::cell::RefCell; @@ -9,18 +9,18 @@ use crate::entity::item::{ItemDetail, ItemNote, BankName}; use crate::entity::item::{Meseta, NewItemEntity, ItemEntity, InventoryItemEntity, BankItemEntity}; use crate::entity::item::tool::{Tool, ToolType}; use crate::entity::item::weapon; -use crate::ship::map::MapArea; +use maps::area::MapArea; use crate::ship::ship::ItemDropLocation; use crate::ship::trade::TradeItem; -use crate::ship::drops::{ItemDrop, ItemDropType}; -use crate::ship::location::{AreaClient, RoomId}; -use crate::ship::shops::ShopItem; +use drops::{ItemDrop, ItemDropType}; +use location::{AreaClient, RoomId}; +use shops::ShopItem; use crate::ship::packet::handler::trade::{TradeError, OTHER_MESETA_ITEM_ID}; -use crate::ship::items::bank::*; -use crate::ship::items::floor::*; -use crate::ship::items::inventory::*; -use crate::ship::items::transaction::{ItemTransaction, ItemAction, TransactionError, TransactionCommitError}; +use crate::bank::*; +use crate::floor::*; +use crate::inventory::*; +use crate::transaction::{ItemTransaction, ItemAction, TransactionError, TransactionCommitError}; #[derive(PartialEq, Eq)] pub enum FloorType { diff --git a/src/ship/items/state.rs b/items/src/state.rs similarity index 95% rename from src/ship/items/state.rs rename to items/src/state.rs index 906762a..aa27143 100644 --- a/src/ship/items/state.rs +++ b/items/src/state.rs @@ -4,18 +4,18 @@ use async_std::sync::{Arc, RwLock, Mutex}; use futures::stream::{FuturesOrdered, StreamExt}; use anyhow::Context; -use crate::entity::gateway::{EntityGateway, GatewayError}; -use crate::entity::character::{CharacterEntity, CharacterEntityId}; -use crate::entity::item::{ItemEntityId, ItemDetail, ItemEntity, InventoryItemEntity, BankItemEntity, BankIdentifier}; -use crate::entity::item::tool::Tool; -use crate::entity::item::weapon::Weapon; -use crate::entity::item::mag::Mag; -use crate::ship::drops::ItemDrop; -use crate::ship::items::ClientItemId; -use crate::ship::items::inventory::{Inventory, InventoryItem, InventoryItemDetail, InventoryError, InventoryState}; -use crate::ship::items::floor::{FloorState, FloorItem, LocalFloor, SharedFloor, FloorType}; -use crate::ship::items::bank::{Bank, BankState, BankItem, BankItemDetail, BankError}; -use crate::ship::location::{AreaClient, RoomId}; +use entity::gateway::{EntityGateway, GatewayError}; +use entity::character::{CharacterEntity, CharacterEntityId}; +use entity::item::{ItemEntityId, ItemDetail, ItemEntity, InventoryItemEntity, BankItemEntity, BankIdentifier}; +use entity::item::tool::Tool; +use entity::item::weapon::Weapon; +use entity::item::mag::Mag; +use drops::ItemDrop; +use crate::ClientItemId; +use crate::inventory::{Inventory, InventoryItem, InventoryItemDetail, InventoryError, InventoryState}; +use crate::floor::{FloorState, FloorItem, LocalFloor, SharedFloor, FloorType}; +use crate::bank::{Bank, BankState, BankItem, BankItemDetail, BankError}; +use location::{AreaClient, RoomId}; #[derive(thiserror::Error, Debug)] pub enum ItemStateError { @@ -50,7 +50,7 @@ pub enum ItemStateError { #[error("stacked item")] StackedItemError(Vec), #[error("apply item {0}")] - ApplyItemError(#[from] crate::ship::items::apply_item::ApplyItemError), + ApplyItemError(#[from] crate::apply_item::ApplyItemError), #[error("item is not a mag {0}")] NotAMag(ClientItemId), #[error("item is not mag food {0}")] diff --git a/src/ship/items/tasks.rs b/items/src/tasks.rs similarity index 95% rename from src/ship/items/tasks.rs rename to items/src/tasks.rs index 0c3f948..cf313fc 100644 --- a/src/ship/items/tasks.rs +++ b/items/src/tasks.rs @@ -1,24 +1,23 @@ use futures::future::BoxFuture; -use crate::ship::items::ClientItemId; -use crate::entity::item::Meseta; - -use crate::ship::ship::SendShipPacket; -use crate::ship::map::MapArea; -use crate::entity::character::{CharacterEntity, CharacterEntityId}; -use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction}; -use crate::entity::item::ItemModifier; -use crate::entity::room::RoomEntityId; -use crate::ship::items::state::{ItemState, ItemStateProxy, IndividualItemDetail}; -use crate::ship::items::itemstateaction::{ItemStateAction, ItemAction}; -use crate::ship::items::inventory::InventoryItem; -use crate::ship::items::floor::FloorItem; -use crate::ship::shops::ShopItem; -use crate::ship::trade::TradeItem; -use crate::ship::location::AreaClient; -use crate::ship::drops::ItemDrop; -use crate::ship::monster::MonsterType; - -use crate::ship::items::actions; +use crate::ClientItemId; +use entity::item::Meseta; + +use maps::area::MapArea; +use entity::character::{CharacterEntity, CharacterEntityId}; +use entity::gateway::{EntityGateway, EntityGatewayTransaction}; +use entity::item::ItemModifier; +use entity::room::RoomEntityId; +use crate::state::{ItemState, ItemStateProxy, IndividualItemDetail}; +use crate::itemstateaction::{ItemStateAction, ItemAction}; +use crate::inventory::InventoryItem; +use crate::floor::FloorItem; +use shops::ShopItem; +use crate::trade::TradeItem; +use location::AreaClient; +use drops::ItemDrop; +use maps::monster::MonsterType; + +use crate::actions; pub fn pick_up_item<'a, EG>( item_state: &'a mut ItemState, @@ -278,7 +277,7 @@ pub fn use_item<'a, EG> ( area_client: AreaClient, item_id: &'a ClientItemId, amount: u32, -) -> BoxFuture<'a, Result, anyhow::Error>> +) -> BoxFuture<'a, Result, anyhow::Error>> where EG: EntityGateway + 'static, { @@ -372,6 +371,8 @@ where Ok((transaction, result)) }) } + +#[allow(clippy::type_complexity)] pub fn trade_items<'a, EG> ( item_state: &'a mut ItemState, entity_gateway: &'a mut EG, diff --git a/items/src/trade.rs b/items/src/trade.rs new file mode 100644 index 0000000..19e49bc --- /dev/null +++ b/items/src/trade.rs @@ -0,0 +1,38 @@ +use crate::ClientItemId; + +#[derive(Debug, Clone)] +pub enum TradeItem { + Individual(ClientItemId), + Stacked(ClientItemId, usize), +} + +impl TradeItem { + pub fn stacked(&self) -> Option<(ClientItemId, usize)> { + match self { + TradeItem::Stacked(item_id, amount) => Some((*item_id, *amount)), + _ => None + } + } + + pub fn stacked_mut(&mut self) -> Option<(ClientItemId, &mut usize)> { + match self { + TradeItem::Stacked(item_id, ref mut amount) => Some((*item_id, amount)), + _ => None + } + } + + pub fn item_id(&self) -> ClientItemId { + match self { + TradeItem::Individual(item_id) => *item_id, + TradeItem::Stacked(item_id, _) => *item_id, + } + } + + pub fn amount(&self) -> usize { + match self { + TradeItem::Individual(_) => 1, + TradeItem::Stacked(_, amount) => *amount, + } + } +} + diff --git a/location/Cargo.toml b/location/Cargo.toml new file mode 100644 index 0000000..f6768c0 --- /dev/null +++ b/location/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "location" +version = "0.1.0" +edition = "2021" + +[dependencies] +networking = { workspace = true } + +async-std = { workspace = true } +derive_more = { workspace = true } +futures= { workspace = true } +thiserror = { workspace = true } \ No newline at end of file diff --git a/src/ship/location.rs b/location/src/lib.rs similarity index 99% rename from src/ship/location.rs rename to location/src/lib.rs index 5dc805e..3dd0b2b 100644 --- a/src/ship/location.rs +++ b/location/src/lib.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::time::SystemTime; use thiserror::Error; -use crate::common::serverstate::ClientId; +use networking::serverstate::ClientId; use async_std::sync::{Arc, RwLock}; use futures::{stream, StreamExt}; diff --git a/maps/Cargo.toml b/maps/Cargo.toml new file mode 100644 index 0000000..db84b6a --- /dev/null +++ b/maps/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "maps" +version = "0.1.0" +edition = "2021" + +[dependencies] +byteorder = { workspace = true } +serde = { workspace = true } +thiserror = { workspace = true } +rand = { workspace = true } +rand_chacha = { workspace = true } +toml = { workspace = true } +enum-utils = { workspace = true } +derive_more = { workspace = true } diff --git a/src/ship/map/area.rs b/maps/src/area.rs similarity index 99% rename from src/ship/map/area.rs rename to maps/src/area.rs index 02361c3..bb64737 100644 --- a/src/ship/map/area.rs +++ b/maps/src/area.rs @@ -2,7 +2,7 @@ use serde::{Serialize, Deserialize}; use std::collections::HashMap; use thiserror::Error; -use crate::ship::room::Episode; +use crate::room::Episode; use std::fmt; #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] diff --git a/src/ship/map/enemy.rs b/maps/src/enemy.rs similarity index 94% rename from src/ship/map/enemy.rs rename to maps/src/enemy.rs index 4989041..54ac36d 100644 --- a/src/ship/map/enemy.rs +++ b/maps/src/enemy.rs @@ -1,19 +1,18 @@ // TOOD: `pub(super) for most of these?` use std::io::{Read}; use std::collections::HashMap; +use std::fs::File; +use std::path::PathBuf; use byteorder::{LittleEndian, ReadBytesExt}; use thiserror::Error; - -use crate::ship::ship::ShipEvent; -use crate::ship::monster::MonsterType; -use crate::ship::room::Episode; - -use crate::ship::map::*; - use rand::{Rng, SeedableRng}; use serde::{Serialize, Deserialize}; -use crate::ship::drops::{load_rare_monster_file}; + +use crate::Holiday; +use crate::area::{MapArea, MapAreaError}; +use crate::room::Episode; +use crate::monster::MonsterType; #[derive(Debug, Copy, Clone)] pub struct RawMapEnemy { @@ -69,6 +68,17 @@ impl RawMapEnemy { } +pub fn load_rare_monster_file(episode: Episode) -> T { + // TODO: where does the rare monster toml file actually live + let mut path = PathBuf::from("data/battle_param/"); + path.push(episode.to_string().to_lowercase() + "_rare_monster.toml"); + + let mut f = File::open(path).unwrap(); + let mut s = String::new(); + f.read_to_string(&mut s).unwrap(); + toml::from_str::(s.as_str()).unwrap() +} + #[derive(Error, Debug)] #[error("")] pub enum MapEnemyError { @@ -76,6 +86,7 @@ pub enum MapEnemyError { MapAreaError(#[from] MapAreaError), } + // making this `pub type` doesn't allow `impl`s to be defined? #[derive(Clone, Debug, Serialize, Deserialize)] pub struct RareMonsterAppearTable { @@ -103,7 +114,7 @@ impl RareMonsterAppearTable { rand_chacha::ChaChaRng::from_entropy().gen::() < *self.appear_rate.get(monster).unwrap_or(&0.0f32) } - pub fn apply(&self, enemy: MapEnemy, event: ShipEvent) -> MapEnemy { + pub fn apply(&self, enemy: MapEnemy, event: Holiday) -> MapEnemy { if enemy.can_be_rare() && self.roll_is_rare(&enemy.monster) { enemy.into_rare(event) } @@ -345,12 +356,12 @@ impl MapEnemy { guaranteed rare monsters don't count towards the limit */ #[must_use] - pub fn into_rare(self, event: ShipEvent) -> MapEnemy { + pub fn into_rare(self, event: Holiday) -> 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, Holiday::Easter) => {MapEnemy {monster: MonsterType::EasterRappy, shiny:true, ..self}}, + (MonsterType::RagRappy, Episode::Two, Holiday::Halloween) => {MapEnemy {monster: MonsterType::HalloRappy, shiny:true, ..self}}, + (MonsterType::RagRappy, Episode::Two, Holiday::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}}, diff --git a/maps/src/lib.rs b/maps/src/lib.rs new file mode 100644 index 0000000..2da528c --- /dev/null +++ b/maps/src/lib.rs @@ -0,0 +1,59 @@ +pub mod area; +pub mod enemy; +pub mod object; +pub mod variant; +pub mod maps; +pub mod monster; +pub mod room; + +#[derive(Clone, Copy)] +pub enum Holiday { + None, + Christmas, + Valentines, + Easter, + Halloween, + Sonic, + NewYear, + Summer, + White, + Wedding, + Fall, + Spring, + Summer2, + Spring2, +} + + +impl From for u32 { + fn from(other: Holiday) -> u32 { + u16::from(other) as u32 + } +} + +impl From for u16 { + fn from(other: Holiday) -> u16 { + u8::from(other) as u16 + } +} + +impl From for u8 { + fn from(other: Holiday) -> u8 { + match other { + Holiday::None => 0, + Holiday::Christmas => 1, + Holiday::Valentines => 3, + Holiday::Easter => 4, + Holiday::Halloween => 5, + Holiday::Sonic => 6, + Holiday::NewYear => 7, + Holiday::Summer => 8, + Holiday::White => 9, + Holiday::Wedding => 10, + Holiday::Fall => 11, + Holiday::Spring => 12, + Holiday::Summer2 => 13, + Holiday::Spring2 => 14, + } + } +} diff --git a/src/ship/map/maps.rs b/maps/src/maps.rs similarity index 96% rename from src/ship/map/maps.rs rename to maps/src/maps.rs index 05c7e29..2ed7961 100644 --- a/src/ship/map/maps.rs +++ b/maps/src/maps.rs @@ -6,14 +6,15 @@ use std::fs::File; use thiserror::Error; -use crate::ship::ship::ShipEvent; -use crate::ship::monster::MonsterType; -use crate::ship::room::{Episode, RoomMode, PlayerMode}; - -// TODO: don't use * -use crate::ship::map::*; - +//use crate::ship::ship::ShipEvent; +use crate::area::MapArea; +use crate::Holiday; +use crate::enemy::{MapEnemy, RawMapEnemy, RareMonsterAppearTable}; +use crate::monster::MonsterType; +use crate::variant::{MapVariant, MapVariantMode}; +use crate::object::{MapObject, RawMapObject}; +use crate::room::{Episode, RoomMode, PlayerMode}; pub fn objects_from_stream(cursor: &mut impl Read, episode: &Episode, map_area: &MapArea) -> Vec> { let mut object_data = Vec::new(); @@ -325,7 +326,7 @@ impl Maps { enemies: Vec>, objects: Vec>, rare_monster_table: &RareMonsterAppearTable, - event: ShipEvent) + event: Holiday) { self.enemy_data = enemies .into_iter() @@ -358,7 +359,7 @@ impl Maps { } } -pub fn generate_free_roam_maps(room_mode: RoomMode, event: ShipEvent) -> Maps { +pub fn generate_free_roam_maps(room_mode: RoomMode, event: Holiday) -> Maps { let rare_monster_table = RareMonsterAppearTable::new(room_mode.episode()); let map_variants = default_map_variants(room_mode.episode(), room_mode.player_mode()); Maps { @@ -375,3 +376,12 @@ pub fn generate_free_roam_maps(room_mode: RoomMode, event: ShipEvent) -> Maps { map_variants, } } + + +pub fn null_free_roam_maps(_room_mode: RoomMode, _event: Holiday) -> Maps { + Maps { + enemy_data: Default::default(), + object_data: Default::default(), + map_variants: Default::default(), + } +} diff --git a/src/ship/monster.rs b/maps/src/monster.rs similarity index 98% rename from src/ship/monster.rs rename to maps/src/monster.rs index 1bdb5b7..a72288d 100644 --- a/src/ship/monster.rs +++ b/maps/src/monster.rs @@ -4,7 +4,7 @@ use std::fs::File; use std::io::Read; use std::path::PathBuf; use serde::{Serialize, Deserialize}; -use crate::ship::room::{Difficulty, Episode, RoomMode}; +use crate::room::{Difficulty, Episode, RoomMode}; #[derive(Debug)] @@ -149,7 +149,7 @@ pub enum MonsterType { } -#[derive(serde::Deserialize, Debug)] +#[derive(Deserialize, Debug)] pub struct MonsterStats { pub atp: u16, pub mst: u16, diff --git a/src/ship/map/object.rs b/maps/src/object.rs similarity index 98% rename from src/ship/map/object.rs rename to maps/src/object.rs index 81bce6a..0f73442 100644 --- a/src/ship/map/object.rs +++ b/maps/src/object.rs @@ -1,13 +1,11 @@ // TOOD: `pub(super) for most of these?` -use std::io::{Read}; +use std::io::Read; use byteorder::{LittleEndian, ReadBytesExt}; -use crate::ship::room::Episode; - -// TODO: don't use * -use crate::ship::map::*; +use crate::room::Episode; +use crate::area::MapArea; #[derive(Debug, Copy, Clone)] diff --git a/maps/src/room.rs b/maps/src/room.rs new file mode 100644 index 0000000..8370e7c --- /dev/null +++ b/maps/src/room.rs @@ -0,0 +1,150 @@ +#[derive(Debug, Copy, Clone, derive_more::Display)] +pub enum Episode { + #[display(fmt="ep1")] + One, + #[display(fmt="ep2")] + Two, + #[display(fmt="ep4")] + Four, +} + +impl TryFrom for Episode { + type Error = (); + + fn try_from(value: u8) -> Result { + match value { + 1 => Ok(Episode::One), + 2 => Ok(Episode::Two), + 3 => Ok(Episode::Four), + _ => Err(()) + } + } +} + +impl From for u8 { + fn from(other: Episode) -> u8 { + match other { + Episode::One => 1, + Episode::Two => 2, + Episode::Four => 3, + } + } +} + +impl Episode { + pub fn from_quest(value: u8) -> Option { + match value { + 0 => Some(Episode::One), + 1 => Some(Episode::Two), + 2 => Some(Episode::Four), + _ => None, + } + } +} + +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, derive_more::Display)] +pub enum Difficulty { + Normal, + Hard, + VeryHard, + Ultimate, +} + +impl TryFrom for Difficulty { + type Error = (); + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(Difficulty::Normal), + 1 => Ok(Difficulty::Hard), + 2 => Ok(Difficulty::VeryHard), + 3 => Ok(Difficulty::Ultimate), + _ => Err(()) + } + } +} + +impl From for u8 { + fn from(other: Difficulty) -> u8 { + match other { + Difficulty::Normal => 0, + Difficulty::Hard => 1, + Difficulty::VeryHard => 2, + Difficulty::Ultimate => 3, + } + } +} + +#[derive(Debug, Copy, Clone)] +pub enum PlayerMode { + Single, + Multi, +} + +impl PlayerMode { + pub fn value(&self) -> u8 { + match self { + PlayerMode::Single => 1, + PlayerMode::Multi => 0, + } + } +} + +#[derive(Debug, Copy, Clone, derive_more::Display)] +pub enum RoomMode { + #[display(fmt="single")] + Single { + episode: Episode, + difficulty: Difficulty, + }, + #[display(fmt="multi")] + Multi { + episode: Episode, + difficulty: Difficulty, + }, + #[display(fmt="challenge")] + Challenge { + episode: Episode, + }, + #[display(fmt="battle")] + Battle { + episode: Episode, + difficulty: Difficulty, + } +} + + +impl RoomMode { + pub fn difficulty(&self) -> Difficulty { + match self { + RoomMode::Single {difficulty, ..} => *difficulty, + RoomMode::Multi {difficulty, ..} => *difficulty, + RoomMode::Battle {difficulty, ..} => *difficulty, + RoomMode::Challenge {..} => Difficulty::Normal, + } + } + + pub fn episode(&self) -> Episode { + match self { + RoomMode::Single {episode, ..} => *episode, + RoomMode::Multi {episode, ..} => *episode, + RoomMode::Battle {episode, ..} => *episode, + RoomMode::Challenge {episode, ..} => *episode, + } + } + + pub fn battle(&self) -> bool { + matches!(self, RoomMode::Battle {..}) + } + + pub fn challenge(&self) -> bool { + matches!(self, RoomMode::Challenge {..}) + } + + pub fn player_mode(&self) -> PlayerMode { + match self { + RoomMode::Single {..} => PlayerMode::Single, + _ => PlayerMode::Multi, + } + } +} diff --git a/src/ship/map/variant.rs b/maps/src/variant.rs similarity index 99% rename from src/ship/map/variant.rs rename to maps/src/variant.rs index 0fa7c65..78fa4bf 100644 --- a/src/ship/map/variant.rs +++ b/maps/src/variant.rs @@ -3,7 +3,8 @@ use rand::Rng; // TODO: don't use * -use crate::ship::map::*; +//use crate::map::*; +use crate::area::MapArea; #[derive(Debug, PartialEq, Eq)] pub enum MapVariantMode { diff --git a/networking/Cargo.toml b/networking/Cargo.toml new file mode 100644 index 0000000..1ecadc9 --- /dev/null +++ b/networking/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "networking" +version = "0.1.0" +edition = "2021" + + +[dependencies] +entity = { workspace = true } + +libpso = { workspace = true } + +async-std = { workspace = true } +async-trait = { workspace = true } +futures = { workspace = true } +log = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +derive_more = { workspace = true } \ No newline at end of file diff --git a/src/common/cipherkeys.rs b/networking/src/cipherkeys.rs similarity index 100% rename from src/common/cipherkeys.rs rename to networking/src/cipherkeys.rs diff --git a/src/common/interserver.rs b/networking/src/interserver.rs similarity index 94% rename from src/common/interserver.rs rename to networking/src/interserver.rs index 1f6e0ff..be49a9f 100644 --- a/src/common/interserver.rs +++ b/networking/src/interserver.rs @@ -2,8 +2,8 @@ use std::net::Ipv4Addr; use async_std::channel; use serde::{Serialize, Deserialize}; use serde::de::DeserializeOwned; -use crate::entity::account::UserAccountId; -use crate::entity::character::CharacterEntityId; +use entity::account::UserAccountId; +use entity::character::CharacterEntityId; #[derive(Debug, Copy, Clone, Serialize, Deserialize, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct ServerId(pub usize); diff --git a/src/common/mod.rs b/networking/src/lib.rs similarity index 96% rename from src/common/mod.rs rename to networking/src/lib.rs index d02076e..c8adde9 100644 --- a/src/common/mod.rs +++ b/networking/src/lib.rs @@ -1,7 +1,6 @@ pub mod cipherkeys; pub mod serverstate; pub mod mainloop; -pub mod leveltable; pub mod interserver; // https://www.reddit.com/r/rust/comments/33xhhu/how_to_create_an_array_of_structs_that_havent/ diff --git a/src/common/mainloop/client.rs b/networking/src/mainloop/client.rs similarity index 97% rename from src/common/mainloop/client.rs rename to networking/src/mainloop/client.rs index d7af211..3bcb971 100644 --- a/src/common/mainloop/client.rs +++ b/networking/src/mainloop/client.rs @@ -9,8 +9,8 @@ use log::{trace, info, warn, error}; use libpso::crypto::{PSOCipher, NullCipher, CipherError}; use libpso::PacketParseError; -use crate::common::serverstate::ClientId; -use crate::common::serverstate::{RecvServerPacket, SendServerPacket, ServerState, OnConnect}; +use crate::serverstate::ClientId; +use crate::serverstate::{RecvServerPacket, SendServerPacket, ServerState, OnConnect}; #[derive(Debug)] @@ -255,7 +255,7 @@ where let (mut socket, addr) = listener.accept().await.unwrap(); id += 1; - let client_id = crate::common::serverstate::ClientId(id); + let client_id = crate::serverstate::ClientId(id); info!("new client {:?} {:?} {:?}", client_id, socket, addr); let (client_tx, client_rx) = async_std::channel::unbounded(); diff --git a/src/common/mainloop/interserver.rs b/networking/src/mainloop/interserver.rs similarity index 93% rename from src/common/mainloop/interserver.rs rename to networking/src/mainloop/interserver.rs index dbe940d..5e5194b 100644 --- a/src/common/mainloop/interserver.rs +++ b/networking/src/mainloop/interserver.rs @@ -8,13 +8,11 @@ use std::collections::HashMap; use serde::Serialize; use serde::de::DeserializeOwned; -use crate::common::interserver::{ServerId, InterserverActor}; +use crate::interserver::{ServerId, InterserverActor}; use libpso::crypto::{PSOCipher, NullCipher, CipherError}; -use crate::common::serverstate::{ServerState, SendServerPacket, RecvServerPacket}; -use crate::login::character::CharacterServerState; -//use crate::ship::ship::ShipServerState; -use crate::entity::gateway::entitygateway::EntityGateway; +use crate::serverstate::{ServerState, SendServerPacket, RecvServerPacket}; +use entity::gateway::entitygateway::EntityGateway; use async_std::channel; use std::fmt::Debug; @@ -149,7 +147,7 @@ where info!("[interserver listen] new server: {:?} {:?}", socket, addr); id += 1; - let server_id = crate::common::interserver::ServerId(id); + let server_id = crate::interserver::ServerId(id); let (client_tx, client_rx) = async_std::channel::unbounded(); state.set_sender(server_id, client_tx.clone()).await; @@ -196,7 +194,7 @@ where } }; id += 1; - let server_id = crate::common::interserver::ServerId(id); + let server_id = crate::interserver::ServerId(id); info!("[interserver connect] found loginserv: {:?} {:?}", server_id, socket); let (client_tx, client_rx) = async_std::channel::unbounded(); @@ -220,12 +218,8 @@ where let mut buf = [0u8; 1]; loop { let peek = socket.peek(&mut buf).await; - match peek { - Ok(len) if len == 0 => { - break - }, - _ => { - } + if let Ok(0) = peek { + break } } } diff --git a/src/common/mainloop/mod.rs b/networking/src/mainloop/mod.rs similarity index 100% rename from src/common/mainloop/mod.rs rename to networking/src/mainloop/mod.rs diff --git a/src/common/serverstate.rs b/networking/src/serverstate.rs similarity index 100% rename from src/common/serverstate.rs rename to networking/src/serverstate.rs diff --git a/pktbuilder/Cargo.toml b/pktbuilder/Cargo.toml new file mode 100644 index 0000000..9c070f9 --- /dev/null +++ b/pktbuilder/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "pktbuilder" +version = "0.1.0" +edition = "2021" + +[dependencies] +quests = { workspace = true } +stats = { workspace = true } +location = { workspace = true } +client = { workspace = true } +items = { workspace = true } +networking = { workspace = true } +maps = { workspace = true } +room = { workspace = true } +shops = { workspace = true } +entity = { workspace = true } + +libpso = { workspace = true } + +anyhow = { workspace = true } +futures = { workspace = true } +thiserror = { workspace = true } \ No newline at end of file diff --git a/src/ship/character.rs b/pktbuilder/src/character.rs similarity index 96% rename from src/ship/character.rs rename to pktbuilder/src/character.rs index 348f8ac..5d57896 100644 --- a/src/ship/character.rs +++ b/pktbuilder/src/character.rs @@ -1,10 +1,9 @@ use libpso::character::character; -use crate::common::leveltable::CharacterStats; -use crate::entity::character::CharacterEntity; -//use crate::ship::items::{CharacterInventory, CharacterBank}; -use crate::ship::items::bank::BankState; -use crate::ship::items::inventory::InventoryState; -use crate::entity::item::Meseta; +use stats::leveltable::CharacterStats; +use entity::character::CharacterEntity; +use items::bank::BankState; +use items::inventory::InventoryState; +use entity::item::Meseta; #[derive(Default)] diff --git a/src/ship/packet/builder/mod.rs b/pktbuilder/src/lib.rs similarity index 85% rename from src/ship/packet/builder/mod.rs rename to pktbuilder/src/lib.rs index f74015e..bdcd755 100644 --- a/src/ship/packet/builder/mod.rs +++ b/pktbuilder/src/lib.rs @@ -3,14 +3,15 @@ pub mod message; pub mod room; pub mod quest; pub mod ship; +pub mod character; use libpso::character::character::Inventory; use libpso::packet::ship::{PlayerHeader, PlayerInfo}; -use crate::common::leveltable::LEVEL_TABLE; -use crate::ship::character::CharacterBytesBuilder; -use crate::ship::ship::ClientState; -use crate::ship::location::AreaClient; -use crate::ship::items::inventory::InventoryState; +use stats::leveltable::LEVEL_TABLE; +use crate::character::CharacterBytesBuilder; +use client::ClientState; +use location::AreaClient; +use items::inventory::InventoryState; pub fn player_header(tag: u32, client: &ClientState, area_client: &AreaClient) -> PlayerHeader { PlayerHeader { diff --git a/src/ship/packet/builder/lobby.rs b/pktbuilder/src/lobby.rs similarity index 91% rename from src/ship/packet/builder/lobby.rs rename to pktbuilder/src/lobby.rs index af033ca..34587c5 100644 --- a/src/ship/packet/builder/lobby.rs +++ b/pktbuilder/src/lobby.rs @@ -1,9 +1,10 @@ use libpso::packet::ship::*; -use crate::common::serverstate::ClientId; -use crate::ship::ship::{Clients, ShipEvent}; -use crate::ship::location::{ClientLocation, LobbyId, ClientLocationError}; -use crate::ship::packet::builder::{player_info}; -use crate::ship::items::state::ItemState; +use networking::serverstate::ClientId; +use maps::Holiday; +use client::Clients; +use location::{ClientLocation, LobbyId, ClientLocationError}; +use crate::player_info; +use items::state::ItemState; use futures::future::join_all; @@ -12,7 +13,7 @@ pub async fn join_lobby(id: ClientId, client_location: &ClientLocation, clients: &Clients, item_state: &ItemState, - event: ShipEvent) + event: Holiday) -> Result { let lobby_clients = client_location.get_clients_in_lobby(lobby).await.map_err(|err| -> ClientLocationError { err.into() })?; @@ -52,7 +53,7 @@ pub async fn add_to_lobby(id: ClientId, client_location: &ClientLocation, clients: &Clients, item_state: &ItemState, - event: ShipEvent) + event: Holiday) -> 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() })?; diff --git a/src/ship/packet/builder/message.rs b/pktbuilder/src/message.rs similarity index 72% rename from src/ship/packet/builder/message.rs rename to pktbuilder/src/message.rs index ac60484..cf843df 100644 --- a/src/ship/packet/builder/message.rs +++ b/pktbuilder/src/message.rs @@ -1,21 +1,21 @@ use libpso::packet::messages::*; use libpso::packet::ship::*; -use crate::entity::item; -use crate::common::leveltable::CharacterStats; -use crate::ship::ship::{ShipError}; -use crate::ship::items::ClientItemId; -use crate::ship::items::inventory::InventoryItem; -use crate::ship::items::state::IndividualItemDetail; -use crate::ship::items::bank::BankState; -use crate::ship::items::floor::FloorItem; -use crate::ship::location::AreaClient; +use entity::item; +use stats::leveltable::CharacterStats; +//use crate::ship::ship::{ShipError}; +use items::ClientItemId; +use items::inventory::InventoryItem; +use items::state::IndividualItemDetail; +use items::bank::BankState; +use items::floor::FloorItem; +use location::AreaClient; use std::convert::TryInto; -use crate::ship::shops::ShopItem; +use shops::ShopItem; -pub fn item_drop(client: u8, target: u8, item_drop: &FloorItem) -> Result { +pub fn item_drop(client: u8, target: u8, item_drop: &FloorItem) -> ItemDrop { let item_bytes = item_drop.as_client_bytes(); - Ok(ItemDrop { + ItemDrop { client, target, map_area: item_drop.map_area.area_value(), @@ -24,37 +24,37 @@ pub fn item_drop(client: u8, target: u8, item_drop: &FloorItem) -> Result Result { +pub fn create_individual_item(area_client: AreaClient, item_id: ClientItemId, item: &IndividualItemDetail) -> CreateItem { let bytes = item.as_client_bytes(); - Ok(CreateItem { + CreateItem { client: area_client.local_client.id(), target: 0, - item_data: bytes[0..12].try_into()?, + item_data: bytes[0..12].try_into().unwrap(), item_id: item_id.0, - item_data2: bytes[12..16].try_into()?, + item_data2: bytes[12..16].try_into().unwrap(), unknown: 0, - }) + } } // TODO: this doesn't need to be a Result, just unwrap try_intos they are guaranteed to succeed -pub fn create_stacked_item(area_client: AreaClient, item_id: ClientItemId, tool: &item::tool::Tool, amount: usize) -> Result { +pub fn create_stacked_item(area_client: AreaClient, item_id: ClientItemId, tool: &item::tool::Tool, amount: usize) -> CreateItem { let bytes = tool.as_stacked_bytes(amount); - Ok(CreateItem { + CreateItem { client: area_client.local_client.id(), target: 0, - item_data: bytes[0..12].try_into()?, + item_data: bytes[0..12].try_into().unwrap(), item_id: item_id.0, - item_data2: bytes[12..16].try_into()?, + item_data2: bytes[12..16].try_into().unwrap(), unknown: 0, - }) + } } pub fn create_meseta(area_client: AreaClient, amount: usize) -> CreateItem { @@ -69,32 +69,32 @@ pub fn create_meseta(area_client: AreaClient, amount: usize) -> CreateItem { } } -pub fn create_withdrawn_inventory_item(area_client: AreaClient, item: &InventoryItem) -> Result { +pub fn create_withdrawn_inventory_item(area_client: AreaClient, item: &InventoryItem) -> CreateItem { let bytes = item.item.as_client_bytes(); - Ok(CreateItem { + CreateItem { client: area_client.local_client.id(), target: 0, - item_data: bytes[0..12].try_into()?, + item_data: bytes[0..12].try_into().unwrap(), item_id: item.item_id.0, - item_data2: bytes[12..16].try_into()?, + item_data2: bytes[12..16].try_into().unwrap(), unknown: 0, - }) + } } -pub fn create_withdrawn_inventory_item2(area_client: AreaClient, item: &InventoryItem) -> Result { +pub fn create_withdrawn_inventory_item2(area_client: AreaClient, item: &InventoryItem) -> CreateItem { let bytes = item.item.as_client_bytes(); - Ok(CreateItem { + CreateItem { client: area_client.local_client.id(), target: 0, - item_data: bytes[0..12].try_into()?, + item_data: bytes[0..12].try_into().unwrap(), item_id: item.item_id.0, - item_data2: bytes[12..16].try_into()?, + item_data2: bytes[12..16].try_into().unwrap(), unknown: 0, - }) + } } -pub fn remove_item_from_floor(area_client: AreaClient, item: &FloorItem) -> Result { - Ok(RemoveItemFromFloor { +pub fn remove_item_from_floor(area_client: AreaClient, item: &FloorItem) -> RemoveItemFromFloor { + RemoveItemFromFloor { client: area_client.local_client.id(), target: 0, client_id: area_client.local_client.id(), @@ -102,12 +102,12 @@ pub fn remove_item_from_floor(area_client: AreaClient, item: &FloorItem) -> Resu map_area: item.map_area.area_value(), unknown2: 0, item_id: item.item_id.0, - }) + } } -pub fn drop_split_stack(area_client: AreaClient, item: &FloorItem) -> Result { +pub fn drop_split_stack(area_client: AreaClient, item: &FloorItem) -> DropSplitStack { let item_bytes = item.as_client_bytes(); - Ok(DropSplitStack { + DropSplitStack { client: area_client.local_client.id(), target: 0, variety: 0, @@ -115,16 +115,16 @@ pub fn drop_split_stack(area_client: AreaClient, item: &FloorItem) -> Result Result { +pub fn drop_split_meseta_stack(area_client: AreaClient, item: &FloorItem) -> DropSplitStack { let item_bytes = item.as_client_bytes(); - Ok(DropSplitStack { + DropSplitStack { client: area_client.local_client.id(), target: 0, variety: 0, @@ -132,11 +132,11 @@ pub fn drop_split_meseta_stack(area_client: AreaClient, item: &FloorItem) -> Res map_area: item.map_area.area_value(), x: item.x, z: item.z, - item_bytes: item_bytes[0..12].try_into()?, + item_bytes: item_bytes[0..12].try_into().unwrap(), item_id: item.item_id.0, - item_bytes2: item_bytes[12..16].try_into()?, + item_bytes2: item_bytes[12..16].try_into().unwrap(), unknown2: 0, - }) + } } pub fn character_gained_exp(area_client: AreaClient, exp: u32) -> GiveCharacterExp { @@ -215,13 +215,13 @@ pub fn shop_list(shop_type: u8, items: &[I]) -> ShopList { } } -pub fn tek_preview(id: ClientItemId, weapon: &item::weapon::Weapon) -> Result { +pub fn tek_preview(id: ClientItemId, weapon: &item::weapon::Weapon) -> TekPreview { let bytes = weapon.as_bytes(); - Ok(TekPreview { + TekPreview { client: 0x79, target: 0, - item_bytes: bytes[0..12].try_into()?, + item_bytes: bytes[0..12].try_into().unwrap(), item_id: id.0, - item_bytes2: bytes[12..16].try_into()?, - }) + item_bytes2: bytes[12..16].try_into().unwrap(), + } } diff --git a/src/ship/packet/builder/quest.rs b/pktbuilder/src/quest.rs similarity index 94% rename from src/ship/packet/builder/quest.rs rename to pktbuilder/src/quest.rs index f162269..d3f2f4f 100644 --- a/src/ship/packet/builder/quest.rs +++ b/pktbuilder/src/quest.rs @@ -1,8 +1,9 @@ -use crate::ship::quests::{Quest, QuestList}; -use crate::ship::ship::{QUEST_CATEGORY_MENU_ID, QUEST_SELECT_MENU_ID}; +use quests::{Quest, QuestList}; use libpso::packet::ship::*; use libpso::{utf8_to_array, utf8_to_utf16_array}; +pub const QUEST_CATEGORY_MENU_ID: u32 = 0xA2; +pub const QUEST_SELECT_MENU_ID: u32 = 0xA3; pub fn quest_category_list(quests: &QuestList) -> QuestCategoryList { let categories = quests.iter() diff --git a/src/ship/packet/builder/room.rs b/pktbuilder/src/room.rs similarity index 84% rename from src/ship/packet/builder/room.rs rename to pktbuilder/src/room.rs index 2b5c8e5..a7bbe75 100644 --- a/src/ship/packet/builder/room.rs +++ b/pktbuilder/src/room.rs @@ -1,11 +1,12 @@ -use libpso::packet::ship::*; -use crate::common::serverstate::ClientId; -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; -use crate::ship::packet::builder::{player_header, player_info}; use std::convert::TryInto; +use libpso::packet::ship::*; +use networking::serverstate::ClientId; +use maps::Holiday; +use client::{ClientState, Clients}; +use location::{ClientLocation, RoomId, AreaClient, ClientLocationError}; +use room::RoomState; +use items::state::ItemState; +use crate::{player_header, player_info}; use futures::stream::StreamExt; @@ -14,13 +15,14 @@ pub async fn join_room(id: ClientId, client_location: &ClientLocation, room_id: RoomId, room: &RoomState, - event: ShipEvent) + event: Holiday) -> Result { let all_clients = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?; + #[allow(clippy::manual_try_fold)] // I don't think its even possible to make this work here let players = futures::stream::iter(all_clients.iter()) .enumerate() .fold::, _, _>(Ok([PlayerHeader::default(); 4]), |acc, (i, c)| async move { - let header_area_client = client_location.get_local_client(id).await.map_err(|err| ShipError::ClientLocationError(err.into()))?; + let header_area_client = client_location.get_local_client(id).await.map_err(|err| -> ClientLocationError {err.into() })?; clients.with(c.client, |client| Box::pin(async move { acc.map(|mut a| { a[i] = player_header(0x10000, client, &header_area_client); @@ -57,7 +59,7 @@ pub async fn add_to_room(_id: ClientId, area_client: &AreaClient, leader: &AreaClient, item_state: &ItemState, - event: ShipEvent) + event: Holiday) -> Result { let inventory = item_state.get_character_inventory(&client.character).await?; Ok(AddToRoom { diff --git a/src/ship/packet/builder/ship.rs b/pktbuilder/src/ship.rs similarity index 85% rename from src/ship/packet/builder/ship.rs rename to pktbuilder/src/ship.rs index 1411f2a..41c6ea0 100644 --- a/src/ship/packet/builder/ship.rs +++ b/pktbuilder/src/ship.rs @@ -1,8 +1,9 @@ use libpso::packet::login::{ShipList, ShipListEntry}; use libpso::utf8_to_utf16_array; -use crate::common::interserver::Ship; -use crate::login::character::SHIP_MENU_ID; +use networking::interserver::Ship; + +pub const SHIP_MENU_ID: u32 = 1; pub fn ship_list(ships: &[Ship]) -> ShipList { let ships = ships.iter() diff --git a/pktbuilder/src/team.rs b/pktbuilder/src/team.rs new file mode 100644 index 0000000..9ebdfb9 --- /dev/null +++ b/pktbuilder/src/team.rs @@ -0,0 +1,107 @@ +use futures::stream::{FuturesOrdered, StreamExt}; +use libpso::packet::ship::*; +use crate::common::serverstate::ClientId; +use crate::entity::gateway::EntityGateway; +use crate::ship::client::{Clients, ClientState}; +use crate::ship::teams::Teams; +use crate::ship::location::ClientLocation; +use crate::ship::ship::ShipError; +use crate::entity::team::TeamEntity; + + +pub fn client_team_state_changed(client_id: ClientId, client: &ClientState, team: &TeamEntity) -> ClientTeamStateChanged { + ClientTeamStateChanged { + unknown: 0, + guildcard: client.user.guildcard(), + team_id: team.id.0, + unknown2: [0;2], + privilege: 0x40, // TODO: improve + team_name: libpso::utf8_to_utf16_array!(team.name, 14), + unknown3: 0x00986C84, // TODO: what if we omit this? + } +} + +fn player_team_info(client_id: ClientId, client: &ClientState, team: &TeamEntity) -> PlayerTeamInfo { + PlayerTeamInfo { + guildcard: client.user.guildcard(), + team_id: team.id.0, + info: 0, + info2: 0, + privilege: 0x40, // TODO: improve + team_name: libpso::utf8_to_utf16_array!(team.name, 14), + unknown: 0x00986C84, // TODO: what if we omit this? + guildcard_again: client.user.guildcard(), + client_id: client_id.0 as u32, + character_name: libpso::utf8_to_utf16_array!(client.character.name, 12), + unknown2: 0, + unknown3: 0, + team_flag: team.team_flag, + } +} + +pub fn team_info_individual(client_id: ClientId, client: &ClientState, team: &TeamEntity) -> TeamInfo { + TeamInfo { + clients: vec![player_team_info(client_id, client, team)] + } +} + + +pub async fn player_team_info_list(id: ClientId, + client_location: &ClientLocation, + clients: &Clients, + teams: &Teams, +) -> Result, ShipError> +where + EG: EntityGateway + Clone + 'static, +{ + Ok(futures::stream::iter(client_location.get_all_clients_by_client(id).await?.into_iter()) + .filter_map(|area_client| { + let clients = clients.clone(); + async move { + clients.with(area_client.client, |client| { + let mut teams = teams.clone(); + Box::pin(async move { + let team = teams.get_team(area_client.client).await.ok()??; + Some(player_team_info(area_client.client, client, &team)) + })}).await.ok()? + }}) + .collect::>() + .await) +} + +pub async fn team_info(id: ClientId, + client_location: &ClientLocation, + clients: &Clients, + teams: &Teams, +) -> Result +where + EG: EntityGateway + Clone + 'static, +{ + Ok(TeamInfo { + clients: player_team_info_list(id, client_location, clients, teams).await?, + }) +} + +pub async fn lobby_team_list(id: ClientId, + client_location: &ClientLocation, + clients: &Clients, + teams: &Teams, +) -> Result +where + EG: EntityGateway + Clone + 'static, +{ + Ok(TeamLobbyList { + clients: player_team_info_list(id, client_location, clients, teams).await?, + }) +} + +pub fn team_invitation_info(client_id: ClientId, client: &ClientState, team: &TeamEntity) -> TeamInvitationInfo { + TeamInvitationInfo { + guildcard: client.user.guildcard(), + team_id: team.id.0, + unknown: [0; 2], + team_name: libpso::utf8_to_utf16_array!(team.name, 14), + unknown2: 0x00986C84, // TODO: what if we omit this? + team_flag: team.team_flag, + } +} diff --git a/src/ship/packet/builder/trade.rs b/pktbuilder/src/trade.rs similarity index 100% rename from src/ship/packet/builder/trade.rs rename to pktbuilder/src/trade.rs diff --git a/quests/Cargo.toml b/quests/Cargo.toml new file mode 100644 index 0000000..6cab926 --- /dev/null +++ b/quests/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "quests" +version = "0.1.0" +edition = "2021" + +[dependencies] +maps = { workspace = true } + +libpso = { workspace = true } + +async-std = { workspace = true } +ages-prs = { workspace = true } +log = { workspace = true } +byteorder = { workspace = true } +thiserror = { workspace = true } +anyhow = { workspace = true } +toml = { workspace = true } +serde = { workspace = true } diff --git a/src/ship/quests.rs b/quests/src/lib.rs similarity index 97% rename from src/ship/quests.rs rename to quests/src/lib.rs index 4f5eb26..91e5d4d 100644 --- a/src/ship/quests.rs +++ b/quests/src/lib.rs @@ -10,9 +10,12 @@ use serde::{Serialize, Deserialize}; use ages_prs::{LegacyPrsDecoder, LegacyPrsEncoder}; use byteorder::{LittleEndian, ReadBytesExt}; use libpso::util::array_to_utf16; -use crate::ship::map::{MapArea, MapAreaError, MapObject, MapEnemy, enemy_data_from_stream, objects_from_stream}; -use crate::ship::room::{Episode, RoomMode}; -use crate::ship::map::area::{MapAreaLookup, MapAreaLookupBuilder}; +use maps::area::{MapArea, MapAreaError}; +use maps::object::MapObject; +use maps::enemy::MapEnemy; +use maps::maps::{objects_from_stream, enemy_data_from_stream}; +use maps::room::{Episode, RoomMode}; +use maps::area::{MapAreaLookup, MapAreaLookupBuilder}; #[derive(Debug, Serialize, Deserialize, Hash, PartialEq, Eq, PartialOrd, Ord)] @@ -101,7 +104,7 @@ fn quest_episode(bin: &[u8]) -> Option { for bytes in bin.windows(3) { // set_episode if bytes[0] == 0xF8 && bytes[1] == 0xBC { - return Episode::from_quest(bytes[2]).ok() + return Episode::from_quest(bytes[2]) } } None diff --git a/room/Cargo.toml b/room/Cargo.toml new file mode 100644 index 0000000..d081284 --- /dev/null +++ b/room/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "room" +version = "0.1.0" +edition = "2021" + +[dependencies] +maps = { workspace = true } +entity = { workspace = true } +quests = { workspace = true } +location = { workspace = true } +drops = { workspace = true } + +rand = { workspace = true } +async-std = { workspace = true } +futures = { workspace = true } +anyhow = { workspace = true } +thiserror = { workspace = true } diff --git a/src/ship/room.rs b/room/src/lib.rs similarity index 55% rename from src/ship/room.rs rename to room/src/lib.rs index b3a03c2..fca3669 100644 --- a/src/ship/room.rs +++ b/room/src/lib.rs @@ -1,5 +1,5 @@ use std::collections::HashMap; -use std::convert::{From, Into, TryFrom}; +use std::convert::{From, Into}; use async_std::sync::{Arc, RwLock, RwLockReadGuard}; use futures::future::BoxFuture; use futures::stream::{FuturesOrdered, Stream}; @@ -7,19 +7,29 @@ use futures::stream::{FuturesOrdered, Stream}; use thiserror::Error; use rand::Rng; -use crate::ship::map::Maps; -use crate::ship::drops::DropTable; -use crate::entity::character::SectionID; -use crate::entity::room::{RoomEntityId, RoomEntityMode}; -use crate::ship::monster::{load_monster_stats_table, MonsterType, MonsterStats}; -use crate::ship::map::area::MapAreaLookup; -use crate::ship::quests; -use crate::ship::ship::{ShipError, ShipEvent}; -use crate::ship::location::{MAX_ROOMS, RoomId}; +use quests::{QuestList, QuestLoadError}; +use maps::maps::Maps; +use drops::DropTable; +use entity::character::SectionID; +use entity::room::{RoomEntityId, RoomEntityMode}; +use maps::monster::{load_monster_stats_table, MonsterType, MonsterStats}; +use maps::area::MapAreaLookup; +use maps::Holiday; +use location::{MAX_ROOMS, RoomId}; + +use maps::room::{Episode, Difficulty, RoomMode}; + +#[derive(Error, Debug)] +pub enum RoomError { + #[error("invalid room id {0}")] + Invalid(u32), +} + #[derive(Clone)] pub struct Rooms([Arc>>; MAX_ROOMS]); + impl Default for Rooms { fn default() -> Rooms { Rooms(core::array::from_fn(|_| Arc::new(RwLock::new(None)))) @@ -30,7 +40,7 @@ impl Rooms { pub async fn add(&self, room_id: RoomId, room: RoomState) -> Result<(), anyhow::Error> { *self.0 .get(room_id.0) - .ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))? + .ok_or(RoomError::Invalid(room_id.0 as u32))? .write() .await = Some(room); Ok(()) @@ -63,14 +73,14 @@ impl Rooms { { let room = self.0 .get(room_id.0) - .ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))? + .ok_or(RoomError::Invalid(room_id.0 as u32))? .read() .await; if let Some(room) = room.as_ref() { Ok(func(room).await) } else { - Err(ShipError::InvalidRoom(room_id.0 as u32).into()) + Err(RoomError::Invalid(room_id.0 as u32).into()) } } @@ -81,7 +91,7 @@ impl Rooms { { let mut room = self.0 .get(room_id.0) - .ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))? + .ok_or(RoomError::Invalid(room_id.0 as u32))? .write() .await; @@ -89,7 +99,7 @@ impl Rooms { Ok(func(room).await) } else { - Err(ShipError::InvalidRoom(room_id.0 as u32).into()) + Err(RoomError::Invalid(room_id.0 as u32).into()) } } @@ -123,156 +133,7 @@ pub enum RoomCreationError { CouldNotLoadQuests, } -#[derive(Debug, Copy, Clone, derive_more::Display)] -pub enum Episode { - #[display(fmt="ep1")] - One, - #[display(fmt="ep2")] - Two, - #[display(fmt="ep4")] - Four, -} - -#[derive(Debug, Copy, Clone)] -pub enum PlayerMode{ - Single, - Multi, -} - -impl PlayerMode { - pub fn value(&self) -> u8 { - match self { - PlayerMode::Single => 1, - PlayerMode::Multi => 0, - } - } -} - -impl TryFrom for Episode { - type Error = RoomCreationError; - - fn try_from(value: u8) -> Result { - match value { - 1 => Ok(Episode::One), - 2 => Ok(Episode::Two), - 3 => Ok(Episode::Four), - _ => Err(RoomCreationError::InvalidEpisode(value)) - } - } -} - -impl From for u8 { - fn from(other: Episode) -> u8 { - match other { - Episode::One => 1, - Episode::Two => 2, - Episode::Four => 3, - } - } -} - -impl Episode { - pub fn from_quest(value: u8) -> Result { - match value { - 0 => Ok(Episode::One), - 1 => Ok(Episode::Two), - 2 => Ok(Episode::Four), - _ => Err(RoomCreationError::InvalidEpisode(value)) - } - } -} -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, derive_more::Display)] -pub enum Difficulty { - Normal, - Hard, - VeryHard, - Ultimate, -} - -impl TryFrom for Difficulty { - type Error = RoomCreationError; - - fn try_from(value: u8) -> Result { - match value { - 0 => Ok(Difficulty::Normal), - 1 => Ok(Difficulty::Hard), - 2 => Ok(Difficulty::VeryHard), - 3 => Ok(Difficulty::Ultimate), - _ => Err(RoomCreationError::InvalidDifficulty(value)) - } - } -} - -impl From for u8 { - fn from(other: Difficulty) -> u8 { - match other { - Difficulty::Normal => 0, - Difficulty::Hard => 1, - Difficulty::VeryHard => 2, - Difficulty::Ultimate => 3, - } - } -} - -#[derive(Debug, Copy, Clone, derive_more::Display)] -pub enum RoomMode { - #[display(fmt="single")] - Single { - episode: Episode, - difficulty: Difficulty, - }, - #[display(fmt="multi")] - Multi { - episode: Episode, - difficulty: Difficulty, - }, - #[display(fmt="challenge")] - Challenge { - episode: Episode, - }, - #[display(fmt="battle")] - Battle { - episode: Episode, - difficulty: Difficulty, - } -} - - -impl RoomMode { - pub fn difficulty(&self) -> Difficulty { - match self { - RoomMode::Single {difficulty, ..} => *difficulty, - RoomMode::Multi {difficulty, ..} => *difficulty, - RoomMode::Battle {difficulty, ..} => *difficulty, - RoomMode::Challenge {..} => Difficulty::Normal, - } - } - - pub fn episode(&self) -> Episode { - match self { - RoomMode::Single {episode, ..} => *episode, - RoomMode::Multi {episode, ..} => *episode, - RoomMode::Battle {episode, ..} => *episode, - RoomMode::Challenge {episode, ..} => *episode, - } - } - - pub fn battle(&self) -> bool { - matches!(self, RoomMode::Battle {..}) - } - - pub fn challenge(&self) -> bool { - matches!(self, RoomMode::Challenge {..}) - } - - pub fn player_mode(&self) -> PlayerMode { - match self { - RoomMode::Single {..} => PlayerMode::Single, - _ => PlayerMode::Multi, - } - } -} pub enum QuestCategoryType { Standard, Government, @@ -310,15 +171,15 @@ pub struct RoomState { pub name: String, pub password: [u16; 16], pub maps: Maps, - pub drop_table: Box, + pub drop_table: Box, pub section_id: SectionID, pub random_seed: u32, pub bursting: bool, pub monster_stats: Box>, pub map_areas: MapAreaLookup, pub quest_group: QuestCategoryType, - pub standard_quests: quests::QuestList, - pub government_quests: quests::QuestList, + pub standard_quests: QuestList, + pub government_quests: QuestList, // enemy info } @@ -353,14 +214,14 @@ impl RoomState { difficulty + 0x22 } - pub fn quests(&self) -> &quests::QuestList { + pub fn quests(&self) -> &QuestList { match self.quest_group { QuestCategoryType::Standard => &self.standard_quests, QuestCategoryType::Government => &self.government_quests, } } - #[allow(clippy::too_many_arguments)] + #[allow(clippy::too_many_arguments, clippy::type_complexity)] pub fn new (room_id: RoomEntityId, mode: RoomEntityMode, episode: Episode, @@ -368,9 +229,11 @@ impl RoomState { section_id: SectionID, name: String, password: [u16; 16], - event: ShipEvent, - map_builder: Arc Maps + Send + Sync>>, - drop_table_builder: Arc DropTable + Send + Sync>>, + event: Holiday, + map_builder: Arc Maps + Send + Sync>>, + drop_table_builder: Arc Box + Send + Sync>>, + standard_quest_builder: Arc Result + Send + Sync>>, + government_quest_builder: Arc Result + Send + Sync>>, ) -> Result { let mode = match mode { RoomEntityMode::Single => RoomMode::Single { @@ -399,12 +262,12 @@ impl RoomState { password, maps: map_builder(mode, event), section_id, - drop_table: Box::new(drop_table_builder(episode, difficulty, section_id)), + drop_table: drop_table_builder(episode, difficulty, section_id), bursting: false, map_areas: MapAreaLookup::new(&episode), quest_group: QuestCategoryType::Standard, - standard_quests: quests::load_standard_quests(mode)?, - government_quests: quests::load_government_quests(mode)?, + standard_quests: standard_quest_builder(mode)?, + government_quests: government_quest_builder(mode)?, }) } diff --git a/shops/Cargo.toml b/shops/Cargo.toml new file mode 100644 index 0000000..25e3055 --- /dev/null +++ b/shops/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "shops" +version = "0.1.0" +edition = "2021" + +[dependencies] +maps = { workspace = true } +stats = { workspace = true } +entity = { workspace = true } + +async-std = { workspace = true } +async-trait = { workspace = true } +futures = { workspace = true } +rand = { workspace = true } +rand_chacha = { workspace = true } +toml = { workspace = true } +serde = { workspace = true } \ No newline at end of file diff --git a/src/ship/shops/armor.rs b/shops/src/armor.rs similarity index 95% rename from src/ship/shops/armor.rs rename to shops/src/armor.rs index d1d8b47..f424877 100644 --- a/src/ship/shops/armor.rs +++ b/shops/src/armor.rs @@ -5,12 +5,12 @@ use std::convert::TryInto; use serde::Deserialize; use rand::{Rng, SeedableRng}; use rand::distributions::{WeightedIndex, Distribution}; -use crate::entity::item::ItemDetail; -use crate::entity::item::armor::{Armor, ArmorType}; -use crate::entity::item::shield::{Shield, ShieldType}; -use crate::entity::item::unit::{Unit, UnitType}; -use crate::ship::shops::ShopItem; -use crate::ship::item_stats::{ARMOR_STATS, SHIELD_STATS, UNIT_STATS}; +use entity::item::ItemDetail; +use entity::item::armor::{Armor, ArmorType}; +use entity::item::shield::{Shield, ShieldType}; +use entity::item::unit::{Unit, UnitType}; +use crate::ShopItem; +use stats::items::{ARMOR_STATS, SHIELD_STATS, UNIT_STATS}; // #[derive(Debug)] // pub enum ArmorShopItem { @@ -342,8 +342,8 @@ impl ArmorShop { pub fn generate_armor_list(&mut self, character_level: usize) -> Vec { self.generate_frame_list(character_level).into_iter() - .chain(self.generate_barrier_list(character_level).into_iter()) - .chain(self.generate_unit_list(character_level).into_iter()) + .chain(self.generate_barrier_list(character_level)) + .chain(self.generate_unit_list(character_level)) .collect() } } diff --git a/shops/src/lib.rs b/shops/src/lib.rs new file mode 100644 index 0000000..94abdec --- /dev/null +++ b/shops/src/lib.rs @@ -0,0 +1,95 @@ +mod weapon; +mod tool; +mod armor; + +use async_std::sync::{Arc, Mutex}; +use futures::future::OptionFuture; +use std::collections::HashMap; +use entity::item::ItemDetail; +use maps::room::Difficulty; +use entity::character::SectionID; + +pub use weapon::{WeaponShop, WeaponShopItem}; +pub use tool::{ToolShop, ToolShopItem}; +pub use armor::{ArmorShop, ArmorShopItem}; + +pub trait ShopItem { + fn price(&self) -> usize; + fn as_bytes(&self) -> [u8; 12]; + fn as_item(&self) -> ItemDetail; +} + +#[async_trait::async_trait] +pub trait ItemShops { + async fn generate_weapon_list(&self, difficulty: Difficulty, section_id: SectionID, char_level: usize) -> Option>; + async fn generate_tool_list(&self, char_level: usize) -> Vec; + async fn generate_armor_list(&self, char_level: usize) -> Vec; +} + + +#[derive(Clone)] +pub struct StandardItemShops { + weapon_shop: HashMap<(Difficulty, SectionID), Arc>>>, + tool_shop: Arc>>, + armor_shop: Arc>>, +} + +impl Default for StandardItemShops { + fn default() -> StandardItemShops { + let difficulty = [Difficulty::Normal, Difficulty::Hard, Difficulty::VeryHard, Difficulty::Ultimate]; + let section_id = [SectionID::Viridia, SectionID::Greenill, SectionID::Skyly, SectionID::Bluefull, SectionID::Purplenum, + SectionID::Pinkal, SectionID::Redria, SectionID::Oran, SectionID::Yellowboze, SectionID::Whitill]; + + let mut weapon_shop = HashMap::new(); + for d in difficulty.iter() { + for id in section_id.iter() { + weapon_shop.insert((*d, *id), Arc::new(Mutex::new(WeaponShop::new(*d, *id)))); + } + } + + StandardItemShops { + weapon_shop, + tool_shop: Arc::new(Mutex::new(ToolShop::default())), + armor_shop: Arc::new(Mutex::new(ArmorShop::default())), + } + } +} + +#[async_trait::async_trait] +impl ItemShops for StandardItemShops { + async fn generate_weapon_list(&self, difficulty: Difficulty, section_id: SectionID, char_level: usize) -> Option> { + OptionFuture::from( + self.weapon_shop + .get(&(difficulty, section_id)) + .map(|shop| async { + shop + .lock() + .await + .generate_weapon_list(char_level) + })).await + } + + async fn generate_tool_list(&self, char_level: usize) -> Vec { + self.tool_shop + .lock() + .await + .generate_tool_list(char_level) + } + + async fn generate_armor_list(&self, char_level: usize) -> Vec { + self.armor_shop + .lock() + .await + .generate_armor_list(char_level) + } +} + + + + +pub enum ShopType { + Weapon, + Tool, + Armor +} + diff --git a/src/ship/shops/tool.rs b/shops/src/tool.rs similarity index 92% rename from src/ship/shops/tool.rs rename to shops/src/tool.rs index b5f9718..b068d19 100644 --- a/src/ship/shops/tool.rs +++ b/shops/src/tool.rs @@ -6,11 +6,11 @@ use std::convert::TryInto; use serde::Deserialize; use rand::{Rng, SeedableRng}; use rand::distributions::{WeightedIndex, Distribution}; -use crate::entity::item::ItemDetail; -use crate::entity::item::tool::{Tool, ToolType}; -use crate::entity::item::tech::{Technique, TechniqueDisk}; -use crate::ship::shops::ShopItem; -use crate::ship::item_stats::{TOOL_STATS, TECH_STATS}; +use entity::item::ItemDetail; +use entity::item::tool::{Tool, ToolType}; +use entity::item::tech::{Technique, TechniqueDisk}; +use crate::ShopItem; +use stats::items::{TOOL_STATS, TECH_STATS}; #[derive(Debug, PartialEq, Eq)] @@ -36,16 +36,7 @@ impl Ord for ToolShopItem { impl PartialOrd for ToolShopItem { fn partial_cmp(&self, other: &ToolShopItem) -> Option { - let a = match self { - ToolShopItem::Tool(t) => Tool{tool : *t}.as_individual_bytes(), - ToolShopItem::Tech(t) => t.as_bytes(), - }; - let b = match other { - ToolShopItem::Tool(t) => Tool{tool : *t}.as_individual_bytes(), - ToolShopItem::Tech(t) => t.as_bytes(), - }; - - a.partial_cmp(&b) + Some(self.cmp(other)) } } @@ -285,7 +276,7 @@ impl ToolShop { pub fn generate_tool_list(&mut self, character_level: usize) -> Vec { let mut tools = Vec::new().into_iter() .chain(self.tools.0.clone().into_iter().map(ToolShopItem::Tool)) - .chain(self.generate_techs(character_level).into_iter()) + .chain(self.generate_techs(character_level)) .collect::>(); tools.sort(); tools diff --git a/src/ship/shops/weapon.rs b/shops/src/weapon.rs similarity index 96% rename from src/ship/shops/weapon.rs rename to shops/src/weapon.rs index 14509c8..06a8c1e 100644 --- a/src/ship/shops/weapon.rs +++ b/shops/src/weapon.rs @@ -8,12 +8,12 @@ use serde::Deserialize; use rand::{Rng, SeedableRng}; use rand::distributions::{WeightedIndex, Distribution}; use rand::seq::{SliceRandom, IteratorRandom}; -use crate::entity::character::SectionID; -use crate::ship::room::Difficulty; -use crate::entity::item::ItemDetail; -use crate::entity::item::weapon::{Weapon, WeaponType, WeaponSpecial, Attribute, WeaponAttribute}; -use crate::ship::shops::ShopItem; -use crate::ship::item_stats::WEAPON_STATS; +use entity::character::SectionID; +use maps::room::Difficulty; +use entity::item::ItemDetail; +use entity::item::weapon::{Weapon, WeaponType, WeaponSpecial, Attribute, WeaponAttribute}; +use crate::ShopItem; +use stats::items::WEAPON_STATS; const TIER1_SPECIAL: [WeaponSpecial; 8] = [WeaponSpecial::Draw, WeaponSpecial::Heart, WeaponSpecial::Ice, WeaponSpecial::Bind, @@ -412,7 +412,7 @@ impl WeaponShop { .last() .unwrap(); - let attr_choice = WeightedIndex::new(&[tier.none, tier.native, tier.abeast, tier.machine, tier.dark, tier.hit]).unwrap(); + let attr_choice = WeightedIndex::new([tier.none, tier.native, tier.abeast, tier.machine, tier.dark, tier.hit]).unwrap(); let attr = match attr_choice.sample(&mut self.rng) { 0 => return None, 1 => Attribute::Native, @@ -439,7 +439,7 @@ impl WeaponShop { .last() .unwrap(); - let attr_choice = WeightedIndex::new(&[tier.none, tier.native, tier.abeast, tier.machine, tier.dark, tier.hit]).unwrap(); + let attr_choice = WeightedIndex::new([tier.none, tier.native, tier.abeast, tier.machine, tier.dark, tier.hit]).unwrap(); let attr = match attr_choice.sample(&mut self.rng) { 0 => return None, 1 => Attribute::Native, diff --git a/src/bin/login.rs b/src/bin/login.rs index f8b6b94..f4c5335 100644 --- a/src/bin/login.rs +++ b/src/bin/login.rs @@ -1,8 +1,8 @@ use log::{info}; -use elseware::entity::gateway::postgres::PostgresGateway; +use entity::gateway::postgres::PostgresGateway; use elseware::login::login::LoginServerState; use elseware::login::character::CharacterServerState; -use elseware::common::interserver::AuthToken; +use networking::interserver::AuthToken; fn main() { let colors = fern::colors::ColoredLevelConfig::new() @@ -38,17 +38,17 @@ fn main() { let login_state = LoginServerState::new(entity_gateway.clone(), charserv_ip); let login_loop = async_std::task::spawn(async move { - elseware::common::mainloop::run_server(login_state, elseware::login::login::LOGIN_PORT).await; + networking::mainloop::run_server(login_state, elseware::login::login::LOGIN_PORT).await; }); let char_state = CharacterServerState::new(entity_gateway, AuthToken(shipgate_token)); let sub_char_state = char_state.clone(); let character_loop = async_std::task::spawn(async move { - elseware::common::mainloop::run_server(sub_char_state, elseware::login::character::CHARACTER_PORT).await; + networking::mainloop::run_server(sub_char_state, elseware::login::character::CHARACTER_PORT).await; }); let inter_character_loop = async_std::task::spawn(async move { - elseware::common::mainloop::run_interserver_listen(char_state, elseware::login::login::COMMUNICATION_PORT).await; + networking::mainloop::run_interserver_listen(char_state, elseware::login::login::COMMUNICATION_PORT).await; }); info!("[auth/character] starting server"); diff --git a/src/bin/main.rs b/src/bin/main.rs index 0a3ada0..e50db30 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -1,18 +1,18 @@ use std::net::Ipv4Addr; use log::{info}; -use elseware::common::interserver::AuthToken; +use networking::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, ShipEvent}; +use elseware::patch::{PatchServerState, generate_patch_tree, load_config, load_motd}; +use elseware::ship::ship::ShipServerStateBuilder; -#[allow(unused_imports)] -use elseware::entity::gateway::{EntityGateway, InMemoryGateway, PostgresGateway}; -use elseware::entity::account::{NewUserAccountEntity, NewUserSettingsEntity}; -use elseware::entity::character::NewCharacterEntity; -use elseware::entity::item::{NewItemEntity, ItemDetail, InventoryItemEntity}; -use elseware::entity::item; +use maps::Holiday; +use entity::gateway::{EntityGateway, InMemoryGateway}; +use entity::account::{NewUserAccountEntity, NewUserSettingsEntity}; +use entity::character::NewCharacterEntity; +use entity::item::{NewItemEntity, ItemDetail, InventoryItemEntity}; +use entity::item; fn setup_logger() { let colors = fern::colors::ColoredLevelConfig::new() @@ -338,25 +338,25 @@ fn main() { let (patch_file_tree, patch_file_lookup) = generate_patch_tree(patch_config.path.as_str()); let patch_state = PatchServerState::new(patch_file_tree, patch_file_lookup, patch_motd); let patch_loop = async_std::task::spawn(async move { - elseware::common::mainloop::run_server(patch_state, patch_config.port).await; + networking::mainloop::run_server(patch_state, patch_config.port).await; }); info!("[auth] starting server"); let login_state = LoginServerState::new(entity_gateway.clone(), "127.0.0.1".parse().unwrap()); let login_loop = async_std::task::spawn(async move { - elseware::common::mainloop::run_server(login_state, elseware::login::login::LOGIN_PORT).await; + networking::mainloop::run_server(login_state, elseware::login::login::LOGIN_PORT).await; }); info!("[character] starting server"); let char_state = CharacterServerState::new(entity_gateway.clone(), AuthToken("".into())); let sub_char_state = char_state.clone(); let character_loop = async_std::task::spawn(async move { - elseware::common::mainloop::run_server(sub_char_state, elseware::login::character::CHARACTER_PORT).await; + networking::mainloop::run_server(sub_char_state, elseware::login::character::CHARACTER_PORT).await; }); let sub_char_state = char_state.clone(); let inter_character_loop = async_std::task::spawn(async move { - elseware::common::mainloop::run_interserver_listen(sub_char_state, elseware::login::login::COMMUNICATION_PORT).await; + networking::mainloop::run_interserver_listen(sub_char_state, elseware::login::login::COMMUNICATION_PORT).await; }); info!("[ship] starting servers"); @@ -364,32 +364,32 @@ fn main() { .name("US/Sona-Nyl".into()) .ip(Ipv4Addr::new(127,0,0,1)) .port(elseware::ship::ship::SHIP_PORT) - .event(ShipEvent::Halloween) + .event(Holiday::Halloween) .gateway(entity_gateway.clone()) .build(); let sub_ship_state = ship_state.clone(); let ship_loop1 = async_std::task::spawn(async move { - elseware::common::mainloop::run_server(sub_ship_state, elseware::ship::ship::SHIP_PORT).await; + networking::mainloop::run_server(sub_ship_state, elseware::ship::ship::SHIP_PORT).await; }); let sub_ship_state = ship_state.clone(); let inter_ship_loop1 = async_std::task::spawn(async move { - elseware::common::mainloop::run_interserver_connect(sub_ship_state, std::net::Ipv4Addr::new(127, 0, 0, 1), elseware::login::login::COMMUNICATION_PORT).await; + networking::mainloop::run_interserver_connect(sub_ship_state, std::net::Ipv4Addr::new(127, 0, 0, 1), elseware::login::login::COMMUNICATION_PORT).await; }); let ship_state = ShipServerStateBuilder::default() .name("EU/Dylath-Leen".into()) .ip(Ipv4Addr::new(127,0,0,1)) .port(elseware::ship::ship::SHIP_PORT+2000) - .event(ShipEvent::Christmas) + .event(Holiday::Christmas) .gateway(entity_gateway.clone()) .build(); let sub_ship_state = ship_state.clone(); let ship_loop2 = async_std::task::spawn(async move { - elseware::common::mainloop::run_server(sub_ship_state, elseware::ship::ship::SHIP_PORT+2000).await; + networking::mainloop::run_server(sub_ship_state, elseware::ship::ship::SHIP_PORT+2000).await; }); let sub_ship_state = ship_state.clone(); let inter_ship_loop2 = async_std::task::spawn(async move { - elseware::common::mainloop::run_interserver_connect(sub_ship_state, std::net::Ipv4Addr::new(127, 0, 0, 1), elseware::login::login::COMMUNICATION_PORT).await; + networking::mainloop::run_interserver_connect(sub_ship_state, std::net::Ipv4Addr::new(127, 0, 0, 1), elseware::login::login::COMMUNICATION_PORT).await; }); let ship_state = ShipServerStateBuilder::default() @@ -400,11 +400,11 @@ fn main() { .build(); let sub_ship_state = ship_state.clone(); let ship_loop3 = async_std::task::spawn(async move { - elseware::common::mainloop::run_server(sub_ship_state, elseware::ship::ship::SHIP_PORT+3000).await; + networking::mainloop::run_server(sub_ship_state, elseware::ship::ship::SHIP_PORT+3000).await; }); let sub_ship_state = ship_state.clone(); let inter_ship_loop3 = async_std::task::spawn(async move { - elseware::common::mainloop::run_interserver_connect(sub_ship_state, std::net::Ipv4Addr::new(127, 0, 0, 1), elseware::login::login::COMMUNICATION_PORT).await; + networking::mainloop::run_interserver_connect(sub_ship_state, std::net::Ipv4Addr::new(127, 0, 0, 1), elseware::login::login::COMMUNICATION_PORT).await; }); futures::future::join_all(vec![patch_loop, login_loop, character_loop, inter_character_loop, diff --git a/src/bin/patch.rs b/src/bin/patch.rs index e7304d8..a9fb839 100644 --- a/src/bin/patch.rs +++ b/src/bin/patch.rs @@ -1,5 +1,5 @@ -use elseware::patch::patch::{PatchServerState, generate_patch_tree, load_config_env, load_motd}; -use log::{info}; +use elseware::patch::{PatchServerState, generate_patch_tree, load_config_env, load_motd}; +use log::info; fn main() { info!("[patch] starting server"); @@ -9,10 +9,8 @@ fn main() { let patch_state = PatchServerState::new(patch_file_tree, patch_file_lookup, patch_motd); let patch_loop = async_std::task::spawn(async move { - elseware::common::mainloop::run_server(patch_state, patch_config.port).await; + networking::mainloop::run_server(patch_state, patch_config.port).await; }); - async_std::task::block_on(async move { - patch_loop.await - }); + async_std::task::block_on(patch_loop); } diff --git a/src/bin/ship.rs b/src/bin/ship.rs index ff1ccc6..c021601 100644 --- a/src/bin/ship.rs +++ b/src/bin/ship.rs @@ -1,7 +1,7 @@ -use log::{info}; -use elseware::entity::gateway::postgres::PostgresGateway; +use log::info; +use entity::gateway::postgres::PostgresGateway; use elseware::ship::ship::ShipServerStateBuilder; -use elseware::common::interserver::AuthToken; +use networking::interserver::AuthToken; fn main() { let colors = fern::colors::ColoredLevelConfig::new() @@ -49,10 +49,10 @@ fn main() { let sub_ship_state = ship_state.clone(); let ship_loop = async_std::task::spawn(async move { - elseware::common::mainloop::run_server(sub_ship_state, elseware::ship::ship::SHIP_PORT).await; + networking::mainloop::run_server(sub_ship_state, elseware::ship::ship::SHIP_PORT).await; }); let inter_ship_loop = async_std::task::spawn(async move { - elseware::common::mainloop::run_interserver_connect(ship_state, shipgate_ip, elseware::login::login::COMMUNICATION_PORT).await; + networking::mainloop::run_interserver_connect(ship_state, shipgate_ip, elseware::login::login::COMMUNICATION_PORT).await; }); info!("[auth/character] starting server"); diff --git a/src/entity/gateway/postgres/migrations/mod.rs b/src/entity/gateway/postgres/migrations/mod.rs deleted file mode 100644 index 533298a..0000000 --- a/src/entity/gateway/postgres/migrations/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -use refinery::include_migration_mods; - -include_migration_mods!("src/entity/gateway/postgres/migrations"); diff --git a/src/lib.rs b/src/lib.rs index 2c3d9c0..f194399 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,17 +1,15 @@ #![allow(clippy::type_complexity)] #![allow(incomplete_features)] #![feature(inline_const)] -#![feature(drain_filter)] +#![feature(extract_if)] #![feature(try_blocks)] -#![feature(once_cell)] #![feature(test)] #![feature(error_generic_member_access)] -#![feature(provide_any)] extern crate test; -pub mod common; -pub mod entity; +//pub mod common; +//pub mod entity; pub mod patch; pub mod login; pub mod ship; diff --git a/src/login/character.rs b/src/login/character.rs index f23beea..6529f3d 100644 --- a/src/login/character.rs +++ b/src/login/character.rs @@ -12,30 +12,31 @@ use libpso::packet::login::*; use libpso::packet::ship::{MenuDetail, SmallLeftDialog}; use libpso::{PacketParseError, PSOPacket}; use libpso::crypto::bb::PSOBBCipher; -use crate::entity::item; use libpso::character::character; +use entity::item; -use crate::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY}; -use crate::common::serverstate::{SendServerPacket, RecvServerPacket, ServerState, OnConnect, ClientId}; -use crate::common::interserver::{ServerId, InterserverActor, LoginMessage, ShipMessage, Ship}; -use crate::common::leveltable::LEVEL_TABLE; +use networking::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY}; +use networking::serverstate::{SendServerPacket, RecvServerPacket, ServerState, OnConnect, ClientId}; +use networking::interserver::{ServerId, InterserverActor, LoginMessage, ShipMessage, Ship}; +use stats::leveltable::LEVEL_TABLE; use libpso::{utf8_to_array, utf8_to_utf16_array}; -use crate::entity::gateway::{EntityGateway, GatewayError}; -use crate::entity::account::{UserAccountId, UserAccountEntity, NewUserSettingsEntity, USERFLAG_NEWCHAR, USERFLAG_DRESSINGROOM}; -use crate::entity::item::{NewItemEntity, ItemDetail, ItemNote, InventoryItemEntity, InventoryEntity, BankEntity, BankIdentifier, EquippedEntity, Meseta}; -use crate::entity::item::weapon::Weapon; -use crate::entity::item::armor::Armor; -use crate::entity::item::tech::Technique; -use crate::entity::item::tool::Tool; -use crate::entity::item::mag::Mag; -use crate::entity::character::{CharacterEntity, NewCharacterEntity, CharacterClass, TechLevel}; +use entity::gateway::{EntityGateway, GatewayError}; +use entity::account::{UserAccountId, UserAccountEntity, NewUserSettingsEntity, USERFLAG_NEWCHAR, USERFLAG_DRESSINGROOM}; +use entity::item::{NewItemEntity, ItemDetail, ItemNote, InventoryItemEntity, InventoryEntity, BankEntity, BankIdentifier, EquippedEntity, Meseta}; +use entity::item::weapon::Weapon; +use entity::item::armor::Armor; +use entity::item::tech::Technique; +use entity::item::tool::Tool; +use entity::item::mag::Mag; +use entity::character::{CharacterEntity, NewCharacterEntity, CharacterClass, TechLevel}; use crate::login::login::{get_login_status}; -use crate::common::interserver::AuthToken; +use networking::interserver::AuthToken; + +use pktbuilder::ship::SHIP_MENU_ID; pub const CHARACTER_PORT: u16 = 12001; -pub const SHIP_MENU_ID: u32 = 1; #[derive(thiserror::Error, Debug)] pub enum CharacterError { @@ -484,7 +485,7 @@ impl CharacterServerState { async fn set_flag(&mut self, id: ClientId, setflag: &SetFlag) -> Result, anyhow::Error> { let mut client = self.clients.write().await; let client = client.get_mut(&id).ok_or_else(|| CharacterError::ClientNotFound(id))?; - let mut user = client.user.as_mut().unwrap(); + let user = client.user.as_mut().unwrap(); user.flags = setflag.flags; self.entity_gateway.save_user(user).await.unwrap(); Ok(None.into_iter()) @@ -515,7 +516,7 @@ impl CharacterServerState { async fn character_preview(&mut self, id: ClientId, preview: &CharacterPreview) -> Result, anyhow::Error> { let mut client = self.clients.write().await; let client = client.get_mut(&id).ok_or_else(|| CharacterError::ClientNotFound(id))?; - let mut user = client.user.as_mut().unwrap(); + let user = client.user.as_mut().unwrap(); if user.flags == USERFLAG_NEWCHAR { new_character(&mut self.entity_gateway, user, preview).await? } @@ -834,9 +835,21 @@ impl<'a> SelectScreenCharacterBuilder<'a> { #[cfg(test)] mod test { use super::*; - use crate::entity::account::*; + use entity::account::*; use libpso::character::{settings, character}; - use crate::entity::gateway::{InMemoryGateway, GatewayError}; + use entity::gateway::{InMemoryGateway, EntityGatewayTransaction, GatewayError}; + + #[derive(Clone)] + struct CharTestDb; + + impl EntityGateway for CharTestDb { + type Transaction<'t> = CharTestDb where Self: 't; + } + + impl EntityGatewayTransaction for CharTestDb { + type ParentGateway = CharTestDb; + } + #[async_std::test] async fn test_option_send() { @@ -846,7 +859,7 @@ mod test { #[async_trait::async_trait] impl EntityGateway for TestData { - type Transaction<'a> = () where Self: 'a; + type Transaction<'a> = CharTestDb where Self: 'a; async fn get_user_settings_by_user(&mut self, user: &UserAccountEntity) -> Result { Ok(UserSettingsEntity { id: UserSettingsId(0), @@ -889,7 +902,7 @@ mod test { #[derive(Clone)] struct TestData; impl EntityGateway for TestData { - type Transaction<'a> = () where Self: 'a; + type Transaction<'a> = CharTestDb where Self: 'a; } let mut server = CharacterServerState::new(TestData {}, AuthToken("".into())); let send = server.handle(ClientId(1), RecvCharacterPacket::Checksum(Checksum {checksum: 1234, diff --git a/src/login/login.rs b/src/login/login.rs index b4fcad6..cdc32d9 100644 --- a/src/login/login.rs +++ b/src/login/login.rs @@ -11,11 +11,11 @@ use libpso::{PacketParseError, PSOPacket}; use libpso::crypto::bb::PSOBBCipher; use libpso::util::array_to_utf8; -use crate::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY}; -use crate::common::serverstate::{SendServerPacket, RecvServerPacket, ServerState, OnConnect, ClientId}; +use networking::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY}; +use networking::serverstate::{SendServerPacket, RecvServerPacket, ServerState, OnConnect, ClientId}; -use crate::entity::gateway::EntityGateway; -use crate::entity::account::{UserAccountEntity}; +use entity::gateway::EntityGateway; +use entity::account::{UserAccountEntity}; pub const LOGIN_PORT: u16 = 12000; pub const COMMUNICATION_PORT: u16 = 12123; @@ -178,8 +178,8 @@ impl ServerState for LoginServerState { #[cfg(test)] mod test { use super::*; - use crate::entity::account::{UserAccountId}; - use crate::entity::gateway::{EntityGatewayTransaction, GatewayError}; + use entity::account::{UserAccountId}; + use entity::gateway::{EntityGatewayTransaction, GatewayError}; const LOGIN_PACKET: RecvLoginPacket = RecvLoginPacket::Login(Login { tag: 65536, @@ -204,13 +204,16 @@ mod test { character_slot: 0, } }); - - impl EntityGateway for () { - type Transaction<'t> = () where Self: 't; + + #[derive(Clone)] + struct LoginTestDb; + + impl EntityGateway for LoginTestDb { + type Transaction<'t> = LoginTestDb where Self: 't; } - impl EntityGatewayTransaction for () { - type ParentGateway = (); + impl EntityGatewayTransaction for LoginTestDb { + type ParentGateway = LoginTestDb; } #[async_std::test] @@ -221,7 +224,7 @@ mod test { #[async_trait::async_trait] impl EntityGateway for TestData { - type Transaction<'t> = () where Self: 't; + type Transaction<'t> = LoginTestDb where Self: 't; async fn get_user_by_name(&mut self, name: String) -> Result { assert!(name == "testuser"); Ok(UserAccountEntity { @@ -280,7 +283,7 @@ mod test { #[async_trait::async_trait] impl EntityGateway for TestData { - type Transaction<'t> = () where Self: 't; + type Transaction<'t> = LoginTestDb where Self: 't; async fn get_user_by_name(&mut self, _name: String) -> Result { Err(GatewayError::Error) } @@ -315,7 +318,7 @@ mod test { #[async_trait::async_trait] impl EntityGateway for TestData { - type Transaction<'t> = () where Self: 't; + type Transaction<'t> = LoginTestDb where Self: 't; async fn get_user_by_name(&mut self, name: String) -> Result { assert!(name == "testuser"); Ok(UserAccountEntity { @@ -365,7 +368,7 @@ mod test { #[async_trait::async_trait] impl EntityGateway for TestData { - type Transaction<'t> = () where Self: 't; + type Transaction<'t> = LoginTestDb where Self: 't; async fn get_user_by_name(&mut self, name: String) -> Result { assert!(name == "testuser"); Ok(UserAccountEntity { diff --git a/src/patch/mod.rs b/src/patch/mod.rs index 323a777..565530d 100644 --- a/src/patch/mod.rs +++ b/src/patch/mod.rs @@ -1,2 +1,432 @@ -#[allow(clippy::module_inception)] -pub mod patch; +use std::collections::{HashMap, HashSet}; +use std::fs; +use std::io; +use std::io::{Read}; +use std::path::{Path, PathBuf}; +use rand::Rng; +use crc::{crc32, Hasher32}; +use libpso::{PacketParseError, PSOPacket}; +use libpso::packet::patch::*; +use libpso::crypto::pc::PSOPCCipher; +use ron::de::from_str; +use serde::Deserialize; + +use networking::mainloop::{NetworkError}; +use networking::serverstate::{RecvServerPacket, SendServerPacket, ServerState, OnConnect, ClientId}; + +#[derive(Debug)] +pub enum PatchError { + NetworkError(NetworkError), + IOError(std::io::Error), +} + +impl From for PatchError { + fn from(err: NetworkError) -> PatchError { + PatchError::NetworkError(err) + } +} + +impl From for PatchError { + fn from(err: std::io::Error) -> PatchError { + PatchError::IOError(err) + } +} + + +#[derive(Debug, Clone)] +pub struct PatchFile { + path: PathBuf, + checksum: u32, + size: u32, +} + +pub enum PatchTreeIterItem { + Directory(PathBuf), + File(PathBuf, u32), + UpDirectory, +} + +#[derive(Debug, Clone)] +pub enum PatchFileTree { + Directory(PathBuf, Vec), + File(PathBuf, u32), // file_id +} + +impl PatchFileTree { + fn iter_dir(tree: &PatchFileTree) -> Vec { + let mut v = Vec::new(); + + match tree { + PatchFileTree::Directory(dir, files) => { + v.push(PatchTreeIterItem::Directory(dir.clone())); + for file in files { + v.append(&mut PatchFileTree::iter_dir(file)); + } + v.push(PatchTreeIterItem::UpDirectory); + }, + PatchFileTree::File(path, id) => { + v.push(PatchTreeIterItem::File(path.clone(), *id)); + } + } + + v + } + + pub fn flatten(&self) -> Vec { + PatchFileTree::iter_dir(self) + } +} + + +#[derive(Debug)] +pub enum RecvPatchPacket { + PatchWelcomeReply(PatchWelcomeReply), + LoginReply(LoginReply), + FileInfoReply(FileInfoReply), + FileInfoListEnd(FileInfoListEnd), +} + +impl RecvServerPacket for RecvPatchPacket { + fn from_bytes(data: &[u8]) -> Result { + match data[2] { + 0x02 => Ok(RecvPatchPacket::PatchWelcomeReply(PatchWelcomeReply::from_bytes(data)?)), + 0x04 => Ok(RecvPatchPacket::LoginReply(LoginReply::from_bytes(data)?)), + 0x0F => Ok(RecvPatchPacket::FileInfoReply(FileInfoReply::from_bytes(data)?)), + 0x10 => Ok(RecvPatchPacket::FileInfoListEnd(FileInfoListEnd::from_bytes(data)?)), + _ => Err(PacketParseError::WrongPacketForServerType(u16::from_le_bytes([data[2], data[3]]), data.to_vec())) + } + } +} + +#[derive(Debug)] +pub enum SendPatchPacket { + ChangeDirectory(ChangeDirectory), + EndFileSend(EndFileSend), + FileInfo(FileInfo), + FileSend(Box), + FilesToPatchMetadata(FilesToPatchMetadata), + FinalizePatching(FinalizePatching), + Message(Message), + PatchEndList(PatchEndList), + PatchStartList(PatchStartList), + PatchWelcome(PatchWelcome), + RequestLogin(RequestLogin), + StartFileSend(StartFileSend), + UpOneDirectory(UpOneDirectory), +} + +impl SendServerPacket for SendPatchPacket { + fn as_bytes(&self) -> Vec { + match self { + SendPatchPacket::ChangeDirectory(pkt) => pkt.as_bytes(), + SendPatchPacket::EndFileSend(pkt) => pkt.as_bytes(), + SendPatchPacket::FileInfo(pkt) => pkt.as_bytes(), + SendPatchPacket::FileSend(pkt) => pkt.as_bytes(), + SendPatchPacket::FilesToPatchMetadata(pkt) => pkt.as_bytes(), + SendPatchPacket::FinalizePatching(pkt) => pkt.as_bytes(), + SendPatchPacket::Message(pkt) => pkt.as_bytes(), + SendPatchPacket::PatchEndList(pkt) => pkt.as_bytes(), + SendPatchPacket::PatchStartList(pkt) => pkt.as_bytes(), + SendPatchPacket::PatchWelcome(pkt) => pkt.as_bytes(), + SendPatchPacket::RequestLogin(pkt) => pkt.as_bytes(), + SendPatchPacket::StartFileSend(pkt) => pkt.as_bytes(), + SendPatchPacket::UpOneDirectory(pkt) => pkt.as_bytes(), + } + } +} + + +#[derive(Clone)] +pub struct PatchServerState { + patch_file_tree: PatchFileTree, + patch_file_lookup: HashMap, + patch_file_info: Vec, + patch_motd: String, +} + +impl PatchServerState { + pub fn new(patch_file_tree: PatchFileTree, patch_file_lookup: HashMap, patch_motd: String) -> PatchServerState { + PatchServerState { + patch_file_tree, + patch_file_lookup, + patch_file_info: Vec::new(), + patch_motd, + } + } +} + +#[async_trait::async_trait] +impl ServerState for PatchServerState { + type SendPacket = SendPatchPacket; + type RecvPacket = RecvPatchPacket; + type Cipher = PSOPCCipher; + type PacketError = PatchError; + + async fn on_connect(&mut self, _id: ClientId) -> Result>, PatchError> { + let mut rng = rand::thread_rng(); + let key_in: u32 = rng.gen(); + let key_out: u32 = rng.gen(); + + Ok(vec![OnConnect::Packet(SendPatchPacket::PatchWelcome(PatchWelcome::new(key_out, key_in))), + OnConnect::Cipher(PSOPCCipher::new(key_in), PSOPCCipher::new(key_out)) + ]) + } + + async fn handle(&mut self, id: ClientId, pkt: RecvPatchPacket) -> Result, PatchError> { + Ok(match pkt { + RecvPatchPacket::PatchWelcomeReply(_pkt) => { + vec![SendPatchPacket::RequestLogin(RequestLogin {})] + .into_iter() + .map(move |pkt| (id, pkt)) + .collect() + }, + RecvPatchPacket::LoginReply(_pkt) => { + let mut pkts = vec![SendPatchPacket::Message(Message::new(self.patch_motd.clone()))]; + pkts.append(&mut get_file_list_packets(&self.patch_file_tree)); + pkts.push(SendPatchPacket::PatchEndList(PatchEndList {})); + pkts + .into_iter() + .map(move |pkt| (id, pkt)) + .collect() + }, + RecvPatchPacket::FileInfoReply(pkt) => { + self.patch_file_info.push(pkt); + Vec::new() + }, + RecvPatchPacket::FileInfoListEnd(_pkt) => { + let need_update = self.patch_file_info.iter() + .filter(|file_info| does_file_need_updating(file_info, &self.patch_file_lookup)) + .collect::>(); + + let total_size = need_update.iter().fold(0, |a, file_info| a + file_info.size); + let total_files = need_update.len() as u32; + + vec![SendPatchPacket::FilesToPatchMetadata(FilesToPatchMetadata::new(total_size, total_files)), + SendPatchPacket::PatchStartList(PatchStartList {})] + .into_iter() + .chain(SendFileIterator::new(self)) + .map(move |pkt| (id, pkt)) + .collect() + } + }) + } + + async fn on_disconnect(&mut self, _id: ClientId) -> Result, PatchError> { + Ok(Vec::new()) + } +} + +fn load_patch_dir(basedir: &str, patchbase: &str, file_ids: &mut HashMap) -> PatchFileTree { + let paths = fs::read_dir(basedir).expect("could not read directory"); + + let mut files = Vec::new(); + let mut dirs = Vec::new(); + for p in paths { + let path = p.expect("not a real path").path(); + let patch_path = path.strip_prefix(basedir).unwrap(); + if path.is_dir() { + dirs.push(load_patch_dir(path.to_str().unwrap(), patch_path.to_str().unwrap(), file_ids)); + } + else { + files.push(PatchFileTree::File(patch_path.to_path_buf(), file_ids.len() as u32)); + let (checksum, size) = get_checksum_and_size(&path).unwrap(); + file_ids.insert(file_ids.len() as u32, PatchFile { + path, + checksum, + size, + }); + } + } + + files.append(&mut dirs); + + PatchFileTree::Directory(PathBuf::from(patchbase), files) +} + +pub fn generate_patch_tree(basedir: &str) -> (PatchFileTree, HashMap) { + let mut file_ids = HashMap::new(); + + let patch_tree = load_patch_dir(basedir, "", &mut file_ids); + + (patch_tree, file_ids) +} + + +fn get_file_list_packets(patch_file_tree: &PatchFileTree) -> Vec { + let mut pkts = Vec::new(); + + for item in patch_file_tree.flatten() { + match item { + PatchTreeIterItem::Directory(path) => { + pkts.push(SendPatchPacket::ChangeDirectory(ChangeDirectory::new(path.to_str().unwrap()))); + }, + PatchTreeIterItem::File(path, id) => { + pkts.push(SendPatchPacket::FileInfo(FileInfo::new(path.to_str().unwrap(), id))); + }, + PatchTreeIterItem::UpDirectory => { + pkts.push(SendPatchPacket::UpOneDirectory(UpOneDirectory {})); + } + } + } + + pkts +} + +fn get_checksum_and_size(path: &Path) -> Result<(u32, u32), PatchError> { + let file = fs::File::open(path)?; + let size = file.metadata().unwrap().len(); + let mut crc = crc32::Digest::new(crc32::IEEE); + let mut buf = [0u8; 1024 * 32]; + let mut reader = io::BufReader::new(file); + while let Ok(len) = reader.read(&mut buf) { + if len == 0 { + break; + } + crc.write(&buf[0..len]); + } + + Ok((crc.sum32(), size as u32)) +} + +fn does_file_need_updating(file_info: &FileInfoReply, patch_file_lookup: &HashMap) -> bool { + let patch_file = patch_file_lookup.get(&file_info.id).unwrap(); + patch_file.checksum != file_info.checksum || patch_file.size != file_info.size +} + + +struct SendFileIterator { + done: bool, + file_iter: Box + Send>, + patch_file_lookup: HashMap, + current_file: Option>, + chunk_num: u32, +} + +impl SendFileIterator { + fn new(state: &PatchServerState) -> SendFileIterator { + let file_ids_to_update = state.patch_file_info.iter() + .filter(|file_info| does_file_need_updating(file_info, &state.patch_file_lookup)) + .map(|k| k.id) + .collect::>(); + + SendFileIterator { + done: false, + patch_file_lookup: state.patch_file_lookup.clone(), + file_iter: Box::new(state.patch_file_tree.flatten().into_iter().filter(move |file| { + match file { + PatchTreeIterItem::File(_path, id) => { + file_ids_to_update.contains(id) + }, + _ => true, + } + })), + current_file: None, + chunk_num: 0, + } + } +} + +impl Iterator for SendFileIterator { + type Item = SendPatchPacket; + + fn next(&mut self) -> Option { + if self.done { + return None; + } + + match self.current_file { + Some(ref mut file) => { + let mut buf = [0u8; PATCH_FILE_CHUNK_SIZE as usize]; + let len = file.read(&mut buf).unwrap(); + if len == 0 { + self.current_file = None; + self.chunk_num = 0; + Some(SendPatchPacket::EndFileSend(EndFileSend::new())) + } + else { + let mut crc = crc32::Digest::new(crc32::IEEE); + crc.write(&buf[0..len]); + let pkt = SendPatchPacket::FileSend(Box::new(FileSend { + chunk_num: self.chunk_num, + checksum: crc.sum32(), + chunk_size: len as u32, + buffer: buf, + })); + self.chunk_num += 1; + Some(pkt) + } + }, + None => { + match self.file_iter.next() { + Some(next_file) => { + match next_file { + PatchTreeIterItem::Directory(path) => { + Some(SendPatchPacket::ChangeDirectory(ChangeDirectory::new(path.to_str().unwrap()))) + }, + PatchTreeIterItem::File(path, id) => { + let patch_file = self.patch_file_lookup.get(&id).unwrap(); + let file = fs::File::open(&patch_file.path).unwrap(); + let size = file.metadata().unwrap().len(); + self.current_file = Some(io::BufReader::new(file)); + Some(SendPatchPacket::StartFileSend(StartFileSend::new(path.to_str().unwrap(), size as u32, id))) + }, + PatchTreeIterItem::UpDirectory => { + Some(SendPatchPacket::UpOneDirectory(UpOneDirectory {})) + }, + } + }, + None => { + self.current_file = None; + self.done = true; + Some(SendPatchPacket::FinalizePatching(FinalizePatching {})) + } + } + } + } + } +} + +#[derive(Debug, Deserialize)] +pub struct PatchConfig { + pub path: String, + pub ip: String, // TODO: this does nothing + pub port: u16, +} + +pub fn load_config() -> PatchConfig { + let ini_file = match fs::File::open(std::path::Path::new("patch.ron")) { + Err(err) => panic!("Failed to open patch.ron config file. \n{err}"), + Ok(ini_file) => ini_file, + }; + + let mut s = String::new(); + if let Err(err) = (&ini_file).read_to_string(&mut s) { + panic!("Failed to read patch.ron config file. \n{err}"); + } + + let config: PatchConfig = match from_str(s.as_str()) { + Ok(config) => config, + Err(err) => panic!("Failed to load values from patch.ron \n{err}"), + }; + config +} + +pub fn load_config_env() -> PatchConfig { + let patch_path = std::env::var("PATCHFILE_DIR").unwrap(); + let patch_port = std::env::var("PATCH_PORT").unwrap().parse().unwrap(); + + PatchConfig { + path: patch_path, + ip: "127.0.0.1".into(), + port: patch_port, + } +} + +pub fn load_motd() -> String { + if let Ok(m) = fs::read_to_string("patch.motd") { + m + } + else { + "Welcome to Elseware!".to_string() + } +} diff --git a/src/patch/patch.rs b/src/patch/patch.rs deleted file mode 100644 index 26441f8..0000000 --- a/src/patch/patch.rs +++ /dev/null @@ -1,432 +0,0 @@ -use std::collections::{HashMap, HashSet}; -use std::fs; -use std::io; -use std::io::{Read}; -use std::path::{Path, PathBuf}; -use rand::Rng; -use crc::{crc32, Hasher32}; -use libpso::{PacketParseError, PSOPacket}; -use libpso::packet::patch::*; -use libpso::crypto::pc::PSOPCCipher; -use ron::de::from_str; -use serde::Deserialize; - -use crate::common::mainloop::{NetworkError}; -use crate::common::serverstate::{RecvServerPacket, SendServerPacket, ServerState, OnConnect, ClientId}; - -#[derive(Debug)] -pub enum PatchError { - NetworkError(NetworkError), - IOError(std::io::Error), -} - -impl From for PatchError { - fn from(err: NetworkError) -> PatchError { - PatchError::NetworkError(err) - } -} - -impl From for PatchError { - fn from(err: std::io::Error) -> PatchError { - PatchError::IOError(err) - } -} - - -#[derive(Debug, Clone)] -pub struct PatchFile { - path: PathBuf, - checksum: u32, - size: u32, -} - -pub enum PatchTreeIterItem { - Directory(PathBuf), - File(PathBuf, u32), - UpDirectory, -} - -#[derive(Debug, Clone)] -pub enum PatchFileTree { - Directory(PathBuf, Vec), - File(PathBuf, u32), // file_id -} - -impl PatchFileTree { - fn iter_dir(tree: &PatchFileTree) -> Vec { - let mut v = Vec::new(); - - match tree { - PatchFileTree::Directory(dir, files) => { - v.push(PatchTreeIterItem::Directory(dir.clone())); - for file in files { - v.append(&mut PatchFileTree::iter_dir(file)); - } - v.push(PatchTreeIterItem::UpDirectory); - }, - PatchFileTree::File(path, id) => { - v.push(PatchTreeIterItem::File(path.clone(), *id)); - } - } - - v - } - - pub fn flatten(&self) -> Vec { - PatchFileTree::iter_dir(self) - } -} - - -#[derive(Debug)] -pub enum RecvPatchPacket { - PatchWelcomeReply(PatchWelcomeReply), - LoginReply(LoginReply), - FileInfoReply(FileInfoReply), - FileInfoListEnd(FileInfoListEnd), -} - -impl RecvServerPacket for RecvPatchPacket { - fn from_bytes(data: &[u8]) -> Result { - match data[2] { - 0x02 => Ok(RecvPatchPacket::PatchWelcomeReply(PatchWelcomeReply::from_bytes(data)?)), - 0x04 => Ok(RecvPatchPacket::LoginReply(LoginReply::from_bytes(data)?)), - 0x0F => Ok(RecvPatchPacket::FileInfoReply(FileInfoReply::from_bytes(data)?)), - 0x10 => Ok(RecvPatchPacket::FileInfoListEnd(FileInfoListEnd::from_bytes(data)?)), - _ => Err(PacketParseError::WrongPacketForServerType(u16::from_le_bytes([data[2], data[3]]), data.to_vec())) - } - } -} - -#[derive(Debug)] -pub enum SendPatchPacket { - ChangeDirectory(ChangeDirectory), - EndFileSend(EndFileSend), - FileInfo(FileInfo), - FileSend(Box), - FilesToPatchMetadata(FilesToPatchMetadata), - FinalizePatching(FinalizePatching), - Message(Message), - PatchEndList(PatchEndList), - PatchStartList(PatchStartList), - PatchWelcome(PatchWelcome), - RequestLogin(RequestLogin), - StartFileSend(StartFileSend), - UpOneDirectory(UpOneDirectory), -} - -impl SendServerPacket for SendPatchPacket { - fn as_bytes(&self) -> Vec { - match self { - SendPatchPacket::ChangeDirectory(pkt) => pkt.as_bytes(), - SendPatchPacket::EndFileSend(pkt) => pkt.as_bytes(), - SendPatchPacket::FileInfo(pkt) => pkt.as_bytes(), - SendPatchPacket::FileSend(pkt) => pkt.as_bytes(), - SendPatchPacket::FilesToPatchMetadata(pkt) => pkt.as_bytes(), - SendPatchPacket::FinalizePatching(pkt) => pkt.as_bytes(), - SendPatchPacket::Message(pkt) => pkt.as_bytes(), - SendPatchPacket::PatchEndList(pkt) => pkt.as_bytes(), - SendPatchPacket::PatchStartList(pkt) => pkt.as_bytes(), - SendPatchPacket::PatchWelcome(pkt) => pkt.as_bytes(), - SendPatchPacket::RequestLogin(pkt) => pkt.as_bytes(), - SendPatchPacket::StartFileSend(pkt) => pkt.as_bytes(), - SendPatchPacket::UpOneDirectory(pkt) => pkt.as_bytes(), - } - } -} - - -#[derive(Clone)] -pub struct PatchServerState { - patch_file_tree: PatchFileTree, - patch_file_lookup: HashMap, - patch_file_info: Vec, - patch_motd: String, -} - -impl PatchServerState { - pub fn new(patch_file_tree: PatchFileTree, patch_file_lookup: HashMap, patch_motd: String) -> PatchServerState { - PatchServerState { - patch_file_tree, - patch_file_lookup, - patch_file_info: Vec::new(), - patch_motd, - } - } -} - -#[async_trait::async_trait] -impl ServerState for PatchServerState { - type SendPacket = SendPatchPacket; - type RecvPacket = RecvPatchPacket; - type Cipher = PSOPCCipher; - type PacketError = PatchError; - - async fn on_connect(&mut self, _id: ClientId) -> Result>, PatchError> { - let mut rng = rand::thread_rng(); - let key_in: u32 = rng.gen(); - let key_out: u32 = rng.gen(); - - Ok(vec![OnConnect::Packet(SendPatchPacket::PatchWelcome(PatchWelcome::new(key_out, key_in))), - OnConnect::Cipher(PSOPCCipher::new(key_in), PSOPCCipher::new(key_out)) - ]) - } - - async fn handle(&mut self, id: ClientId, pkt: RecvPatchPacket) -> Result, PatchError> { - Ok(match pkt { - RecvPatchPacket::PatchWelcomeReply(_pkt) => { - vec![SendPatchPacket::RequestLogin(RequestLogin {})] - .into_iter() - .map(move |pkt| (id, pkt)) - .collect() - }, - RecvPatchPacket::LoginReply(_pkt) => { - let mut pkts = vec![SendPatchPacket::Message(Message::new(self.patch_motd.clone()))]; - pkts.append(&mut get_file_list_packets(&self.patch_file_tree)); - pkts.push(SendPatchPacket::PatchEndList(PatchEndList {})); - pkts - .into_iter() - .map(move |pkt| (id, pkt)) - .collect() - }, - RecvPatchPacket::FileInfoReply(pkt) => { - self.patch_file_info.push(pkt); - Vec::new() - }, - RecvPatchPacket::FileInfoListEnd(_pkt) => { - let need_update = self.patch_file_info.iter() - .filter(|file_info| does_file_need_updating(file_info, &self.patch_file_lookup)) - .collect::>(); - - let total_size = need_update.iter().fold(0, |a, file_info| a + file_info.size); - let total_files = need_update.len() as u32; - - vec![SendPatchPacket::FilesToPatchMetadata(FilesToPatchMetadata::new(total_size, total_files)), - SendPatchPacket::PatchStartList(PatchStartList {})] - .into_iter() - .chain(SendFileIterator::new(self)) - .map(move |pkt| (id, pkt)) - .collect() - } - }) - } - - async fn on_disconnect(&mut self, _id: ClientId) -> Result, PatchError> { - Ok(Vec::new()) - } -} - -fn load_patch_dir(basedir: &str, patchbase: &str, file_ids: &mut HashMap) -> PatchFileTree { - let paths = fs::read_dir(basedir).expect("could not read directory"); - - let mut files = Vec::new(); - let mut dirs = Vec::new(); - for p in paths { - let path = p.expect("not a real path").path(); - let patch_path = path.strip_prefix(basedir).unwrap(); - if path.is_dir() { - dirs.push(load_patch_dir(path.to_str().unwrap(), patch_path.to_str().unwrap(), file_ids)); - } - else { - files.push(PatchFileTree::File(patch_path.to_path_buf(), file_ids.len() as u32)); - let (checksum, size) = get_checksum_and_size(&path).unwrap(); - file_ids.insert(file_ids.len() as u32, PatchFile { - path, - checksum, - size, - }); - } - } - - files.append(&mut dirs); - - PatchFileTree::Directory(PathBuf::from(patchbase), files) -} - -pub fn generate_patch_tree(basedir: &str) -> (PatchFileTree, HashMap) { - let mut file_ids = HashMap::new(); - - let patch_tree = load_patch_dir(basedir, "", &mut file_ids); - - (patch_tree, file_ids) -} - - -fn get_file_list_packets(patch_file_tree: &PatchFileTree) -> Vec { - let mut pkts = Vec::new(); - - for item in patch_file_tree.flatten() { - match item { - PatchTreeIterItem::Directory(path) => { - pkts.push(SendPatchPacket::ChangeDirectory(ChangeDirectory::new(path.to_str().unwrap()))); - }, - PatchTreeIterItem::File(path, id) => { - pkts.push(SendPatchPacket::FileInfo(FileInfo::new(path.to_str().unwrap(), id))); - }, - PatchTreeIterItem::UpDirectory => { - pkts.push(SendPatchPacket::UpOneDirectory(UpOneDirectory {})); - } - } - } - - pkts -} - -fn get_checksum_and_size(path: &Path) -> Result<(u32, u32), PatchError> { - let file = fs::File::open(path)?; - let size = file.metadata().unwrap().len(); - let mut crc = crc32::Digest::new(crc32::IEEE); - let mut buf = [0u8; 1024 * 32]; - let mut reader = io::BufReader::new(file); - while let Ok(len) = reader.read(&mut buf) { - if len == 0 { - break; - } - crc.write(&buf[0..len]); - } - - Ok((crc.sum32(), size as u32)) -} - -fn does_file_need_updating(file_info: &FileInfoReply, patch_file_lookup: &HashMap) -> bool { - let patch_file = patch_file_lookup.get(&file_info.id).unwrap(); - patch_file.checksum != file_info.checksum || patch_file.size != file_info.size -} - - -struct SendFileIterator { - done: bool, - file_iter: Box + Send>, - patch_file_lookup: HashMap, - current_file: Option>, - chunk_num: u32, -} - -impl SendFileIterator { - fn new(state: &PatchServerState) -> SendFileIterator { - let file_ids_to_update = state.patch_file_info.iter() - .filter(|file_info| does_file_need_updating(file_info, &state.patch_file_lookup)) - .map(|k| k.id) - .collect::>(); - - SendFileIterator { - done: false, - patch_file_lookup: state.patch_file_lookup.clone(), - file_iter: Box::new(state.patch_file_tree.flatten().into_iter().filter(move |file| { - match file { - PatchTreeIterItem::File(_path, id) => { - file_ids_to_update.contains(id) - }, - _ => true, - } - })), - current_file: None, - chunk_num: 0, - } - } -} - -impl Iterator for SendFileIterator { - type Item = SendPatchPacket; - - fn next(&mut self) -> Option { - if self.done { - return None; - } - - match self.current_file { - Some(ref mut file) => { - let mut buf = [0u8; PATCH_FILE_CHUNK_SIZE as usize]; - let len = file.read(&mut buf).unwrap(); - if len == 0 { - self.current_file = None; - self.chunk_num = 0; - Some(SendPatchPacket::EndFileSend(EndFileSend::new())) - } - else { - let mut crc = crc32::Digest::new(crc32::IEEE); - crc.write(&buf[0..len]); - let pkt = SendPatchPacket::FileSend(Box::new(FileSend { - chunk_num: self.chunk_num, - checksum: crc.sum32(), - chunk_size: len as u32, - buffer: buf, - })); - self.chunk_num += 1; - Some(pkt) - } - }, - None => { - match self.file_iter.next() { - Some(next_file) => { - match next_file { - PatchTreeIterItem::Directory(path) => { - Some(SendPatchPacket::ChangeDirectory(ChangeDirectory::new(path.to_str().unwrap()))) - }, - PatchTreeIterItem::File(path, id) => { - let patch_file = self.patch_file_lookup.get(&id).unwrap(); - let file = fs::File::open(&patch_file.path).unwrap(); - let size = file.metadata().unwrap().len(); - self.current_file = Some(io::BufReader::new(file)); - Some(SendPatchPacket::StartFileSend(StartFileSend::new(path.to_str().unwrap(), size as u32, id))) - }, - PatchTreeIterItem::UpDirectory => { - Some(SendPatchPacket::UpOneDirectory(UpOneDirectory {})) - }, - } - }, - None => { - self.current_file = None; - self.done = true; - Some(SendPatchPacket::FinalizePatching(FinalizePatching {})) - } - } - } - } - } -} - -#[derive(Debug, Deserialize)] -pub struct PatchConfig { - pub path: String, - pub ip: String, // TODO: this does nothing - pub port: u16, -} - -pub fn load_config() -> PatchConfig { - let ini_file = match fs::File::open(std::path::Path::new("patch.ron")) { - Err(err) => panic!("Failed to open patch.ron config file. \n{err}"), - Ok(ini_file) => ini_file, - }; - - let mut s = String::new(); - if let Err(err) = (&ini_file).read_to_string(&mut s) { - panic!("Failed to read patch.ron config file. \n{err}"); - } - - let config: PatchConfig = match from_str(s.as_str()) { - Ok(config) => config, - Err(err) => panic!("Failed to load values from patch.ron \n{err}"), - }; - config -} - -pub fn load_config_env() -> PatchConfig { - let patch_path = std::env::var("PATCHFILE_DIR").unwrap(); - let patch_port = std::env::var("PATCH_PORT").unwrap().parse().unwrap(); - - PatchConfig { - path: patch_path, - ip: "127.0.0.1".into(), - port: patch_port, - } -} - -pub fn load_motd() -> String { - if let Ok(m) = fs::read_to_string("patch.motd") { - m - } - else { - "Welcome to Elseware!".to_string() - } -} diff --git a/src/ship/chatcommand.rs b/src/ship/chatcommand.rs index 97d5054..7799e17 100644 --- a/src/ship/chatcommand.rs +++ b/src/ship/chatcommand.rs @@ -1,11 +1,11 @@ use libpso::packet::ship::PlayerChat; -use crate::entity::gateway::EntityGateway; -use crate::common::serverstate::ClientId; +use entity::gateway::EntityGateway; +use networking::serverstate::ClientId; use crate::ship::ship::{ShipServerState, SendShipPacket}; -use crate::ship::client::Clients; -use crate::ship::items::state::ItemState; -use crate::entity::item::{BankName, BankIdentifier}; -use crate::ship::packet::builder::message::bank_item_list; +use client::Clients; +use items::state::ItemState; +use entity::item::{BankName, BankIdentifier}; +use pktbuilder::message::bank_item_list; async fn default_bank<'a, EG>(id: ClientId, entity_gateway: &mut EG, diff --git a/src/ship/drops/drop_table.rs b/src/ship/drops/drop_table.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/ship/map/mod.rs b/src/ship/map/mod.rs deleted file mode 100644 index 693fd46..0000000 --- a/src/ship/map/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -pub mod area; -pub mod enemy; -pub mod object; -pub mod variant; -pub mod maps; - -// TODO: don't just forward everything to the module scope -pub use area::*; -pub use enemy::*; -pub use object::*; -pub use variant::*; -pub use maps::*; diff --git a/src/ship/mod.rs b/src/ship/mod.rs index c58edb6..cf2a804 100644 --- a/src/ship/mod.rs +++ b/src/ship/mod.rs @@ -1,16 +1,4 @@ #[allow(clippy::module_inception)] pub mod ship; -pub mod location; -pub mod character; -pub mod client; -pub mod room; -pub mod items; -pub mod item_stats; -pub mod map; -pub mod monster; -pub mod drops; pub mod packet; -pub mod quests; -pub mod shops; -pub mod trade; pub mod chatcommand; diff --git a/src/ship/packet/handler/auth.rs b/src/ship/packet/handler/auth.rs index 698de75..915da68 100644 --- a/src/ship/packet/handler/auth.rs +++ b/src/ship/packet/handler/auth.rs @@ -1,11 +1,11 @@ use libpso::packet::login::{Login, LoginResponse, AccountStatus, Session}; use libpso::packet::ship::*; -use crate::common::serverstate::ClientId; +use networking::serverstate::ClientId; use crate::ship::ship::{SendShipPacket, ShipError, ClientState, Clients}; use crate::login::login::get_login_status; -use crate::entity::gateway::EntityGateway; -use crate::ship::items::state::ItemState; -use crate::common::interserver::ShipMessage; +use entity::gateway::EntityGateway; +use items::state::ItemState; +use networking::interserver::ShipMessage; #[allow(clippy::too_many_arguments)] pub async fn validate_login(id: ClientId, diff --git a/src/ship/packet/handler/communication.rs b/src/ship/packet/handler/communication.rs index c0ac3e5..3982efc 100644 --- a/src/ship/packet/handler/communication.rs +++ b/src/ship/packet/handler/communication.rs @@ -1,8 +1,8 @@ use libpso::packet::ship::*; -use crate::common::serverstate::ClientId; +use networking::serverstate::ClientId; use crate::ship::ship::{SendShipPacket, Clients}; -use crate::ship::location::{ClientLocation}; -use crate::entity::gateway::EntityGateway; +use location::{ClientLocation}; +use entity::gateway::EntityGateway; use futures::future::join_all; diff --git a/src/ship/packet/handler/direct_message.rs b/src/ship/packet/handler/direct_message.rs index 3c914e8..c93ae0d 100644 --- a/src/ship/packet/handler/direct_message.rs +++ b/src/ship/packet/handler/direct_message.rs @@ -1,24 +1,25 @@ use log::warn; +use async_std::sync::Arc; use rand::Rng; use rand::seq::SliceRandom; use libpso::packet::ship::*; use libpso::packet::messages::*; -use crate::common::leveltable::LEVEL_TABLE; -use crate::common::serverstate::ClientId; -use crate::ship::ship::{SendShipPacket, ShipError, Clients, ItemShops}; -use crate::ship::location::ClientLocation; -use crate::ship::drops::ItemDrop; -use crate::ship::room::Rooms; -use crate::ship::items::ClientItemId; -use crate::entity::gateway::EntityGateway; -use crate::entity::item; +use stats::leveltable::LEVEL_TABLE; +use networking::serverstate::ClientId; +use crate::ship::ship::{SendShipPacket, ShipError, Clients}; +use location::ClientLocation; +use drops::ItemDrop; +use room::Rooms; +use items::ClientItemId; +use entity::gateway::EntityGateway; +use entity::item; use libpso::utf8_to_utf16_array; -use crate::ship::packet::builder; -use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem}; -use crate::ship::items::state::{ItemState, ItemStateError}; -use crate::ship::items::floor::{FloorType, FloorItemDetail}; -use crate::ship::items::actions::TriggerCreateItem; -use crate::ship::items::tasks::{pick_up_item, withdraw_meseta, deposit_meseta, withdraw_item, deposit_item, buy_shop_item, enemy_drops_item, box_drops_item, take_meseta, apply_modifier}; +use pktbuilder as builder; +use shops::{ItemShops, ShopItem, ToolShopItem, ArmorShopItem}; +use items::state::{ItemState, ItemStateError}; +use items::floor::{FloorType, FloorItemDetail}; +use items::actions::TriggerCreateItem; +use items::tasks::{pick_up_item, withdraw_meseta, deposit_meseta, withdraw_item, deposit_item, buy_shop_item, enemy_drops_item, box_drops_item, take_meseta, apply_modifier}; const BANK_ACTION_DEPOSIT: u8 = 0; const BANK_ACTION_WITHDRAW: u8 = 1; @@ -122,7 +123,7 @@ where })).await?; let floor_item = enemy_drops_item(item_state, entity_gateway, character_id, room_entity_id, monster.monster, item_drop).await?; - let item_drop_msg = builder::message::item_drop(request_item.client, request_item.target, &floor_item)?; + let item_drop_msg = builder::message::item_drop(request_item.client, request_item.target, &floor_item); item_drop_packets.push((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg))))); } @@ -149,10 +150,10 @@ where let mut item_state = item_state.clone(); Box::pin(async move { let (item, floor_type) = item_state.get_floor_item(&client.character.id, &ClientItemId(pickup_item.item_id)).await?; - let remove_item = builder::message::remove_item_from_floor(area_client, &item)?; + let remove_item = builder::message::remove_item_from_floor(area_client, &item); let create_item = match &item.item { - FloorItemDetail::Individual(individual_floor_item) => Some(builder::message::create_individual_item(area_client, item.item_id, individual_floor_item)?), - FloorItemDetail::Stacked(stacked_floor_item) => Some(builder::message::create_stacked_item(area_client, item.item_id, &stacked_floor_item.tool, stacked_floor_item.count())?), + FloorItemDetail::Individual(individual_floor_item) => Some(builder::message::create_individual_item(area_client, item.item_id, individual_floor_item)), + FloorItemDetail::Stacked(stacked_floor_item) => Some(builder::message::create_stacked_item(area_client, item.item_id, &stacked_floor_item.tool, stacked_floor_item.count())), FloorItemDetail::Meseta(_) => None, }; @@ -234,7 +235,7 @@ where })).await?; let floor_item = box_drops_item(item_state, entity_gateway, character_id, room_entity_id, item_drop).await?; //let floor_item = enemy_drops_item(item_state, &mut entity_gateway, client.character.id, item_drop).await?; - let item_drop_msg = builder::message::item_drop(box_drop_request.client, box_drop_request.target, &floor_item)?; + let item_drop_msg = builder::message::item_drop(box_drop_request.client, box_drop_request.target, &floor_item); item_drop_packets.push((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg))))) } @@ -293,7 +294,7 @@ where } else { let item_added_to_inventory = withdraw_item(&mut item_state, &mut entity_gateway, &client.character, &ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as u32).await?; - let item_created = builder::message::create_withdrawn_inventory_item2(area_client, &item_added_to_inventory)?; + let item_created = builder::message::create_withdrawn_inventory_item2(area_client, &item_added_to_inventory); vec![SendShipPacket::Message(Message::new(GameMessage::CreateItem(item_created)))] } }, @@ -320,45 +321,30 @@ pub async fn shop_request(id: ClientId, client_location: &ClientLocation, clients: &Clients, rooms: &Rooms, - shops: &ItemShops) + shops: &Arc>) -> Result, anyhow::Error> { - //let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; let room_id = client_location.get_room(id).await?; - /* - let room = rooms.get(room_id.0) - .ok_or(ShipError::InvalidRoom(room_id.0 as u32))? - .as_ref() - .ok_or(ShipError::InvalidRoom(room_id.0 as u32))?; - */ let difficulty = rooms.with(room_id, |room| Box::pin(async move { room.mode.difficulty() })).await?; let shop_list = clients.with_mut(id, |client| { - let mut shops = shops.clone(); + let shops = shops.clone(); Box::pin(async move { let level = LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp) as usize; match shop_request.shop_type { SHOP_OPTION_WEAPON => { - client.weapon_shop = shops.weapon_shop.get_mut(&(difficulty, client.character.section_id)) - .ok_or(ShipError::ShopError)? - .lock() + client.weapon_shop = shops.generate_weapon_list(difficulty, client.character.section_id, level) .await - .generate_weapon_list(level); + .ok_or(ShipError::ShopError)?; Ok(builder::message::shop_list(shop_request.shop_type, &client.weapon_shop)) }, SHOP_OPTION_TOOL => { - client.tool_shop = shops.tool_shop - .lock() - .await - .generate_tool_list(level); + client.tool_shop = shops.generate_tool_list(level).await; Ok(builder::message::shop_list(shop_request.shop_type, &client.tool_shop)) }, SHOP_OPTION_ARMOR => { - client.armor_shop = shops.armor_shop - .lock() - .await - .generate_armor_list(level); + client.armor_shop = shops.generate_armor_list(level).await; Ok(builder::message::shop_list(shop_request.shop_type, &client.armor_shop)) }, _ => { @@ -440,7 +426,7 @@ where _ => {} } } - Ok::<_, anyhow::Error>(builder::message::create_withdrawn_inventory_item(area_client, &inventory_item)?) + Ok::<_, anyhow::Error>(builder::message::create_withdrawn_inventory_item(area_client, &inventory_item)) })}).await??; let other_clients_in_area = client_location.get_client_neighbors(id).await?; @@ -503,7 +489,7 @@ where }); take_meseta(&mut item_state, &mut entity_gateway, &client.character.id, item::Meseta(100)).await?; - Ok::<_, anyhow::Error>(builder::message::tek_preview(ClientItemId(tek_request.item_id), &weapon)?) + Ok::<_, anyhow::Error>(builder::message::tek_preview(ClientItemId(tek_request.item_id), &weapon)) })}).await??; @@ -539,7 +525,7 @@ where }; let weapon = apply_modifier(&mut item_state, &mut entity_gateway, &client.character, item_id, item::ItemModifier::WeaponModifier(modifier)).await?; - let create_item_pkt = builder::message::create_individual_item(area_client, item_id, &weapon)?; + let create_item_pkt = builder::message::create_individual_item(area_client, item_id, &weapon); Ok(neighbors.into_iter() .map(move |c| { diff --git a/src/ship/packet/handler/lobby.rs b/src/ship/packet/handler/lobby.rs index d5506c4..3033091 100644 --- a/src/ship/packet/handler/lobby.rs +++ b/src/ship/packet/handler/lobby.rs @@ -1,15 +1,17 @@ use libpso::packet::ship::*; -use crate::common::serverstate::ClientId; -use crate::common::leveltable::LEVEL_TABLE; -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}; -use crate::ship::packet; -use crate::ship::items::state::ItemState; -use crate::entity::gateway::EntityGateway; -use crate::entity::room::RoomNote; -use crate::ship::map::MapArea; +use networking::serverstate::ClientId; +use stats::leveltable::LEVEL_TABLE; +use crate::ship::ship::{SendShipPacket, ShipError}; +use maps::Holiday; +use client::Clients; +use room::Rooms; +use pktbuilder::character::FullCharacterBytesBuilder; +use location::{ClientLocation, LobbyId, RoomLobby, ClientLocationError, RoomId}; +//use pktbuilder; +use items::state::ItemState; +use entity::gateway::EntityGateway; +use entity::room::RoomNote; +use maps::area::MapArea; use futures::future::join_all; // this function needs a better home @@ -57,11 +59,11 @@ pub async fn send_player_to_lobby(id: ClientId, client_location: &mut ClientLocation, clients: &Clients, item_state: &ItemState, - event: ShipEvent) + event: Holiday) -> Result, anyhow::Error> { let lobby = client_location.add_client_to_next_available_lobby(id, LobbyId(0)).await.map_err(|_| ShipError::TooManyClients)?; - let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_state, event).await?; - let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state, event).await?; + let join_lobby = pktbuilder::lobby::join_lobby(id, lobby, client_location, clients, item_state, event).await?; + let addto = pktbuilder::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() @@ -77,7 +79,7 @@ pub async fn change_lobby(id: ClientId, item_state: &mut ItemState, rooms: &Rooms, entity_gateway: &mut EG, - event: ShipEvent) + event: Holiday) -> Result, anyhow::Error> where EG: EntityGateway + Clone + 'static, @@ -111,7 +113,7 @@ where })}).await??; }, } - let leave_lobby = packet::builder::lobby::remove_from_lobby(id, client_location).await?; + let leave_lobby = pktbuilder::lobby::remove_from_lobby(id, client_location).await?; let old_neighbors = client_location.get_client_neighbors(id).await.unwrap(); let mut lobby = LobbyId(requested_lobby as usize); if client_location.add_client_to_lobby(id, lobby).await.is_err() { @@ -131,8 +133,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, event).await?; - let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state, event).await?; + let join_lobby = pktbuilder::lobby::join_lobby(id, lobby, client_location, clients, item_state, event).await?; + let addto = pktbuilder::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() diff --git a/src/ship/packet/handler/message.rs b/src/ship/packet/handler/message.rs index f380d14..ca731c9 100644 --- a/src/ship/packet/handler/message.rs +++ b/src/ship/packet/handler/message.rs @@ -1,16 +1,17 @@ use libpso::packet::ship::*; use libpso::packet::messages::*; -use crate::entity::gateway::EntityGateway; -use crate::entity::item::Meseta; -use crate::common::serverstate::ClientId; -use crate::common::leveltable::LEVEL_TABLE; -use crate::ship::ship::{SendShipPacket, ShipError, Clients, ItemDropLocation}; -use crate::ship::room::Rooms; -use crate::ship::location::{ClientLocation, ClientLocationError}; -use crate::ship::items::ClientItemId; -use crate::ship::packet::builder; -use crate::ship::items::state::ItemState; -use crate::ship::items::tasks::{drop_item, drop_partial_item, drop_meseta, equip_item, unequip_item, sort_inventory, use_item, feed_mag, sell_item, take_meseta, floor_item_limit_reached}; +use entity::gateway::EntityGateway; +use entity::item::Meseta; +use networking::serverstate::ClientId; +use stats::leveltable::LEVEL_TABLE; +use crate::ship::ship::{SendShipPacket, ShipError}; +use client::{Clients, ItemDropLocation}; +use ::room::Rooms; +use location::{ClientLocation, ClientLocationError}; +use items::ClientItemId; +use pktbuilder as builder; +use items::state::ItemState; +use items::tasks::{drop_item, drop_partial_item, drop_meseta, equip_item, unequip_item, sort_inventory, use_item, feed_mag, sell_item, take_meseta, floor_item_limit_reached}; pub async fn request_exp(id: ClientId, request_exp: RequestExp, @@ -162,7 +163,7 @@ where drop_meseta(&mut item_state, &mut entity_gateway, &client.character, drop_location.map_area, (drop_location.x, drop_location.z), no_longer_has_item.amount).await })}).await??; - let dropped_meseta_pkt = builder::message::drop_split_meseta_stack(area_client, &dropped_meseta)?; + let dropped_meseta_pkt = builder::message::drop_split_meseta_stack(area_client, &dropped_meseta); let no_longer_has_meseta_pkt = builder::message::player_no_longer_has_meseta(area_client, no_longer_has_item.amount); let clients_in_area = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?; @@ -198,7 +199,7 @@ where no_longer_has_item.amount) .await })}).await??; - let dropped_item_pkt = builder::message::drop_split_stack(area_client, &dropped_item)?; + let dropped_item_pkt = builder::message::drop_split_stack(area_client, &dropped_item); let clients_in_area = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?; Ok(clients_in_area.into_iter() @@ -348,6 +349,15 @@ where })}).await?? .into_iter() .flat_map(move |pkt| { + let pkt = match pkt { + items::actions::CreateItem::Individual(area_client, item_id, item_detail) => { + builder::message::create_individual_item(area_client, item_id, &item_detail) + }, + items::actions::CreateItem::Stacked(area_client, item_id, tool, amount) => { + builder::message::create_stacked_item(area_client, item_id, &tool, amount) + } + }; + let pkt = SendShipPacket::Message(Message::new(GameMessage::CreateItem(pkt))); let player_use_tool = player_use_tool.clone(); neighbors.clone().map(move |client| { vec![(client.client, SendShipPacket::Message(Message::new(GameMessage::PlayerUseItem(player_use_tool.clone())))), (client.client, pkt.clone())] diff --git a/src/ship/packet/handler/quest.rs b/src/ship/packet/handler/quest.rs index 1c8c31e..6193530 100644 --- a/src/ship/packet/handler/quest.rs +++ b/src/ship/packet/handler/quest.rs @@ -1,12 +1,14 @@ 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, ShipEvent}; -use crate::ship::room::Rooms; -use crate::ship::map::enemy::RareMonsterAppearTable; -use crate::ship::location::{ClientLocation}; -use crate::ship::packet::builder::quest; +use networking::serverstate::ClientId; +use crate::ship::ship::{SendShipPacket, ShipError}; +use client::Clients; +use maps::Holiday; +use room::Rooms; +use maps::enemy::RareMonsterAppearTable; +use location::{ClientLocation}; +use pktbuilder::quest; use libpso::util::array_to_utf8; enum QuestFileType { @@ -97,7 +99,7 @@ pub async fn player_chose_quest(id: ClientId, clients: &Clients, client_location: &ClientLocation, rooms: &Rooms, - event: ShipEvent) + event: Holiday) -> Result, anyhow::Error> { let room_id = client_location.get_room(id).await?; diff --git a/src/ship/packet/handler/room.rs b/src/ship/packet/handler/room.rs index 9198c63..563385d 100644 --- a/src/ship/packet/handler/room.rs +++ b/src/ship/packet/handler/room.rs @@ -1,22 +1,26 @@ -use std::convert::{TryFrom, Into}; +use std::convert::Into; use futures::stream::StreamExt; use async_std::sync::Arc; use libpso::packet::ship::*; use libpso::packet::messages::*; -use crate::common::serverstate::ClientId; -use crate::common::leveltable::LEVEL_TABLE; -use crate::entity::gateway::EntityGateway; -use crate::entity::character::SectionID; -use crate::entity::room::{NewRoomEntity, RoomEntityMode, RoomNote}; -use crate::ship::drops::DropTable; -use crate::ship::ship::{SendShipPacket, Clients, ShipEvent}; -use crate::ship::room::{Rooms, Episode, Difficulty, RoomState, RoomMode}; -use crate::ship::map::Maps; -use crate::ship::location::{ClientLocation, RoomId, RoomLobby, GetAreaError}; -use crate::ship::packet::builder; -use crate::ship::items::state::ItemState; +use networking::serverstate::ClientId; +use stats::leveltable::LEVEL_TABLE; +use entity::gateway::EntityGateway; +use entity::character::SectionID; +use entity::room::{NewRoomEntity, RoomEntityMode, RoomNote}; +use drops::DropTable; +use crate::ship::ship::SendShipPacket; +use client::Clients; +use room::{Rooms, RoomState, RoomCreationError}; +use maps::Holiday; +use maps::room::{Episode, Difficulty, RoomMode}; +use maps::maps::Maps; +use location::{ClientLocation, RoomId, RoomLobby, GetAreaError}; +use pktbuilder as builder; +use items::state::ItemState; +use quests::{QuestList, QuestLoadError}; #[allow(clippy::too_many_arguments)] pub async fn create_room(id: ClientId, @@ -26,9 +30,11 @@ pub async fn create_room(id: ClientId, clients: &Clients, item_state: &mut ItemState, rooms: &Rooms, - map_builder: Arc Maps + Send + Sync>>, - drop_table_builder: Arc DropTable + Send + Sync>>, - event: ShipEvent) + map_builder: Arc Maps + Send + Sync>>, + drop_table_builder: Arc Box + Send + Sync>>, + standard_quest_builder: Arc Result + Send + Sync>>, + government_quest_builder: Arc Result + Send + Sync>>, + event: Holiday) -> Result, anyhow::Error> where EG: EntityGateway + Clone + 'static, @@ -36,7 +42,8 @@ where let level = clients.with(id, |client| Box::pin(async move { LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp) })).await?; - let difficulty = Difficulty::try_from(create_room.difficulty)?; + let difficulty = create_room.difficulty.try_into() + .map_err(|()| RoomCreationError::InvalidDifficulty(create_room.difficulty))?; match difficulty { Difficulty::Ultimate if level < 80 => { return Ok(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 80 \nto create Ultimate rooms.".into())))]) @@ -64,8 +71,9 @@ where (0, 0, 1) => RoomEntityMode::Single, _ => RoomEntityMode::Multi, }; - let episode = create_room.episode.try_into()?; - let difficulty = create_room.difficulty.try_into()?; + let episode = create_room.episode.try_into() + .map_err(|()| RoomCreationError::InvalidEpisode(create_room.episode))?; + //let difficulty = create_room.difficulty.try_into()?; let room = clients.with(id, |client| { let mut item_state = item_state.clone(); @@ -86,7 +94,7 @@ where let mut room = RoomState::new(room_entity.id, mode, episode, difficulty, client.character.section_id, name, create_room.password, event, - map_builder, drop_table_builder)?; + map_builder, drop_table_builder, standard_quest_builder, government_quest_builder)?; room.bursting = true; Ok::<_, anyhow::Error>(room) })}).await??; @@ -132,7 +140,7 @@ pub async fn join_room(id: ClientId, clients: &Clients, item_state: &mut ItemState, rooms: &Rooms, - event: ShipEvent) + event: Holiday) -> Result, anyhow::Error> where EG: EntityGateway + Clone + 'static, diff --git a/src/ship/packet/handler/settings.rs b/src/ship/packet/handler/settings.rs index de7bfaa..4bd665d 100644 --- a/src/ship/packet/handler/settings.rs +++ b/src/ship/packet/handler/settings.rs @@ -1,7 +1,7 @@ use libpso::packet::ship::*; -use crate::common::serverstate::ClientId; +use networking::serverstate::ClientId; use crate::ship::ship::{SendShipPacket, Clients}; -use crate::entity::gateway::EntityGateway; +use entity::gateway::EntityGateway; pub async fn update_config(id: ClientId, update_config: UpdateConfig, diff --git a/src/ship/packet/handler/ship.rs b/src/ship/packet/handler/ship.rs index a63ebdf..762e73f 100644 --- a/src/ship/packet/handler/ship.rs +++ b/src/ship/packet/handler/ship.rs @@ -1,10 +1,10 @@ use async_std::sync::{Arc, RwLock}; use libpso::packet::ship::*; use libpso::packet::login::RedirectClient; -use crate::common::serverstate::ClientId; -use crate::common::interserver::Ship; +use networking::serverstate::ClientId; +use networking::interserver::Ship; use crate::ship::ship::{SendShipPacket, ShipError}; -use crate::ship::packet::builder; +use pktbuilder as builder; pub async fn ship_list(id: ClientId, ship_list: &Arc>>) -> Vec<(ClientId, SendShipPacket)> { let ship_list = ship_list diff --git a/src/ship/packet/handler/trade.rs b/src/ship/packet/handler/trade.rs index c1cb2d0..8da8f38 100644 --- a/src/ship/packet/handler/trade.rs +++ b/src/ship/packet/handler/trade.rs @@ -1,19 +1,19 @@ use std::convert::TryInto; use libpso::packet::ship::*; use libpso::packet::messages::*; -use crate::common::serverstate::ClientId; +use networking::serverstate::ClientId; use crate::ship::ship::{SendShipPacket, ShipError, Clients}; -use crate::ship::location::{ClientLocation}; -use crate::ship::items::ClientItemId; -use crate::ship::items::state::{ItemState, ItemStateError}; -use crate::ship::items::inventory::InventoryItemDetail; -use crate::ship::trade::{TradeItem, TradeState, TradeStatus}; -use crate::entity::gateway::EntityGateway; -use crate::ship::packet::builder; -use crate::ship::items::tasks::trade_items; -use crate::ship::location::{AreaClient, RoomId}; -use crate::entity::item::Meseta; -use crate::ship::trade::ClientTradeState; +use location::{ClientLocation}; +use items::ClientItemId; +use items::state::{ItemState, ItemStateError}; +use items::inventory::InventoryItemDetail; +use items::trade::TradeItem; +use entity::gateway::EntityGateway; +use pktbuilder as builder; +use items::tasks::trade_items; +use location::{AreaClient, RoomId}; +use entity::item::Meseta; +use trade::{ClientTradeState, TradeState, TradeStatus}; pub const MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFF01); pub const OTHER_MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFFFF); @@ -450,8 +450,8 @@ where enum TradeReady/*<'a>*/ { OnePlayer, BothPlayers(RoomId, - (AreaClient, crate::ship::trade::ClientTradeState), - (AreaClient, crate::ship::trade::ClientTradeState)), + (AreaClient, ClientTradeState), + (AreaClient, ClientTradeState)), //(AreaClient, &'a crate::ship::ship::ClientState, crate::ship::trade::ClientTradeState), //(AreaClient, &'a crate::ship::ship::ClientState, crate::ship::trade::ClientTradeState)), } @@ -527,10 +527,10 @@ where .map(|(client, item)| { match item.item { InventoryItemDetail::Individual(individual_item) => { - GameMessage::CreateItem(builder::message::create_individual_item(client, item.item_id, &individual_item).unwrap()) + GameMessage::CreateItem(builder::message::create_individual_item(client, item.item_id, &individual_item)) }, InventoryItemDetail::Stacked(stacked_item) => { - GameMessage::CreateItem(builder::message::create_stacked_item(client, item.item_id, &stacked_item.tool, stacked_item.count()).unwrap()) + GameMessage::CreateItem(builder::message::create_stacked_item(client, item.item_id, &stacked_item.tool, stacked_item.count())) } } }); diff --git a/src/ship/packet/mod.rs b/src/ship/packet/mod.rs index 5b46ce1..9b5f908 100644 --- a/src/ship/packet/mod.rs +++ b/src/ship/packet/mod.rs @@ -1,2 +1,2 @@ -pub mod builder; +//pub mod builder; pub mod handler; diff --git a/src/ship/ship.rs b/src/ship/ship.rs index cd40965..5eacf5c 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -1,9 +1,8 @@ #![allow(dead_code, unused_must_use)] use std::net::Ipv4Addr; -use std::collections::HashMap; use async_std::channel; -use async_std::sync::{Arc, Mutex, RwLock}; +use async_std::sync::{Arc, RwLock}; use rand::Rng; use thiserror::Error; @@ -14,80 +13,32 @@ use libpso::{PacketParseError, PSOPacket}; use libpso::crypto::bb::PSOBBCipher; use libpso::packet::ship::{BLOCK_MENU_ID, ROOM_MENU_ID}; -use crate::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY}; -use crate::common::serverstate::{SendServerPacket, RecvServerPacket, ServerState, OnConnect, ClientId}; -use crate::common::interserver::{AuthToken, Ship, ServerId, InterserverActor, LoginMessage, ShipMessage}; -use crate::login::character::SHIP_MENU_ID; -use crate::entity::gateway::{EntityGateway, GatewayError}; -use crate::entity::character::SectionID; -use crate::entity::room::RoomNote; -use crate::ship::location::{ClientLocation, RoomLobby, ClientLocationError, RoomId}; -use crate::ship::drops::DropTable; -use crate::ship::items; -use crate::ship::room; -use crate::ship::map::{Maps, MapsError, MapAreaError, generate_free_roam_maps}; +use networking::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY}; +use networking::serverstate::{SendServerPacket, RecvServerPacket, ServerState, OnConnect, ClientId}; +use networking::interserver::{AuthToken, Ship, ServerId, InterserverActor, LoginMessage, ShipMessage}; +use pktbuilder::ship::SHIP_MENU_ID; +use entity::gateway::{EntityGateway, GatewayError}; +use entity::character::SectionID; +use entity::room::RoomNote; +use location::{ClientLocation, RoomLobby, ClientLocationError, RoomId}; +use drops::{DropTable, StandardDropTable}; +use items; +use room; +use maps::room::{RoomMode, Episode, Difficulty}; +use quests::{load_standard_quests, load_government_quests}; +use quests::{QuestList, QuestLoadError}; +use maps::Holiday; +use maps::area::MapAreaError; +use maps::maps::{Maps, MapsError, generate_free_roam_maps}; use crate::ship::packet::handler; -use crate::ship::shops::{WeaponShop, ToolShop, ArmorShop}; -use crate::ship::trade::TradeState; +use shops::{ItemShops, StandardItemShops}; +use trade::TradeState; use crate::ship::chatcommand; +use pktbuilder::quest::{QUEST_CATEGORY_MENU_ID, QUEST_SELECT_MENU_ID}; -// TODO: remove once stuff settles down -pub use crate::ship::client::*; +pub use client::{Clients, ClientState}; 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)] @@ -139,7 +90,7 @@ pub enum ShipError { #[error("gateway error {0}")] GatewayError(#[from] GatewayError), #[error("unknown monster {0}")] - UnknownMonster(crate::ship::monster::MonsterType), + UnknownMonster(maps::monster::MonsterType), #[error("invalid ship {0}")] InvalidShip(usize), #[error("invalid block {0}")] @@ -149,7 +100,7 @@ pub enum ShipError { #[error("trade error {0}")] TradeError(#[from] crate::ship::packet::handler::trade::TradeError), #[error("trade state error {0}")] - TradeStateError(#[from] crate::ship::trade::TradeStateError), + TradeStateError(#[from] trade::TradeStateError), #[error("message error {0}")] MessageError(#[from] crate::ship::packet::handler::direct_message::MessageError), #[error("room creation error {0}")] @@ -167,6 +118,7 @@ impl> From for ShipError { */ + #[derive(Debug)] pub enum RecvShipPacket { Login(Login), @@ -341,34 +293,6 @@ impl SendServerPacket for SendShipPacket { } } -#[derive(Clone)] -pub struct ItemShops { - pub weapon_shop: HashMap<(room::Difficulty, SectionID), Arc>>>, - pub tool_shop: Arc>>, - pub armor_shop: Arc>>, -} - -impl Default for ItemShops { - fn default() -> ItemShops { - let difficulty = [room::Difficulty::Normal, room::Difficulty::Hard, room::Difficulty::VeryHard, room::Difficulty::Ultimate]; - let section_id = [SectionID::Viridia, SectionID::Greenill, SectionID::Skyly, SectionID::Bluefull, SectionID::Purplenum, - SectionID::Pinkal, SectionID::Redria, SectionID::Oran, SectionID::Yellowboze, SectionID::Whitill]; - - let mut weapon_shop = HashMap::new(); - for d in difficulty.iter() { - for id in section_id.iter() { - weapon_shop.insert((*d, *id), Arc::new(Mutex::new(WeaponShop::new(*d, *id)))); - } - } - - ItemShops { - weapon_shop, - tool_shop: Arc::new(Mutex::new(ToolShop::default())), - armor_shop: Arc::new(Mutex::new(ArmorShop::default())), - } - } -} - pub struct ShipServerStateBuilder { entity_gateway: Option, @@ -376,9 +300,12 @@ pub struct ShipServerStateBuilder { ip: Option, port: Option, auth_token: Option, - event: Option, - map_builder: Option Maps + Send + Sync>>, - drop_table_builder: Option DropTable + Send + Sync>>, + event: Option, + shops: Option>, + map_builder: Option Maps + Send + Sync>>, + drop_table_builder: Option Box + Send + Sync>>, + standard_quest_builder: Option Result + Send + Sync>>, + government_quest_builder: Option Result + Send + Sync>>, num_blocks: usize, } @@ -391,8 +318,11 @@ impl Default for ShipServerStateBuilder port: None, auth_token: None, event: None, + shops: None, map_builder: None, drop_table_builder: None, + standard_quest_builder: None, + government_quest_builder: None, num_blocks: 2, } } @@ -430,23 +360,41 @@ impl ShipServerStateBuilder { } #[must_use] - pub fn event(mut self, event: ShipEvent) -> ShipServerStateBuilder { + pub fn event(mut self, event: Holiday) -> ShipServerStateBuilder { self.event = Some(event); self } #[must_use] - pub fn map_builder(mut self, map_builder: Box Maps + Send + Sync>) -> ShipServerStateBuilder { + pub fn map_builder(mut self, map_builder: Box Maps + Send + Sync>) -> ShipServerStateBuilder { self.map_builder = Some(map_builder); self } #[must_use] - pub fn drop_table_builder(mut self, drop_table_builder: Box DropTable + Send + Sync>) -> ShipServerStateBuilder { + pub fn drop_table_builder(mut self, drop_table_builder: Box Box + Send + Sync>) -> ShipServerStateBuilder { self.drop_table_builder = Some(drop_table_builder); self } + #[must_use] + pub fn standard_quest_builder(mut self, standard_quest_builder: Box Result + Send + Sync>) -> ShipServerStateBuilder { + self.standard_quest_builder = Some(standard_quest_builder); + self + } + + #[must_use] + pub fn government_quest_builder(mut self, government_quest_builder: Box Result + Send + Sync>) -> ShipServerStateBuilder { + self.government_quest_builder = Some(government_quest_builder); + self + } + + #[must_use] + pub fn item_shops(mut self, item_shops: impl ItemShops + Send + Sync + 'static) -> ShipServerStateBuilder { + self.shops = Some(Box::new(item_shops)); + self + } + #[must_use] pub fn blocks(mut self, num_blocks: usize) -> ShipServerStateBuilder { self.num_blocks = num_blocks; @@ -462,11 +410,13 @@ impl ShipServerStateBuilder { item_state: items::state::ItemState::default(), ip: self.ip.unwrap_or_else(|| Ipv4Addr::new(127,0,0,1)), port: self.port.unwrap_or(SHIP_PORT), - shops: ItemShops::default(), + shops: Arc::new(self.shops.unwrap_or_else(|| Box::::default())), blocks: Blocks(blocks), - event: self.event.unwrap_or(ShipEvent::None), - map_builder: Arc::new(self.map_builder.unwrap_or(Box::new(generate_free_roam_maps))), - drop_table_builder: Arc::new(self.drop_table_builder.unwrap_or(Box::new(DropTable::new))), + event: self.event.unwrap_or(Holiday::None), + map_builder: Arc::new(self.map_builder.unwrap_or_else(|| Box::new(generate_free_roam_maps))), + drop_table_builder: Arc::new(self.drop_table_builder.unwrap_or_else(|| Box::new(StandardDropTable::new))), + standard_quest_builder: Arc::new(self.standard_quest_builder.unwrap_or_else(|| Box::new(load_standard_quests))), + government_quest_builder: Arc::new(self.government_quest_builder.unwrap_or_else(|| Box::new(load_government_quests))), auth_token: self.auth_token.unwrap_or_else(|| AuthToken("".into())), ship_list: Arc::new(RwLock::new(Vec::new())), @@ -503,9 +453,9 @@ pub struct ShipServerState { pub clients: Clients, name: String, pub(crate) item_state: items::state::ItemState, - shops: ItemShops, + shops: Arc>, pub blocks: Blocks, - event: ShipEvent, + event: Holiday, ip: Ipv4Addr, port: u16, @@ -514,8 +464,10 @@ pub struct ShipServerState { ship_list: Arc>>, shipgate_sender: Option>, trades: TradeState, - map_builder: Arc Maps + Send + Sync>>, - drop_table_builder: Arc DropTable + Send + Sync>>, + map_builder: Arc Maps + Send + Sync>>, + drop_table_builder: Arc Box + Send + Sync>>, + standard_quest_builder: Arc Result + Send + Sync>>, + government_quest_builder: Arc Result + Send + Sync>>, } impl ShipServerState { @@ -757,7 +709,8 @@ 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 self.entity_gateway, &mut block.client_location, &self.clients, &mut self.item_state, - &block.rooms, self.map_builder.clone(), self.drop_table_builder.clone(), self.event).await? + &block.rooms, self.map_builder.clone(), self.drop_table_builder.clone(), + self.standard_quest_builder.clone(), self.government_quest_builder.clone(), self.event).await? }, RecvShipPacket::RoomNameRequest(_req) => { let block = self.blocks.get_from_client(id, &self.clients).await?; diff --git a/src/ship/shops/mod.rs b/src/ship/shops/mod.rs deleted file mode 100644 index 573b667..0000000 --- a/src/ship/shops/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -mod weapon; -mod tool; -mod armor; - -use crate::entity::item::ItemDetail; - -pub trait ShopItem { - fn price(&self) -> usize; - fn as_bytes(&self) -> [u8; 12]; - fn as_item(&self) -> ItemDetail; -} - -pub enum ShopType { - Weapon, - Tool, - Armor -} - -pub use weapon::{WeaponShop, WeaponShopItem}; -pub use tool::{ToolShop, ToolShopItem}; -pub use armor::{ArmorShop, ArmorShopItem}; diff --git a/stats/Cargo.toml b/stats/Cargo.toml new file mode 100644 index 0000000..6cebc62 --- /dev/null +++ b/stats/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "stats" +version = "0.1.0" +edition = "2021" + +[dependencies] +entity = { workspace = true } +toml = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +lazy_static = { workspace = true } diff --git a/src/ship/item_stats.rs b/stats/src/items.rs similarity index 95% rename from src/ship/item_stats.rs rename to stats/src/items.rs index 919c076..13d0855 100644 --- a/src/ship/item_stats.rs +++ b/stats/src/items.rs @@ -4,13 +4,13 @@ use serde::{Serialize, Deserialize}; use std::fs::File; use std::io::Read; -use crate::entity::item::weapon::WeaponType; -use crate::entity::item::armor::ArmorType; -use crate::entity::item::shield::ShieldType; -use crate::entity::item::unit::UnitType; -use crate::entity::item::mag::MagType; -use crate::entity::item::tool::ToolType; -use crate::entity::item::tech::Technique; +use entity::item::weapon::WeaponType; +use entity::item::armor::ArmorType; +use entity::item::shield::ShieldType; +use entity::item::unit::UnitType; +use entity::item::mag::MagType; +use entity::item::tool::ToolType; +use entity::item::tech::Technique; lazy_static::lazy_static! { diff --git a/src/common/leveltable.rs b/stats/src/leveltable.rs similarity index 99% rename from src/common/leveltable.rs rename to stats/src/leveltable.rs index 9fb1465..7b06966 100644 --- a/src/common/leveltable.rs +++ b/stats/src/leveltable.rs @@ -1,6 +1,6 @@ use std::fs::File; use serde_json::Value; -use crate::entity::character::CharacterClass; +use entity::character::CharacterClass; use std::sync::LazyLock; pub static LEVEL_TABLE: LazyLock = LazyLock::new(CharacterLevelTable::default); diff --git a/stats/src/lib.rs b/stats/src/lib.rs new file mode 100644 index 0000000..0564e34 --- /dev/null +++ b/stats/src/lib.rs @@ -0,0 +1,4 @@ +#![feature(lazy_cell)] + +pub mod items; +pub mod leveltable; diff --git a/tests/common.rs b/tests/common.rs index e9dccd0..db91ab8 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -1,22 +1,83 @@ #![allow(dead_code)] -use elseware::common::serverstate::{ClientId, ServerState}; -use elseware::entity::gateway::EntityGateway; -use elseware::entity::account::{UserAccountEntity, NewUserAccountEntity, NewUserSettingsEntity}; -use elseware::entity::character::{CharacterEntity, NewCharacterEntity}; -use elseware::entity::item::{Meseta, BankName, BankIdentifier}; -use elseware::ship::ship::{ShipServerState, RecvShipPacket}; -use elseware::ship::room::Difficulty; - -use elseware::entity::item; +use networking::serverstate::{ClientId, ServerState}; +use entity::gateway::EntityGateway; +use entity::account::{UserAccountEntity, NewUserAccountEntity, NewUserSettingsEntity}; +use entity::character::{CharacterEntity, NewCharacterEntity, SectionID}; +use entity::item::{Meseta, BankIdentifier}; +use elseware::ship::ship::{ShipServerState, ShipServerStateBuilder, RecvShipPacket}; +use maps::room::{RoomMode, Difficulty, Episode}; +use maps::area::MapArea; +use maps::maps::null_free_roam_maps; +use maps::object::MapObject; +use maps::monster::MonsterType; +use quests::{QuestList, QuestLoadError}; +use drops::{DropTable, ItemDropType}; +use shops::{ItemShops, WeaponShopItem, ToolShopItem, ArmorShopItem}; + +use entity::item; use libpso::packet::ship::*; use libpso::packet::login::{Login, Session}; use libpso::{utf8_to_array, utf8_to_utf16_array}; +fn null_quest_builder(_mode: RoomMode) -> Result { + Ok(Default::default()) +} + +struct NullDropTable; + +impl DropTable for NullDropTable { + fn get_drop(&mut self, _map_area: &MapArea, _monster: &MonsterType) -> Option { + None + } + fn get_box_drop(&mut self, _map_area: &MapArea, _object: &MapObject) -> Option { + None + } +} + +pub fn null_drop_table_builder(_episode: Episode, _difficult: Difficulty, _section_id: SectionID) -> Box { + Box::new(NullDropTable) +} + +struct NullItemShops; + +#[async_trait::async_trait] +impl ItemShops for NullItemShops { + async fn generate_weapon_list(&self, _difficulty: Difficulty, _section_id: SectionID, _char_level: usize) -> Option> { + Some(Vec::new()) + } + async fn generate_tool_list(&self, _char_level: usize) -> Vec { + Vec::new() + } + async fn generate_armor_list(&self, _char_level: usize) -> Vec { + Vec::new() + } +} + +pub fn standard_ship_buildable(gateway: EG) -> ShipServerStateBuilder { + ShipServerState::builder() + .gateway(gateway) + .standard_quest_builder(Box::new(null_quest_builder)) + .government_quest_builder(Box::new(null_quest_builder)) + .drop_table_builder(Box::new(null_drop_table_builder)) + .map_builder(Box::new(null_free_roam_maps)) + .item_shops(NullItemShops) +} + +pub fn standard_ship(gateway: EG) -> ShipServerState { + ShipServerState::builder() + .gateway(gateway) + .standard_quest_builder(Box::new(null_quest_builder)) + .government_quest_builder(Box::new(null_quest_builder)) + .drop_table_builder(Box::new(null_drop_table_builder)) + .map_builder(Box::new(null_free_roam_maps)) + .item_shops(NullItemShops) + .build() +} //TODO: remove kb_conf_preset -pub async fn new_user_character(entity_gateway: &mut EG, username: &str, password: &str, kb_conf_preset: usize) -> (UserAccountEntity, CharacterEntity) { +pub async fn new_user_character(entity_gateway: &mut EG, username: &str, password: &str, _kb_conf_preset: usize) -> (UserAccountEntity, CharacterEntity) { let new_user = NewUserAccountEntity { email: format!("{}@pso.com", username), username: username.into(), diff --git a/tests/test_bank.rs b/tests/test_bank.rs index 71fc8f5..443da34 100644 --- a/tests/test_bank.rs +++ b/tests/test_bank.rs @@ -1,8 +1,9 @@ use std::collections::BTreeSet; -use elseware::common::serverstate::{ClientId, ServerState}; -use elseware::entity::gateway::{EntityGateway, InMemoryGateway}; -use elseware::entity::item; -use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket}; +use networking::serverstate::{ClientId, ServerState}; +use entity::gateway::{EntityGateway, InMemoryGateway}; +use entity::item; +use elseware::ship::ship::{RecvShipPacket, SendShipPacket}; +use shops::StandardItemShops; use libpso::packet::ship::*; use libpso::packet::messages::*; @@ -33,9 +34,7 @@ async fn test_bank_items_sent_in_character_login() { entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![item]), &item::BankIdentifier::Character).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; let packets = ship.handle(ClientId(1), RecvShipPacket::MenuSelect(MenuSelect { @@ -71,9 +70,7 @@ async fn test_request_bank_items() { entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), &item::BankIdentifier::Character).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -115,9 +112,7 @@ async fn test_request_stacked_bank_items() { entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), &item::BankIdentifier::Character).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -180,9 +175,7 @@ async fn test_request_bank_items_sorted() { let bank = vec![item::BankItemEntity::Individual(item1), vec![monomate].into(), item2.into()]; entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), &item::BankIdentifier::Character).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -237,9 +230,7 @@ async fn test_deposit_individual_item() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![item0, item1])).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -303,9 +294,7 @@ async fn test_deposit_stacked_item() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![monomates])).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -364,9 +353,7 @@ async fn test_deposit_partial_stacked_item() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![monomates])).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -445,9 +432,7 @@ async fn test_deposit_stacked_item_with_stack_already_in_bank() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![inventory_monomates])).await.unwrap(); entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), &item::BankIdentifier::Character).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -518,9 +503,7 @@ async fn test_deposit_stacked_item_with_full_stack_in_bank() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![inventory_monomates])).await.unwrap(); entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), &item::BankIdentifier::Character).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -596,9 +579,7 @@ async fn test_deposit_individual_item_in_full_bank() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap(); entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), &item::BankIdentifier::Character).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -668,9 +649,7 @@ async fn test_deposit_stacked_item_in_full_bank() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![monomates])).await.unwrap(); entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(full_bank), &item::BankIdentifier::Character).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -754,9 +733,7 @@ async fn test_deposit_stacked_item_in_full_bank_with_partial_stack() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![monomates])).await.unwrap(); entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(almost_full_bank), &item::BankIdentifier::Character).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -794,9 +771,7 @@ async fn test_deposit_meseta() { let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; entity_gateway.set_character_meseta(&char1.id, item::Meseta(300)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -831,9 +806,7 @@ async fn test_deposit_too_much_meseta() { entity_gateway.set_character_meseta(&char1.id, item::Meseta(300)).await.unwrap(); entity_gateway.set_bank_meseta(&char1.id, &item::BankIdentifier::Character, item::Meseta(999980)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -870,9 +843,7 @@ async fn test_deposit_meseta_when_bank_is_maxed() { entity_gateway.set_character_meseta(&char1.id, item::Meseta(300)).await.unwrap(); entity_gateway.set_bank_meseta(&char1.id, &item::BankIdentifier::Character, item::Meseta(999999)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -925,9 +896,7 @@ async fn test_withdraw_individual_item() { entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), &item::BankIdentifier::Character).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -985,9 +954,7 @@ async fn test_withdraw_stacked_item() { entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), &item::BankIdentifier::Character).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -1044,9 +1011,7 @@ async fn test_withdraw_partial_stacked_item() { } entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), &item::BankIdentifier::Character).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -1122,9 +1087,7 @@ async fn test_withdraw_stacked_item_with_stack_already_in_inventory() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![inventory_monomates])).await.unwrap(); entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), &item::BankIdentifier::Character).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -1197,9 +1160,7 @@ async fn test_withdraw_stacked_item_with_full_stack_in_inventory() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![inventory_monomates])).await.unwrap(); entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), &item::BankIdentifier::Character).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -1275,9 +1236,7 @@ async fn test_withdraw_individual_item_in_full_inventory() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap(); entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), &item::BankIdentifier::Character).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -1343,9 +1302,7 @@ async fn test_withdraw_stacked_item_in_full_inventory() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap(); entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), &item::BankIdentifier::Character).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -1430,9 +1387,7 @@ async fn test_withdraw_stacked_item_in_full_inventory_with_partial_stack() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(items)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -1473,9 +1428,7 @@ async fn test_withdraw_meseta() { let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; entity_gateway.set_bank_meseta(&char1.id, &item::BankIdentifier::Character, item::Meseta(300)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -1510,9 +1463,7 @@ async fn test_withdraw_too_much_meseta() { entity_gateway.set_character_meseta(&char1.id, item::Meseta(999980)).await.unwrap(); entity_gateway.set_bank_meseta(&char1.id, &item::BankIdentifier::Character, item::Meseta(300)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -1549,9 +1500,7 @@ async fn test_withdraw_meseta_inventory_is_maxed() { entity_gateway.set_character_meseta(&char1.id, item::Meseta(999999)).await.unwrap(); entity_gateway.set_bank_meseta(&char1.id, &item::BankIdentifier::Character, item::Meseta(300)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -1590,9 +1539,9 @@ async fn test_withdraw_meseta_and_buy_a_few_monomates_with_it() { entity_gateway.set_character_meseta(&char1.id, item::Meseta(100)).await.unwrap(); entity_gateway.set_bank_meseta(&char1.id, &item::BankIdentifier::Character, item::Meseta(300)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship_buildable(entity_gateway.clone()) + .item_shops(StandardItemShops::default()) + .build(); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -1649,9 +1598,7 @@ async fn test_deposit_items_into_shared_banks() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![item0, item1, item2, item3, item4, item5])).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -1765,9 +1712,7 @@ async fn test_deposit_meseta_into_shared_banks() { let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; entity_gateway.set_character_meseta(&char1.id, item::Meseta(300)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -1852,9 +1797,7 @@ async fn test_withdraw_items_from_shared_banks() { entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![item2, item3]), &item::BankIdentifier::Shared(item::BankName("asdf".into()))).await.unwrap(); entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![item4, item5]), &item::BankIdentifier::Shared(item::BankName("qwer".into()))).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -1956,9 +1899,7 @@ async fn test_withdraw_meseta_from_shared_banks() { entity_gateway.set_bank_meseta(&char1.id, &item::BankIdentifier::Shared(item::BankName("asdf".into())), item::Meseta(300)).await.unwrap(); entity_gateway.set_bank_meseta(&char1.id, &item::BankIdentifier::Shared(item::BankName("qwer".into())), item::Meseta(300)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; diff --git a/tests/test_character.rs b/tests/test_character.rs index ca16eac..0c8cecf 100644 --- a/tests/test_character.rs +++ b/tests/test_character.rs @@ -1,8 +1,8 @@ -use elseware::common::serverstate::{ClientId, ServerState}; -use elseware::entity::gateway::{EntityGateway, InMemoryGateway}; -use elseware::ship::ship::{ShipServerState, RecvShipPacket}; +use networking::serverstate::{ClientId, ServerState}; +use entity::gateway::{EntityGateway, InMemoryGateway}; +use elseware::ship::ship::RecvShipPacket; -use libpso::character::settings::{DEFAULT_KEYBOARD_CONFIG1, DEFAULT_KEYBOARD_CONFIG2, DEFAULT_KEYBOARD_CONFIG3, DEFAULT_KEYBOARD_CONFIG4}; +use libpso::character::settings::{DEFAULT_KEYBOARD_CONFIG1, DEFAULT_KEYBOARD_CONFIG4}; use libpso::packet::ship::*; #[path = "common.rs"] @@ -15,9 +15,7 @@ async fn test_save_options() { let (user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -35,11 +33,9 @@ async fn test_save_options() { async fn test_change_keyboard_mappings() { let mut entity_gateway = InMemoryGateway::default(); - let (user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 2).await; + let (user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 2).await; - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; diff --git a/tests/test_exp_gain.rs b/tests/test_exp_gain.rs index 0317e9d..ae49fce 100644 --- a/tests/test_exp_gain.rs +++ b/tests/test_exp_gain.rs @@ -1,13 +1,12 @@ -use elseware::common::serverstate::{ClientId, ServerState}; -use elseware::entity::gateway::{EntityGateway, InMemoryGateway}; -use elseware::common::leveltable::CharacterLevelTable; -use elseware::ship::ship::{ShipServerState, SendShipPacket, RecvShipPacket}; -use elseware::ship::monster::MonsterType; -use elseware::ship::location::RoomId; -use elseware::ship::map::variant::{MapVariant, MapVariantMode}; -use elseware::ship::map::maps::Maps; -use elseware::ship::map::area::MapArea; -use elseware::ship::map::enemy::MapEnemy; +use networking::serverstate::{ClientId, ServerState}; +use entity::gateway::{EntityGateway, InMemoryGateway}; +use stats::leveltable::CharacterLevelTable; +use elseware::ship::ship::{SendShipPacket, RecvShipPacket}; +use maps::variant::{MapVariant, MapVariantMode}; +use maps::maps::Maps; +use maps::area::MapArea; +use maps::enemy::MapEnemy; +use maps::monster::MonsterType; use libpso::packet::ship::*; use libpso::packet::messages::*; @@ -22,16 +21,15 @@ async fn test_character_gains_exp() { let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .map_builder(Box::new(|_room_mode, _event| { - Maps::new( - vec![MapVariant::new(MapArea::Forest2, MapVariantMode::Online)], - vec![Some(MapEnemy::new(MonsterType::Hildebear, MapArea::Forest2))], - Vec::new(), - ) - })) - .build()); + let mut ship = standard_ship_buildable(entity_gateway.clone()) + .map_builder(Box::new(|_room_mode, _event| { + Maps::new( + vec![MapVariant::new(MapArea::Forest2, MapVariantMode::Online)], + vec![Some(MapEnemy::new(MonsterType::Hildebear, MapArea::Forest2))], + Vec::new(), + ) + })) + .build(); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -58,16 +56,15 @@ async fn test_character_levels_up() { char1.exp = 49; entity_gateway.save_character(&char1).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .map_builder(Box::new(|_room_mode, _event| { - Maps::new( - vec![MapVariant::new(MapArea::Forest2, MapVariantMode::Online)], - vec![Some(MapEnemy::new(MonsterType::Hildebear, MapArea::Forest2))], - Vec::new(), - ) - })) - .build()); + let mut ship = standard_ship_buildable(entity_gateway.clone()) + .map_builder(Box::new(|_room_mode, _event| { + Maps::new( + vec![MapVariant::new(MapArea::Forest2, MapVariantMode::Online)], + vec![Some(MapEnemy::new(MonsterType::Hildebear, MapArea::Forest2))], + Vec::new(), + ) + })) + .build(); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -95,16 +92,15 @@ async fn test_character_levels_up_multiple_times() { let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .map_builder(Box::new(|_room_mode, _event| { - Maps::new( - vec![MapVariant::new(MapArea::DarkFalz, MapVariantMode::Online)], - vec![Some(MapEnemy::new(MonsterType::DarkFalz2, MapArea::DarkFalz))], - Vec::new(), - ) - })) - .build()); + let mut ship = standard_ship_buildable(entity_gateway.clone()) + .map_builder(Box::new(|_room_mode, _event| { + Maps::new( + vec![MapVariant::new(MapArea::DarkFalz, MapVariantMode::Online)], + vec![Some(MapEnemy::new(MonsterType::DarkFalz2, MapArea::DarkFalz))], + Vec::new(), + ) + })) + .build(); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -132,16 +128,15 @@ async fn test_one_character_gets_full_exp_and_other_attacker_gets_partial() { let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .map_builder(Box::new(|_room_mode, _event| { - Maps::new( - vec![MapVariant::new(MapArea::Forest2, MapVariantMode::Online)], - vec![Some(MapEnemy::new(MonsterType::Hildebear, MapArea::Forest2))], - Vec::new(), - ) - })) - .build()); + let mut ship = standard_ship_buildable(entity_gateway.clone()) + .map_builder(Box::new(|_room_mode, _event| { + Maps::new( + vec![MapVariant::new(MapArea::Forest2, MapVariantMode::Online)], + vec![Some(MapEnemy::new(MonsterType::Hildebear, MapArea::Forest2))], + Vec::new(), + ) + })) + .build(); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; diff --git a/tests/test_item_actions.rs b/tests/test_item_actions.rs index 77d52bc..758aa10 100644 --- a/tests/test_item_actions.rs +++ b/tests/test_item_actions.rs @@ -1,7 +1,7 @@ -use elseware::common::serverstate::{ClientId, ServerState}; -use elseware::entity::gateway::{EntityGateway, InMemoryGateway}; -use elseware::ship::ship::{ShipServerState, RecvShipPacket}; -use elseware::entity::item; +use networking::serverstate::{ClientId, ServerState}; +use entity::gateway::{EntityGateway, InMemoryGateway}; +use elseware::ship::ship::RecvShipPacket; +use entity::item; use libpso::packet::ship::*; use libpso::packet::messages::*; @@ -56,9 +56,7 @@ async fn test_equip_unit_from_equip_menu() { entity_gateway.set_character_equips(&char1.id, &equipped).await.unwrap(); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -133,9 +131,7 @@ async fn test_unequip_armor_with_units() { entity_gateway.set_character_equips(&char1.id, &equipped).await.unwrap(); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -193,9 +189,7 @@ async fn test_sort_items() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; diff --git a/tests/test_item_drop.rs b/tests/test_item_drop.rs index 02b01b1..0c700bb 100644 --- a/tests/test_item_drop.rs +++ b/tests/test_item_drop.rs @@ -1,15 +1,14 @@ -use elseware::common::serverstate::{ClientId, ServerState}; -use elseware::entity::gateway::{EntityGateway, InMemoryGateway}; -use elseware::entity::character::SectionID; -use elseware::common::leveltable::CharacterLevelTable; -use elseware::ship::ship::{ShipServerState, SendShipPacket, RecvShipPacket}; -use elseware::ship::room::{Episode, Difficulty}; -use elseware::ship::monster::MonsterType; -use elseware::ship::location::RoomId; -use elseware::ship::drops::{DropTable, MonsterDropStats, MonsterDropType}; -use elseware::ship::drops::rare_drop_table::{RareDropTable, RareDropRate, RareDropItem}; -use elseware::ship::map::{Maps, MapVariant, MapArea, MapVariantMode, MapEnemy}; -use elseware::entity::item::weapon::WeaponType; +use networking::serverstate::{ClientId, ServerState}; +use entity::gateway::InMemoryGateway; +use elseware::ship::ship::{SendShipPacket, RecvShipPacket}; +use maps::monster::MonsterType; +use drops::{StandardDropTable, MonsterDropStats, MonsterDropType}; +use drops::rare_drop_table::{RareDropTable, RareDropRate, RareDropItem}; +use maps::maps::Maps; +use maps::area::MapArea; +use maps::variant::{MapVariant, MapVariantMode}; +use maps::enemy::MapEnemy; +use entity::item::weapon::WeaponType; use libpso::packet::ship::*; use libpso::packet::messages::*; @@ -23,32 +22,31 @@ async fn test_enemy_drops_item() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .map_builder(Box::new(|_room_mode, _event| { - Maps::new( - vec![MapVariant::new(MapArea::Forest2, MapVariantMode::Online)], - vec![Some(MapEnemy::new(MonsterType::Hildebear, MapArea::Forest2))], - Vec::new(), - ) - })) - .drop_table_builder(Box::new(|episode, difficulty, section_id| { - DropTable::builder() - .monster_stat(MonsterType::Hildebear, MonsterDropStats { - dar: 100, - drop_type: MonsterDropType::Weapon, - min_meseta: 0, - max_meseta: 0, - }) - .rare_table(RareDropTable::builder() - .rate(MonsterType::Hildebear, RareDropRate { - rate: 1.0, - item: RareDropItem::Weapon(WeaponType::DarkFlow) - }) - .build(episode, difficulty, section_id)) - .build(episode, difficulty, section_id) - })) - .build()); + let mut ship = standard_ship_buildable(entity_gateway.clone()) + .map_builder(Box::new(|_room_mode, _event| { + Maps::new( + vec![MapVariant::new(MapArea::Forest2, MapVariantMode::Online)], + vec![Some(MapEnemy::new(MonsterType::Hildebear, MapArea::Forest2))], + Vec::new(), + ) + })) + .drop_table_builder(Box::new(|episode, difficulty, section_id| { + StandardDropTable::builder() + .monster_stat(MonsterType::Hildebear, MonsterDropStats { + dar: 100, + drop_type: MonsterDropType::Weapon, + min_meseta: 0, + max_meseta: 0, + }) + .rare_table(RareDropTable::builder() + .rate(MonsterType::Hildebear, RareDropRate { + rate: 1.0, + item: RareDropItem::Weapon(WeaponType::DarkFlow) + }) + .build(episode, difficulty, section_id)) + .build(episode, difficulty, section_id) + })) + .build(); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -79,32 +77,31 @@ async fn test_enemy_drops_item_for_two_players() { let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .map_builder(Box::new(|_room_mode, _event| { - Maps::new( - vec![MapVariant::new(MapArea::Forest2, MapVariantMode::Online)], - vec![Some(MapEnemy::new(MonsterType::Hildebear, MapArea::Forest2))], - Vec::new(), - ) - })) - .drop_table_builder(Box::new(|episode, difficulty, section_id| { - DropTable::builder() - .monster_stat(MonsterType::Hildebear, MonsterDropStats { - dar: 100, - drop_type: MonsterDropType::Weapon, - min_meseta: 0, - max_meseta: 0, - }) - .rare_table(RareDropTable::builder() - .rate(MonsterType::Hildebear, RareDropRate { - rate: 1.0, - item: RareDropItem::Weapon(WeaponType::DarkFlow) - }) - .build(episode, difficulty, section_id)) - .build(episode, difficulty, section_id) - })) - .build()); + let mut ship = standard_ship_buildable(entity_gateway.clone()) + .map_builder(Box::new(|_room_mode, _event| { + Maps::new( + vec![MapVariant::new(MapArea::Forest2, MapVariantMode::Online)], + vec![Some(MapEnemy::new(MonsterType::Hildebear, MapArea::Forest2))], + Vec::new(), + ) + })) + .drop_table_builder(Box::new(|episode, difficulty, section_id| { + StandardDropTable::builder() + .monster_stat(MonsterType::Hildebear, MonsterDropStats { + dar: 100, + drop_type: MonsterDropType::Weapon, + min_meseta: 0, + max_meseta: 0, + }) + .rare_table(RareDropTable::builder() + .rate(MonsterType::Hildebear, RareDropRate { + rate: 1.0, + item: RareDropItem::Weapon(WeaponType::DarkFlow) + }) + .build(episode, difficulty, section_id)) + .build(episode, difficulty, section_id) + })) + .build(); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -147,32 +144,31 @@ async fn test_enemy_drops_item_for_two_players_and_pick_up() { let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .map_builder(Box::new(|_room_mode, _event| { - Maps::new( - vec![MapVariant::new(MapArea::Forest2, MapVariantMode::Online)], - vec![Some(MapEnemy::new(MonsterType::Hildebear, MapArea::Forest2))], - Vec::new(), - ) - })) - .drop_table_builder(Box::new(|episode, difficulty, section_id| { - DropTable::builder() - .monster_stat(MonsterType::Hildebear, MonsterDropStats { - dar: 100, - drop_type: MonsterDropType::Weapon, - min_meseta: 0, - max_meseta: 0, - }) - .rare_table(RareDropTable::builder() - .rate(MonsterType::Hildebear, RareDropRate { - rate: 1.0, - item: RareDropItem::Weapon(WeaponType::DarkFlow) - }) - .build(episode, difficulty, section_id)) - .build(episode, difficulty, section_id) - })) - .build()); + let mut ship = standard_ship_buildable(entity_gateway.clone()) + .map_builder(Box::new(|_room_mode, _event| { + Maps::new( + vec![MapVariant::new(MapArea::Forest2, MapVariantMode::Online)], + vec![Some(MapEnemy::new(MonsterType::Hildebear, MapArea::Forest2))], + Vec::new(), + ) + })) + .drop_table_builder(Box::new(|episode, difficulty, section_id| { + StandardDropTable::builder() + .monster_stat(MonsterType::Hildebear, MonsterDropStats { + dar: 100, + drop_type: MonsterDropType::Weapon, + min_meseta: 0, + max_meseta: 0, + }) + .rare_table(RareDropTable::builder() + .rate(MonsterType::Hildebear, RareDropRate { + rate: 1.0, + item: RareDropItem::Weapon(WeaponType::DarkFlow) + }) + .build(episode, difficulty, section_id)) + .build(episode, difficulty, section_id) + })) + .build(); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; diff --git a/tests/test_item_id.rs b/tests/test_item_id.rs index f8ba644..ad2f860 100644 --- a/tests/test_item_id.rs +++ b/tests/test_item_id.rs @@ -1,9 +1,7 @@ -use elseware::common::serverstate::{ClientId, ServerState}; -use elseware::entity::gateway::{EntityGateway, InMemoryGateway}; -use elseware::entity::item; -use elseware::ship::ship::{ShipServerState, RecvShipPacket}; -use elseware::entity::character::TechLevel; -//use elseware::ship::items::{ClientItemId, ActiveItemEntityId, HeldItemType, FloorItemType}; +use networking::serverstate::{ClientId, ServerState}; +use entity::gateway::{EntityGateway, InMemoryGateway}; +use entity::item; +use elseware::ship::ship::RecvShipPacket; use libpso::packet::ship::*; use libpso::packet::messages::*; @@ -55,9 +53,7 @@ async fn test_use_monomate_after_leaving_and_rejoining_room() { entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(p2_items)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -169,9 +165,7 @@ async fn test_using_some_monomates_after_a_convoluted_series_of_leaves_and_joins } entity_gateway.set_character_inventory(&char3.id, &item::InventoryEntity::new(p3_items)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; log_in_char(&mut ship, ClientId(3), "a3", "a").await; @@ -332,9 +326,7 @@ async fn test_depositing_a_full_stack_then_withdrawing_part() { } entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), &item::BankIdentifier::Character).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; diff --git a/tests/test_item_pickup.rs b/tests/test_item_pickup.rs index 394cc3a..3ec0b9c 100644 --- a/tests/test_item_pickup.rs +++ b/tests/test_item_pickup.rs @@ -1,7 +1,7 @@ -use elseware::common::serverstate::{ClientId, ServerState}; -use elseware::entity::gateway::{EntityGateway, InMemoryGateway}; -use elseware::entity::item; -use elseware::ship::ship::{ShipServerState, RecvShipPacket}; +use networking::serverstate::{ClientId, ServerState}; +use entity::gateway::{EntityGateway, InMemoryGateway}; +use entity::item; +use elseware::ship::ship::RecvShipPacket; use libpso::packet::ship::*; use libpso::packet::messages::*; @@ -34,9 +34,7 @@ async fn test_pick_up_individual_item() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -117,9 +115,7 @@ async fn test_pick_up_item_stack_of_items_already_in_inventory() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_monomate])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(p2_items)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -176,9 +172,7 @@ async fn test_pick_up_item_stack_of_items_not_already_held() { entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_monomate])).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -242,9 +236,7 @@ async fn test_pick_up_meseta_when_inventory_full() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_items)).await.unwrap(); entity_gateway.set_character_meseta(&char2.id, item::Meseta(300)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -334,9 +326,7 @@ async fn test_pick_up_partial_stacked_item_when_inventory_is_otherwise_full() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_monomates])).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -412,9 +402,7 @@ async fn test_can_not_pick_up_item_when_inventory_full() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(p2_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -470,9 +458,7 @@ async fn test_can_not_drop_more_meseta_than_is_held() { entity_gateway.set_character_meseta(&char1.id, item::Meseta(300)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -535,9 +521,7 @@ async fn test_pick_up_stack_that_would_exceed_stack_limit() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_monomates])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_monomates])).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -586,9 +570,7 @@ async fn test_can_not_pick_up_meseta_when_full() { entity_gateway.set_character_meseta(&char1.id, item::Meseta(999999)).await.unwrap(); entity_gateway.set_character_meseta(&char2.id, item::Meseta(300)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -641,9 +623,7 @@ async fn test_meseta_caps_at_999999_when_trying_to_pick_up_more() { entity_gateway.set_character_meseta(&char1.id, item::Meseta(999998)).await.unwrap(); entity_gateway.set_character_meseta(&char2.id, item::Meseta(300)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -706,9 +686,7 @@ async fn test_player_drops_partial_stack_and_other_player_picks_it_up() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![monomates])).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; diff --git a/tests/test_item_use.rs b/tests/test_item_use.rs index b6b8502..13f2310 100644 --- a/tests/test_item_use.rs +++ b/tests/test_item_use.rs @@ -1,9 +1,8 @@ -use elseware::common::serverstate::{ClientId, ServerState}; -use elseware::entity::gateway::{EntityGateway, InMemoryGateway}; -use elseware::entity::item; -use elseware::ship::ship::{ShipServerState, RecvShipPacket}; -use elseware::entity::character::TechLevel; -//use elseware::ship::items::{ClientItemId, ActiveItemEntityId, HeldItemType, FloorItemType}; +use networking::serverstate::{ClientId, ServerState}; +use entity::gateway::{EntityGateway, InMemoryGateway}; +use entity::item; +use elseware::ship::ship::RecvShipPacket; +use entity::character::TechLevel; use libpso::packet::ship::*; use libpso::packet::messages::*; @@ -37,9 +36,7 @@ async fn test_use_monomate() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_items)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -84,9 +81,7 @@ async fn test_use_monomate_twice() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_items)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -132,9 +127,7 @@ async fn test_use_last_monomate() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -172,9 +165,7 @@ async fn test_use_nonstackable_tool() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_items)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -213,9 +204,7 @@ async fn test_use_materials() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -278,9 +267,7 @@ async fn test_jackolantern() { ])]; entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -347,9 +334,7 @@ async fn test_use_barta_1() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -418,9 +403,7 @@ async fn test_use_monogrinder() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![item::InventoryItemEntity::Individual(saber), item::InventoryItemEntity::Stacked(grinders)])).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; diff --git a/tests/test_mags.rs b/tests/test_mags.rs index 1a7d687..dac0e06 100644 --- a/tests/test_mags.rs +++ b/tests/test_mags.rs @@ -1,8 +1,8 @@ -use elseware::common::serverstate::{ClientId, ServerState}; -use elseware::entity::gateway::{EntityGateway, InMemoryGateway}; -use elseware::entity::item; -use elseware::ship::ship::{ShipServerState, RecvShipPacket}; -use elseware::entity::character::{CharacterClass, SectionID}; +use networking::serverstate::{ClientId, ServerState}; +use entity::gateway::{EntityGateway, InMemoryGateway}; +use entity::item; +use elseware::ship::ship::RecvShipPacket; +use entity::character::{CharacterClass, SectionID}; use libpso::packet::ship::*; use libpso::packet::messages::*; @@ -50,9 +50,7 @@ async fn test_mag_feed() { inventory.push(item::InventoryItemEntity::Stacked(monomates)); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -105,9 +103,7 @@ async fn test_mag_change_owner() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![mag])).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -191,9 +187,7 @@ async fn test_mag_cell() { entity_gateway.set_character_equips(&char1.id, &equipped).await.unwrap(); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![mag, mag_cell])).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; diff --git a/tests/test_rooms.rs b/tests/test_rooms.rs index 00b512d..447ea7b 100644 --- a/tests/test_rooms.rs +++ b/tests/test_rooms.rs @@ -1,8 +1,6 @@ -use elseware::common::serverstate::{ClientId, ServerState}; -use elseware::entity::gateway::{EntityGateway, InMemoryGateway}; -use elseware::entity::item; -use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket}; -use elseware::ship::location::RoomId; +use networking::serverstate::{ClientId, ServerState}; +use entity::gateway::{EntityGateway, InMemoryGateway}; +use elseware::ship::ship::{RecvShipPacket, SendShipPacket}; use libpso::packet::ship::*; //use libpso::packet::messages::*; @@ -16,9 +14,9 @@ use common::*; async fn test_set_valid_quest_group() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship_buildable(entity_gateway.clone()) + .standard_quest_builder(Box::new(quests::load_standard_quests)) + .build(); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -36,9 +34,10 @@ async fn test_set_valid_quest_group() { async fn test_set_invalid_quest_group() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship_buildable(entity_gateway.clone()) + .standard_quest_builder(Box::new(quests::load_standard_quests)) + .government_quest_builder(Box::new(quests::load_government_quests)) + .build(); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room(&mut ship, ClientId(1), "room", "").await; @@ -61,9 +60,7 @@ async fn test_get_room_info() { _char1.name = String::from("GODmar"); entity_gateway.save_character(&_char1).await.unwrap(); let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -82,9 +79,7 @@ async fn test_cannot_get_room_info_after_room_is_closed() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -104,9 +99,7 @@ async fn test_cannot_join_room_after_its_closed() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; diff --git a/tests/test_shops.rs b/tests/test_shops.rs index cf230c9..cfcb749 100644 --- a/tests/test_shops.rs +++ b/tests/test_shops.rs @@ -1,9 +1,10 @@ -use elseware::common::serverstate::{ClientId, ServerState}; -use elseware::entity::gateway::{EntityGateway, InMemoryGateway}; -use elseware::entity::item; -use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket, ShipError}; -use elseware::ship::room::Difficulty; -use elseware::ship::items::state::ItemStateError; +use networking::serverstate::{ClientId, ServerState}; +use entity::gateway::{EntityGateway, InMemoryGateway}; +use entity::item; +use elseware::ship::ship::{RecvShipPacket, SendShipPacket}; +use maps::room::Difficulty; +use items::state::ItemStateError; +use shops::StandardItemShops; use libpso::packet::ship::*; use libpso::packet::messages::*; @@ -20,9 +21,9 @@ async fn test_player_opens_weapon_shop() { char1.exp = 80000000; entity_gateway.save_character(&char1).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship_buildable(entity_gateway.clone()) + .item_shops(StandardItemShops::default()) + .build(); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await; @@ -50,9 +51,9 @@ async fn test_player_opens_tool_shop() { char1.exp = 80000000; entity_gateway.save_character(&char1).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship_buildable(entity_gateway.clone()) + .item_shops(StandardItemShops::default()) + .build(); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await; @@ -80,9 +81,9 @@ async fn test_player_opens_armor_shop() { char1.exp = 80000000; entity_gateway.save_character(&char1).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship_buildable(entity_gateway.clone()) + .item_shops(StandardItemShops::default()) + .build(); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await; @@ -111,9 +112,9 @@ async fn test_player_buys_from_weapon_shop() { entity_gateway.save_character(&char1).await.unwrap(); entity_gateway.set_character_meseta(&char1.id, item::Meseta(999999)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship_buildable(entity_gateway.clone()) + .item_shops(StandardItemShops::default()) + .build(); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await; @@ -149,9 +150,9 @@ async fn test_player_buys_from_tool_shop() { entity_gateway.save_character(&char1).await.unwrap(); entity_gateway.set_character_meseta(&char1.id, item::Meseta(999999)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship_buildable(entity_gateway.clone()) + .item_shops(StandardItemShops::default()) + .build(); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await; @@ -186,9 +187,9 @@ async fn test_player_buys_multiple_from_tool_shop() { entity_gateway.save_character(&char1).await.unwrap(); entity_gateway.set_character_meseta(&char1.id, item::Meseta(999999)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship_buildable(entity_gateway.clone()) + .item_shops(StandardItemShops::default()) + .build(); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await; @@ -227,9 +228,9 @@ async fn test_player_buys_from_armor_shop() { entity_gateway.save_character(&char1).await.unwrap(); entity_gateway.set_character_meseta(&char1.id, item::Meseta(999999)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship_buildable(entity_gateway.clone()) + .item_shops(StandardItemShops::default()) + .build(); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await; @@ -280,9 +281,7 @@ async fn test_player_sells_3_attr_weapon_to_shop() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -309,9 +308,9 @@ async fn test_other_clients_see_purchase() { entity_gateway.set_character_meseta(&char1.id, item::Meseta(999999)).await.unwrap(); entity_gateway.save_character(&char1).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship_buildable(entity_gateway.clone()) + .item_shops(StandardItemShops::default()) + .build(); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -360,9 +359,9 @@ async fn test_other_clients_see_stacked_purchase() { ), }).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship_buildable(entity_gateway.clone()) + .item_shops(StandardItemShops::default()) + .build(); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -399,9 +398,7 @@ async fn test_buying_item_without_enough_mseseta() { let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Normal).await; @@ -437,9 +434,9 @@ async fn test_player_double_buys_from_tool_shop() { entity_gateway.save_character(&char1).await.unwrap(); entity_gateway.set_character_meseta(&char1.id, item::Meseta(999999)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship_buildable(entity_gateway.clone()) + .item_shops(StandardItemShops::default()) + .build(); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await; @@ -502,9 +499,9 @@ async fn test_techs_disappear_from_shop_when_bought() { entity_gateway.save_character(&char1).await.unwrap(); entity_gateway.set_character_meseta(&char1.id, item::Meseta(999999)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship_buildable(entity_gateway.clone()) + .item_shops(StandardItemShops::default()) + .build(); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await; @@ -564,9 +561,9 @@ async fn test_units_disappear_from_shop_when_bought() { entity_gateway.save_character(&char1).await.unwrap(); entity_gateway.set_character_meseta(&char1.id, item::Meseta(999999)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship_buildable(entity_gateway.clone()) + .item_shops(StandardItemShops::default()) + .build(); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await; @@ -641,9 +638,7 @@ async fn test_player_sells_untekked_weapon() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -685,9 +680,7 @@ async fn test_player_sells_rare_item() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -728,9 +721,7 @@ async fn test_player_sells_partial_photon_drop_stack() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -769,9 +760,7 @@ async fn test_player_sells_basic_frame() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -810,9 +799,7 @@ async fn test_player_sells_max_frame() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -850,9 +837,7 @@ async fn test_player_sells_basic_barrier() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -890,9 +875,7 @@ async fn test_player_sells_max_barrier() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -929,9 +912,7 @@ async fn test_player_sells_1_star_minusminus_unit() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -968,9 +949,7 @@ async fn test_player_sells_5_star_plusplus_unit() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -1009,9 +988,7 @@ async fn test_player_sells_rare_frame() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -1049,9 +1026,7 @@ async fn test_player_sells_rare_barrier() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -1088,9 +1063,7 @@ async fn test_player_sells_rare_unit() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; @@ -1128,9 +1101,7 @@ async fn test_player_cant_sell_if_meseta_would_go_over_max() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; join_lobby(&mut ship, ClientId(1)).await; diff --git a/tests/test_trade.rs b/tests/test_trade.rs index 4155a46..4a08d0a 100644 --- a/tests/test_trade.rs +++ b/tests/test_trade.rs @@ -1,9 +1,9 @@ use std::convert::TryInto; -use elseware::common::serverstate::{ClientId, ServerState}; -use elseware::entity::gateway::{EntityGateway, InMemoryGateway}; -use elseware::entity::item; -use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket, ShipError}; -use elseware::entity::item::{Meseta, ItemEntity}; +use networking::serverstate::{ClientId, ServerState}; +use entity::gateway::{EntityGateway, InMemoryGateway}; +use entity::item; +use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket}; +use entity::item::{Meseta, ItemEntity, InventoryItemEntity}; use elseware::ship::packet::handler::trade::TradeError; use libpso::packet::ship::*; @@ -62,7 +62,7 @@ struct TradeItemBuilder { } impl TradeItemBuilder { - fn individual(mut self, item: &elseware::entity::item::InventoryItemEntity, item_id: u32) -> Self { + fn individual(mut self, item: &InventoryItemEntity, item_id: u32) -> Self { let idata = item.with_individual(|i| i.item.as_client_bytes()).unwrap(); self.items[self.count] = TradeItem { item_data: idata[0..12].try_into().unwrap(), @@ -74,7 +74,7 @@ impl TradeItemBuilder { self } - fn stacked(mut self, item: &elseware::entity::item::InventoryItemEntity, item_id: u32, amount: u8) -> Self { + fn stacked(mut self, item: &InventoryItemEntity, item_id: u32, amount: u8) -> Self { let idata = item .with_stacked(|i| i[0].item.tool().unwrap().as_stacked_bytes(i.len())) .map(|mut data| { @@ -133,9 +133,7 @@ async fn test_trade_one_individual_item() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -234,9 +232,7 @@ async fn test_trade_player2_to_player1() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(p2_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -335,9 +331,7 @@ async fn test_reverse_trade_ack_order() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -439,9 +433,7 @@ async fn test_trade_one_stacked_item() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -543,9 +535,7 @@ async fn test_trade_partial_stacked_item() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -660,9 +650,7 @@ async fn test_trade_individual_both() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(p2_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -832,9 +820,7 @@ async fn test_trade_stacked_both() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_stack])).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -1002,9 +988,7 @@ async fn test_trade_partial_stack_both() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_stack])).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -1178,9 +1162,7 @@ async fn test_trade_same_stacked_item_to_eachother() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_stack])).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -1350,9 +1332,7 @@ async fn test_trade_stacked_when_already_have_partial_stack() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_stack])).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -1487,9 +1467,7 @@ async fn test_trade_individual_for_stacked() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_stack])).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -1679,9 +1657,7 @@ async fn test_trade_multiple_individual() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(p2_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -1950,9 +1926,7 @@ async fn test_trade_multiple_stacked() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack1, p1_stack2])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_stack1, p2_stack2])).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -2195,9 +2169,7 @@ async fn test_trade_not_enough_inventory_space_individual() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(p2_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -2313,9 +2285,7 @@ async fn test_trade_not_enough_inventory_space_stacked() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(p2_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -2425,9 +2395,7 @@ async fn test_trade_stack_too_big() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_stack])).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -2506,9 +2474,7 @@ async fn test_trade_meseta() { entity_gateway.set_character_meseta(&char1.id, Meseta(2323)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -2588,9 +2554,7 @@ async fn test_trade_too_much_meseta() { entity_gateway.set_character_meseta(&char1.id, Meseta(4000)).await.unwrap(); entity_gateway.set_character_meseta(&char2.id, Meseta(999000)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -2640,9 +2604,7 @@ async fn test_trade_invalid_amount_of_meseta() { entity_gateway.set_character_meseta(&char1.id, Meseta(4000)).await.unwrap(); entity_gateway.set_character_meseta(&char2.id, Meseta(999000)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -2692,9 +2654,7 @@ async fn test_trade_meseta_request_and_items_dont_match() { entity_gateway.set_character_meseta(&char1.id, Meseta(4000)).await.unwrap(); entity_gateway.set_character_meseta(&char2.id, Meseta(999000)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -2741,9 +2701,7 @@ async fn test_player_declined_trade() { let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -2789,9 +2747,7 @@ async fn test_back_out_of_trade_last_minute() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -2885,9 +2841,7 @@ async fn test_valid_trade_when_both_inventories_are_full() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(p2_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -3027,9 +2981,7 @@ async fn test_invalid_trade_when_both_inventories_are_full() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(p2_inv)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -3133,9 +3085,7 @@ async fn test_client_tries_to_start_two_trades() { let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let (_user2, _char3) = new_user_character(&mut entity_gateway, "a3", "a", 1).await; - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; log_in_char(&mut ship, ClientId(3), "a3", "a").await; @@ -3166,9 +3116,7 @@ async fn test_client_tries_trading_with_client_already_trading() { let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let (_user2, _char3) = new_user_character(&mut entity_gateway, "a3", "a", 1).await; - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; log_in_char(&mut ship, ClientId(3), "a3", "a").await; @@ -3223,9 +3171,7 @@ async fn test_add_then_remove_individual_item() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -3354,9 +3300,7 @@ async fn test_add_then_remove_stacked_item() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack1, p1_stack2])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -3489,9 +3433,7 @@ async fn test_add_then_remove_partial_stack() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack1, p1_stack2])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -3578,9 +3520,7 @@ async fn test_add_then_remove_meseta() { entity_gateway.set_character_meseta(&char1.id, Meseta(2323)).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -3679,9 +3619,7 @@ async fn test_items_to_trade_data_does_not_match() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -3762,9 +3700,7 @@ async fn test_items_to_trade_id_does_not_match() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -3836,9 +3772,7 @@ async fn test_stack_is_same_amount_in_request_and_items_to_trade() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -3910,9 +3844,7 @@ async fn test_stack_is_same_amount_in_request_and_items_to_trade2() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -4006,9 +3938,7 @@ async fn test_items_to_trade_count_less_than() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -4110,9 +4040,7 @@ async fn test_items_to_trade_count_greater_than() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -4218,9 +4146,7 @@ async fn test_items_to_trade_count_mismatch_with_meseta() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; @@ -4295,9 +4221,7 @@ async fn test_dropping_item_after_trade() { entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); - let mut ship = Box::new(ShipServerState::builder() - .gateway(entity_gateway.clone()) - .build()); + let mut ship = standard_ship(entity_gateway.clone()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; diff --git a/trade/Cargo.toml b/trade/Cargo.toml new file mode 100644 index 0000000..9d588b4 --- /dev/null +++ b/trade/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "trade" +version = "0.1.0" +edition = "2021" + + +[dependencies] +networking = { workspace = true } +items = { workspace = true } + +async-std = { workspace = true } +futures = { workspace = true } +anyhow = { workspace = true } +thiserror = { workspace = true } \ No newline at end of file diff --git a/src/ship/trade.rs b/trade/src/lib.rs similarity index 79% rename from src/ship/trade.rs rename to trade/src/lib.rs index abe1cd2..319652c 100644 --- a/src/ship/trade.rs +++ b/trade/src/lib.rs @@ -1,45 +1,8 @@ use std::collections::HashMap; -use crate::common::serverstate::ClientId; -use crate::ship::items; +use networking::serverstate::ClientId; use async_std::sync::{Arc, Mutex, MutexGuard}; use futures::future::{Future, OptionFuture}; - -#[derive(Debug, Clone)] -pub enum TradeItem { - Individual(items::ClientItemId), - Stacked(items::ClientItemId, usize), -} - -impl TradeItem { - pub fn stacked(&self) -> Option<(items::ClientItemId, usize)> { - match self { - TradeItem::Stacked(item_id, amount) => Some((*item_id, *amount)), - _ => None - } - } - - pub fn stacked_mut(&mut self) -> Option<(items::ClientItemId, &mut usize)> { - match self { - TradeItem::Stacked(item_id, ref mut amount) => Some((*item_id, amount)), - _ => None - } - } - - pub fn item_id(&self) -> items::ClientItemId { - match self { - TradeItem::Individual(item_id) => *item_id, - TradeItem::Stacked(item_id, _) => *item_id, - } - } - - pub fn amount(&self) -> usize { - match self { - TradeItem::Individual(_) => 1, - TradeItem::Stacked(_, amount) => *amount, - } - } -} - +use items::trade::TradeItem; #[derive(Debug, Clone, Eq, PartialEq)] pub enum TradeStatus {