diff --git a/src/bin/kawari-world.rs b/src/bin/kawari-world.rs index 9a35157..c35d230 100644 --- a/src/bin/kawari-world.rs +++ b/src/bin/kawari-world.rs @@ -832,12 +832,16 @@ async fn client_loop( } let mut inventory = Inventory::default(); + { + let mut game_data = game_data.lock().unwrap(); - // fill inventory - inventory.equip_racial_items( - chara_make.customize.race, - chara_make.customize.gender, - ); + // fill inventory + inventory.equip_racial_items( + chara_make.customize.race, + chara_make.customize.gender, + &mut game_data, + ); + } let (content_id, actor_id) = database.create_player_data( *service_account_id, @@ -932,11 +936,17 @@ async fn client_loop( world_name = game_data.get_world_name(config.world.world_id); } - let characters = database.get_character_list( - *service_account_id, - config.world.world_id, - &world_name, - ); + let characters; + { + let mut game_data = game_data.lock().unwrap(); + + characters = database.get_character_list( + *service_account_id, + config.world.world_id, + &world_name, + &mut game_data, + ); + } // send response { diff --git a/src/common/workdefinitions/client_select_data.rs b/src/common/workdefinitions/client_select_data.rs index 9a12529..9650e3e 100644 --- a/src/common/workdefinitions/client_select_data.rs +++ b/src/common/workdefinitions/client_select_data.rs @@ -3,7 +3,7 @@ use serde_json::json; use crate::common::CustomizeData; // TODO: this isn't really an enum in the game, nor is it a flag either. it's weird! -#[derive(Clone, Copy)] +#[derive(Debug, Clone, Copy)] #[repr(i32)] pub enum RemakeMode { /// No remake options are available. @@ -15,10 +15,11 @@ pub enum RemakeMode { } /// See https://github.com/aers/FFXIVClientStructs/blob/main/FFXIVClientStructs/FFXIV/Application/Network/WorkDefinitions/ClientSelectData.cs +#[derive(Debug)] pub struct ClientSelectData { - pub game_name_unk: String, + pub character_name: String, pub current_class: i32, - pub class_levels: [i32; 30], + pub class_levels: [i32; 32], pub race: i32, pub subrace: i32, pub gender: i32, @@ -32,9 +33,11 @@ pub struct ClientSelectData { /// The most notable is if your character can be remade, it says "Your character is currently bound by duty..." pub content_finder_condition: i32, pub customize: CustomizeData, - pub model_main_weapon: i32, + pub model_main_weapon: u64, pub model_sub_weapon: i32, - pub unk14: [i32; 10], // probably model ids + pub model_ids: [u32; 10], + pub equip_stain: [u32; 10], + pub glasses: [u32; 2], pub unk15: i32, pub unk16: i32, pub remake_mode: RemakeMode, // TODO: upstream a comment about this to FFXIVClientStructs @@ -44,13 +47,12 @@ pub struct ClientSelectData { pub unk20: i32, pub world_name: String, pub unk22: i32, - pub unk23: i32, } impl ClientSelectData { pub fn to_json(&self) -> String { let content = json!([ - self.game_name_unk, + self.character_name, self.current_class.to_string(), self.class_levels.map(|x| x.to_string()), self.race.to_string(), @@ -66,7 +68,9 @@ impl ClientSelectData { self.customize.to_json(), self.model_main_weapon.to_string(), self.model_sub_weapon.to_string(), - self.unk14.map(|x| x.to_string()), + self.model_ids.map(|x| x.to_string()), + self.equip_stain.map(|x| x.to_string()), + self.glasses.map(|x| x.to_string()), self.unk15.to_string(), self.unk16.to_string(), (self.remake_mode as i32).to_string(), @@ -75,7 +79,6 @@ impl ClientSelectData { self.unk20.to_string(), self.world_name, self.unk22.to_string(), - self.unk23.to_string(), ]); let obj = json!({ diff --git a/src/world/connection.rs b/src/world/connection.rs index 15a2f35..1883153 100644 --- a/src/world/connection.rs +++ b/src/world/connection.rs @@ -613,50 +613,17 @@ impl ZoneConnection { let ipc; { let mut game_data = self.gamedata.lock().unwrap(); - let equipped = &self.player_data.inventory.equipped; + let inventory = &self.player_data.inventory; ipc = ServerZoneIpcSegment { op_code: ServerZoneIpcType::Equip, timestamp: timestamp_secs(), data: ServerZoneIpcData::Equip(Equip { - main_weapon_id: game_data - .get_primary_model_id(equipped.main_hand.id) - .unwrap_or(0), + main_weapon_id: inventory.get_main_weapon_id(&mut game_data), sub_weapon_id: 0, crest_enable: 0, pattern_invalid: 0, - model_ids: [ - game_data - .get_primary_model_id(equipped.head.id) - .unwrap_or(0) as u32, - game_data - .get_primary_model_id(equipped.body.id) - .unwrap_or(0) as u32, - game_data - .get_primary_model_id(equipped.hands.id) - .unwrap_or(0) as u32, - game_data - .get_primary_model_id(equipped.legs.id) - .unwrap_or(0) as u32, - game_data - .get_primary_model_id(equipped.feet.id) - .unwrap_or(0) as u32, - game_data - .get_primary_model_id(equipped.ears.id) - .unwrap_or(0) as u32, - game_data - .get_primary_model_id(equipped.neck.id) - .unwrap_or(0) as u32, - game_data - .get_primary_model_id(equipped.wrists.id) - .unwrap_or(0) as u32, - game_data - .get_primary_model_id(equipped.left_ring.id) - .unwrap_or(0) as u32, - game_data - .get_primary_model_id(equipped.right_ring.id) - .unwrap_or(0) as u32, - ], + model_ids: inventory.get_model_ids(&mut game_data), }), ..Default::default() }; @@ -785,7 +752,8 @@ impl ZoneConnection { let chara_details = self.database.find_chara_make(self.player_data.content_id); - let equipped = &self.player_data.inventory.equipped; + let inventory = &self.player_data.inventory; + CommonSpawn { class_job: self.player_data.classjob_id, name: chara_details.name, @@ -797,41 +765,8 @@ impl ZoneConnection { object_kind: ObjectKind::Player(PlayerSubKind::Player), look: chara_details.chara_make.customize, display_flags: DisplayFlag::UNK, - main_weapon_model: game_data - .get_primary_model_id(equipped.main_hand.id) - .unwrap_or(0), - models: [ - game_data - .get_primary_model_id(equipped.head.id) - .unwrap_or(0) as u32, - game_data - .get_primary_model_id(equipped.body.id) - .unwrap_or(0) as u32, - game_data - .get_primary_model_id(equipped.hands.id) - .unwrap_or(0) as u32, - game_data - .get_primary_model_id(equipped.legs.id) - .unwrap_or(0) as u32, - game_data - .get_primary_model_id(equipped.feet.id) - .unwrap_or(0) as u32, - game_data - .get_primary_model_id(equipped.ears.id) - .unwrap_or(0) as u32, - game_data - .get_primary_model_id(equipped.neck.id) - .unwrap_or(0) as u32, - game_data - .get_primary_model_id(equipped.wrists.id) - .unwrap_or(0) as u32, - game_data - .get_primary_model_id(equipped.left_ring.id) - .unwrap_or(0) as u32, - game_data - .get_primary_model_id(equipped.right_ring.id) - .unwrap_or(0) as u32, - ], + main_weapon_model: inventory.get_main_weapon_id(&mut game_data), + models: inventory.get_model_ids(&mut game_data), pos: exit_position.unwrap_or_default(), rotation: exit_rotation.unwrap_or(0.0), ..Default::default() diff --git a/src/world/database.rs b/src/world/database.rs index ab734b7..520084c 100644 --- a/src/world/database.rs +++ b/src/world/database.rs @@ -5,7 +5,7 @@ use serde::Deserialize; use crate::{ common::{ - CustomizeData, Position, + CustomizeData, GameData, Position, workdefinitions::{CharaMake, ClientSelectData, RemakeMode}, }, lobby::ipc::{CharacterDetails, CharacterFlag}, @@ -225,6 +225,7 @@ impl WorldDatabase { service_account_id: u32, world_id: u16, world_name: &str, + game_data: &mut GameData, ) -> Vec { let connection = self.connection.lock().unwrap(); @@ -250,22 +251,24 @@ impl WorldDatabase { for (index, (content_id, actor_id)) in content_actor_ids.iter().enumerate() { let mut stmt = connection .prepare( - "SELECT name, chara_make, zone_id FROM character_data WHERE content_id = ?1", + "SELECT name, chara_make, zone_id, inventory FROM character_data WHERE content_id = ?1", ) .unwrap(); - let result: Result<(String, String, u16), rusqlite::Error> = stmt + let result: Result<(String, String, u16, String), rusqlite::Error> = stmt .query_row((content_id,), |row| { - Ok((row.get(0)?, row.get(1)?, row.get(2)?)) + Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?)) }); - if let Ok((name, chara_make, zone_id)) = result { + if let Ok((name, chara_make, zone_id, inventory_json)) = result { let chara_make = CharaMake::from_json(&chara_make); + let inventory: Inventory = serde_json::from_str(&inventory_json).unwrap(); + let select_data = ClientSelectData { - game_name_unk: "Final Fantasy".to_string(), + character_name: name.clone(), current_class: 2, - class_levels: [5; 30], + class_levels: [5; 32], race: chara_make.customize.race as i32, subrace: chara_make.customize.subrace as i32, gender: chara_make.customize.gender as i32, @@ -277,9 +280,11 @@ impl WorldDatabase { zone_id: zone_id as i32, content_finder_condition: 0, customize: chara_make.customize, - model_main_weapon: 0, + model_main_weapon: inventory.get_main_weapon_id(game_data), model_sub_weapon: 0, - unk14: [0; 10], + model_ids: inventory.get_model_ids(game_data), + equip_stain: [0; 10], + glasses: [0; 2], unk15: 0, unk16: 0, remake_mode: RemakeMode::None, @@ -288,7 +293,6 @@ impl WorldDatabase { unk20: 0, world_name: String::new(), unk22: 0, - unk23: 0, }; characters.push(CharacterDetails { diff --git a/src/world/inventory.rs b/src/world/inventory.rs index f11236a..233fe1d 100644 --- a/src/world/inventory.rs +++ b/src/world/inventory.rs @@ -1,10 +1,7 @@ -use physis::{ - common::{Language, Platform}, - gamedata::GameData, -}; +use physis::common::Language; use serde::{Deserialize, Serialize}; -use crate::config::get_config; +use crate::common::GameData; use super::ipc::{ContainerType, InventoryModify}; @@ -179,14 +176,10 @@ impl Default for Inventory { impl Inventory { /// Equip the starting items for a given race - pub fn equip_racial_items(&mut self, race_id: u8, gender: u8) { - let config = get_config(); - - let mut game_data = - GameData::from_existing(Platform::Win32, &config.game_location).unwrap(); - - let exh = game_data.read_excel_sheet_header("Race").unwrap(); + pub fn equip_racial_items(&mut self, race_id: u8, gender: u8, game_data: &mut GameData) { + let exh = game_data.game_data.read_excel_sheet_header("Race").unwrap(); let exd = game_data + .game_data .read_excel_sheet("Race", &exh, Language::English, 0) .unwrap(); @@ -278,4 +271,45 @@ impl Inventory { ContainerType::ArmouryBody => todo!(), } } + + pub fn get_main_weapon_id(&self, game_data: &mut GameData) -> u64 { + game_data + .get_primary_model_id(self.equipped.main_hand.id) + .unwrap_or(0) + } + + pub fn get_model_ids(&self, game_data: &mut GameData) -> [u32; 10] { + [ + game_data + .get_primary_model_id(self.equipped.head.id) + .unwrap_or(0) as u32, + game_data + .get_primary_model_id(self.equipped.body.id) + .unwrap_or(0) as u32, + game_data + .get_primary_model_id(self.equipped.hands.id) + .unwrap_or(0) as u32, + game_data + .get_primary_model_id(self.equipped.legs.id) + .unwrap_or(0) as u32, + game_data + .get_primary_model_id(self.equipped.feet.id) + .unwrap_or(0) as u32, + game_data + .get_primary_model_id(self.equipped.ears.id) + .unwrap_or(0) as u32, + game_data + .get_primary_model_id(self.equipped.neck.id) + .unwrap_or(0) as u32, + game_data + .get_primary_model_id(self.equipped.wrists.id) + .unwrap_or(0) as u32, + game_data + .get_primary_model_id(self.equipped.left_ring.id) + .unwrap_or(0) as u32, + game_data + .get_primary_model_id(self.equipped.right_ring.id) + .unwrap_or(0) as u32, + ] + } }