From 2010222c9be6a7b18f891dbc581e4001ef6426ae Mon Sep 17 00:00:00 2001 From: jake Date: Sat, 18 Jan 2020 23:36:28 -0800 Subject: [PATCH] implement db backing for items and send them to client --- src/entity/gateway/entitygateway.rs | 2 +- src/entity/gateway/inmemory.rs | 20 ++++---- src/entity/item.rs | 75 +++++++++++++++++++++++++++-- src/login/character.rs | 64 ++++++++++++++++++++++++ src/ship/character.rs | 13 ++--- src/ship/location.rs | 2 +- src/ship/mod.rs | 1 + src/ship/ship.rs | 47 +++++++++--------- 8 files changed, 173 insertions(+), 51 deletions(-) diff --git a/src/entity/gateway/entitygateway.rs b/src/entity/gateway/entitygateway.rs index 3e5f6c0..359e095 100644 --- a/src/entity/gateway/entitygateway.rs +++ b/src/entity/gateway/entitygateway.rs @@ -41,7 +41,7 @@ pub trait EntityGateway { unimplemented!(); } - fn new_item(&mut self, _item: item::Item, _location: ItemLocation) -> Item { + fn new_item(&mut self, _item: ItemDetail, _location: ItemLocation) -> Item { unimplemented!(); } diff --git a/src/entity/gateway/inmemory.rs b/src/entity/gateway/inmemory.rs index 2a0c97f..c302eba 100644 --- a/src/entity/gateway/inmemory.rs +++ b/src/entity/gateway/inmemory.rs @@ -17,7 +17,7 @@ pub struct InMemoryGateway { user_settings: Arc>>, //guildcard: Arc>>, characters: Arc>>, - items: Arc>>, + items: Arc>>, } impl InMemoryGateway { @@ -106,18 +106,18 @@ impl EntityGateway for InMemoryGateway { GuildCardData::default() } - fn new_item(&mut self, item: item::Item, location: ItemLocation) -> Item { + fn new_item(&mut self, item: ItemDetail, location: ItemLocation) -> Item { let mut items = self.items.lock().unwrap(); let id = items .iter() - .fold(0, |sum, (i, _)| std::cmp::max(sum, *i)) + .fold(0, |sum, (i, _)| std::cmp::max(sum, i.0)) + 1; let new_item = Item { - id: id, + id: ItemEntityId(id), location: location, item: item, }; - items.insert(id, new_item.clone()); + items.insert(ItemEntityId(id), new_item.clone()); new_item } @@ -131,11 +131,10 @@ impl EntityGateway for InMemoryGateway { items .iter() .filter(|(_, k)| { - if let ItemLocation::Inventory{character_id, index} = k.location { - character_id == character.id - } - else { - false + match k.location { + ItemLocation::Inventory{character_id, ..} => character_id == character.id, + ItemLocation::Bank{character_id, ..} => character_id == character.id, + _ => false } }) .map(|(_, k)| { @@ -143,5 +142,4 @@ impl EntityGateway for InMemoryGateway { }) .collect() } - } diff --git a/src/entity/item.rs b/src/entity/item.rs index ff5f058..3f280cc 100644 --- a/src/entity/item.rs +++ b/src/entity/item.rs @@ -1,7 +1,14 @@ use libpso::item; +use libpso::character::character; -#[derive(Clone, Debug)] +#[derive(PartialEq, Copy, Clone, Debug, Hash, Eq)] +pub struct ItemEntityId(pub u32); +#[derive(Hash, PartialEq, Eq, Debug, Clone)] +pub struct ItemId(u32); + + +#[derive(Clone, Debug, PartialEq)] pub enum ItemLocation { Inventory { character_id: u32, @@ -9,7 +16,7 @@ pub enum ItemLocation { }, Bank { character_id: u32, - slot: usize + slot: usize, }, Floor { // floor: eventually @@ -19,9 +26,67 @@ pub enum ItemLocation { -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] +pub struct Weapon { + pub equipped: bool, + pub weapon: item::weapon::Weapon, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct Armor { + pub equipped: bool, + pub armor: item::armor::Armor, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct Shield { + pub equipped: bool, + pub shield: item::shield::Shield, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct Tool { + pub tool: item::tool::ToolType, +} + +impl Tool { + pub fn as_bytes(&self) -> [u8; 16] { + let mut result = [0; 16]; + result[0..3].copy_from_slice(&self.tool.value()); + result + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum ItemDetail { + Weapon(Weapon), + Armor(Armor), + Shield(Shield), + Tool(Tool), +} + +impl ItemDetail { + pub fn is_stackable(&self) -> bool { + match self { + ItemDetail::Tool(tool) => true, + _ => false, + } + } + + pub fn as_bytes(&self) -> [u8; 16] { + match self { + ItemDetail::Weapon(weapon) => weapon.weapon.as_bytes(), + ItemDetail::Armor(armor) => armor.armor.as_bytes(), + ItemDetail::Shield(shield) => shield.shield.as_bytes(), + ItemDetail::Tool(tool) => tool.as_bytes(), + } + } +} + + +#[derive(Clone, Debug, PartialEq)] pub struct Item { - pub id: u32, + pub id: ItemEntityId, pub location: ItemLocation, - pub item: item::Item, + pub item: ItemDetail, } diff --git a/src/login/character.rs b/src/login/character.rs index ca4e65c..ace3e1e 100644 --- a/src/login/character.rs +++ b/src/login/character.rs @@ -7,6 +7,7 @@ use crc::{crc32, Hasher32}; use libpso::packet::login::*; use libpso::{PacketParseError, PSOPacket}; use libpso::crypto::bb::PSOBBCipher; +use libpso::item; use crate::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY}; use crate::common::serverstate::{SendServerPacket, RecvServerPacket, ServerState, OnConnect, ClientId}; @@ -15,6 +16,7 @@ use libpso::{utf8_to_array, utf8_to_utf16_array}; use crate::entity::gateway::EntityGateway; use crate::entity::account::{UserAccount, USERFLAG_NEWCHAR, USERFLAG_DRESSINGROOM}; +use crate::entity::item::{ItemDetail, ItemLocation, Weapon, Armor, Shield, Tool}; use crate::entity::character::Character; use crate::login::login::get_login_status; @@ -188,6 +190,68 @@ fn new_character(entity_gateway: &mut EG, user: &UserAccount, char.slot = preview.slot; char.character = preview.character.as_character(); entity_gateway.set_character(&char); + + let new_weapon = match char.character.ch_class { + 0 | 1 | 2 | 9 => item::weapon::WeaponType::Saber, + 3 | 4 | 5 | 11 => item::weapon::WeaponType::Handgun, + 6 | 7 | 8 | 10 => item::weapon::WeaponType::Cane, + _ => panic!() + }; + + entity_gateway.new_item( + ItemDetail::Weapon( + Weapon { + equipped: true, + weapon: item::weapon::Weapon { + weapon: new_weapon, + grind: 0, + special: None, + attrs: [None; 3] + } + }), + ItemLocation::Inventory { + character_id: char.id, + index: 0, + }); + + entity_gateway.new_item( + ItemDetail::Armor ( + Armor { + equipped: true, + armor: item::armor::Armor { + armor: item::armor::ArmorType::Frame, + dfp: 0, + evp: 0, + slots: 0, + } + }), + ItemLocation::Inventory { + character_id: char.id, + index: 1, + }); + + for _ in 0..4 { + entity_gateway.new_item( + ItemDetail::Tool ( + Tool { + tool: item::tool::ToolType::Monomate, + }), + ItemLocation::Inventory { + character_id: char.id, + index: 2, + }); + entity_gateway.new_item( + ItemDetail::Tool ( + Tool { + tool: item::tool::ToolType::Monofluid, + }), + ItemLocation::Inventory { + character_id: char.id, + index: 3, + }); + } + + // TODO: starter mag } diff --git a/src/ship/character.rs b/src/ship/character.rs index 29c5c65..23c4645 100644 --- a/src/ship/character.rs +++ b/src/ship/character.rs @@ -1,5 +1,6 @@ use crate::common::leveltable::CharacterStats; use libpso::character::character; +use crate::ship::items::Inventory; pub struct CharacterBuilder<'a> { @@ -60,8 +61,7 @@ impl<'a> CharacterBuilder<'a> { pub struct FullCharacterBuilder<'a> { character: Option<&'a character::Character>, - inventory: Option<&'a [character::InventoryItem; 30]>, - inventory_len: Option, + inventory: Option<&'a Inventory>, key_config: Option<&'a [u8; 0x16C]>, joystick_config: Option<&'a [u8; 0x38]>, } @@ -72,7 +72,6 @@ impl<'a> FullCharacterBuilder<'a> { FullCharacterBuilder { character: None, inventory: None, - inventory_len: None, key_config: None, joystick_config: None, } @@ -85,10 +84,9 @@ impl<'a> FullCharacterBuilder<'a> { } } - pub fn inventory(self, inventory: &'a [character::InventoryItem; 30], len: usize) -> FullCharacterBuilder<'a> { + pub fn inventory(self, inventory: &'a Inventory) -> FullCharacterBuilder<'a> { FullCharacterBuilder { inventory: Some(inventory), - inventory_len: Some(len), ..self } } @@ -110,15 +108,14 @@ impl<'a> FullCharacterBuilder<'a> { pub fn build(self) -> character::FullCharacter { let character = self.character.unwrap(); let inventory = self.inventory.unwrap(); - let inventory_len = self.inventory_len.unwrap(); let key_config = self.key_config.unwrap(); let joystick_config = self.joystick_config.unwrap(); character::FullCharacter { character: *character, inventory: character::Inventory { - item_count: inventory_len as u8, - items: *inventory, + item_count: inventory.count() as u8, + items: inventory.as_client_inventory_items(), ..character::Inventory::default() }, key_team_config: character::KeyTeamConfig { diff --git a/src/ship/location.rs b/src/ship/location.rs index c7a0176..04b78fa 100644 --- a/src/ship/location.rs +++ b/src/ship/location.rs @@ -289,7 +289,7 @@ impl ClientLocation { self.rooms.iter_mut() .filter(|lobby| lobby.is_some()) .map(|lobby| lobby.as_ref().unwrap()) - .map(|mut lobby| lobby.write().unwrap().remove(id)) + .map(|lobby| lobby.write().unwrap().remove(id)) .any(|k| k); } } diff --git a/src/ship/mod.rs b/src/ship/mod.rs index 71c0187..78a2eef 100644 --- a/src/ship/mod.rs +++ b/src/ship/mod.rs @@ -2,3 +2,4 @@ pub mod ship; pub mod location; pub mod character; pub mod room; +pub mod items; diff --git a/src/ship/ship.rs b/src/ship/ship.rs index f4f3e17..33df6ce 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -17,10 +17,11 @@ use crate::common::leveltable::CharacterLevelTable; use crate::entity::gateway::EntityGateway; use crate::entity::account::{UserAccount, UserSettings, USERFLAG_NEWCHAR, USERFLAG_DRESSINGROOM}; use crate::entity::character::Character; -use crate::entity::item::ItemLocation; +use crate::entity::item::{ItemLocation, Item}; use crate::login::login::get_login_status; use crate::ship::location::{ClientLocation, LobbyId, RoomId, AreaType, MAX_ROOMS}; use crate::ship::character::{CharacterBuilder, FullCharacterBuilder}; +use crate::ship::items; use crate::ship::room; pub const SHIP_PORT: u16 = 23423; @@ -109,24 +110,31 @@ struct ClientState { settings: UserSettings, character: Character, session: Session, + inventory: items::Inventory, + //bank: Bank, block: u32, - room_client_id: u32, } impl ClientState { - fn new(user: UserAccount, settings: UserSettings, character: Character, session: Session) -> ClientState { + fn new(user: UserAccount, settings: UserSettings, character: Character, inventory: items::Inventory, /*bank: Bank,*/ session: Session) -> ClientState { ClientState { user: user, settings: settings, character: character, session: session, + inventory: inventory, + //bank: bank, block: 1, - room_client_id: 0, } } } + + + + + pub struct ShipServerState { entity_gateway: EG, clients: HashMap, @@ -134,6 +142,7 @@ pub struct ShipServerState { level_table: CharacterLevelTable, name: String, rooms: [Option; MAX_ROOMS], + item_activator: items::ItemActivator, } impl ShipServerState { @@ -145,6 +154,7 @@ impl ShipServerState { level_table: CharacterLevelTable::new(), name: "Sona-Nyl".into(), rooms: [None; MAX_ROOMS], + item_activator: items::ItemActivator::new(), } } @@ -162,7 +172,13 @@ impl ShipServerState { let settings = self.entity_gateway.get_user_settings_by_user(&user) .ok_or(ShipError::ClientNotFound(id))?; - self.clients.insert(id, ClientState::new(user, settings, character, pkt.session)); + let items = self.entity_gateway.get_items_by_character(&character); + let (inventory, bank) = items::split_items_into_inventory_and_bank(items); + let stacked_items = items::stack_items(inventory); + let activated_items = stacked_items.into_iter().map(|item| self.item_activator.activate_item(item)).collect(); + let inventory = items::Inventory::new(activated_items); + + self.clients.insert(id, ClientState::new(user, settings, character, inventory, pkt.session)); vec![SendShipPacket::LoginResponse(response), SendShipPacket::ShipBlockList(ShipBlockList::new(&self.name, 3))] }, Err(err) => { @@ -177,34 +193,15 @@ impl ShipServerState { let (level, stats) = self.level_table.get_stats_from_exp(character::Class::from(client.character.character.ch_class), client.character.character.exp); - let items = self.entity_gateway.get_items_by_character(&client.character); - let (inventory, inv_len) = items - .iter() - .take(30) - .fold(([character::InventoryItem::default(); 30], 0), |(mut inv, len), item| { - let index = { - if let ItemLocation::Inventory{index, .. } = item.location { - index - } - else { - panic!("inventory item that isnt in inventory???"); - } - }; - let bytes = item.item.as_bytes(); - inv[index].data1.copy_from_slice(&bytes[0..12]); - inv[index].item_id = item.id; - (inv, len+1) - }); let fc = FullCharacterBuilder::new() .character(&CharacterBuilder::new() .character(&client.character.character) .stats(&stats) .level(level - 1) .build()) - .inventory(&inventory, inv_len) + .inventory(&client.inventory) .key_config(&client.settings.settings.key_config) .joystick_config(&client.settings.settings.joystick_config) - //.team_config(&joystick_config) .build(); Ok(vec![