diff --git a/src/bin/kawari-world.rs b/src/bin/kawari-world.rs index 5911657..46660c5 100644 --- a/src/bin/kawari-world.rs +++ b/src/bin/kawari-world.rs @@ -1,8 +1,9 @@ use std::sync::Arc; use kawari::common::custom_ipc::{CustomIpcData, CustomIpcSegment, CustomIpcType}; -use kawari::common::get_world_name; +use kawari::common::{get_citystate, get_world_name}; use kawari::config::get_config; +use kawari::lobby::CharaMake; use kawari::oodle::OodleNetwork; use kawari::packet::{ CompressionType, ConnectionType, PacketSegment, PacketState, SegmentType, send_keep_alive, @@ -260,7 +261,7 @@ async fn main() { .chara_make .customize .subrace, - city_state: chara_details.chara_make.unk6 as u8, // TODO: probably wrong + city_state: chara_details.city_state, nameday_month: chara_details .chara_make .birth_month @@ -721,8 +722,16 @@ async fn main() { "creating character from: {name} {chara_make_json}" ); - let (content_id, actor_id) = - database.create_player_data(name, chara_make_json); + let chara_make = CharaMake::from_json(&chara_make_json); + + let city_state = + get_citystate(chara_make.classjob_id as u16); + + let (content_id, actor_id) = database.create_player_data( + name, + chara_make_json, + city_state, + ); tracing::info!( "Created new player: {content_id} {actor_id}" diff --git a/src/common/mod.rs b/src/common/mod.rs index 98b2757..092d97e 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -63,3 +63,23 @@ pub fn get_world_name(world_id: u16) -> String { name.clone() } + +/// Gets the starting city-state from a given class/job id. +pub fn get_citystate(classjob_id: u16) -> 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("ClassJob").unwrap(); + let exd = game_data + .read_excel_sheet("ClassJob", &exh, Language::English, 0) + .unwrap(); + + let world_row = &exd.read_row(&exh, classjob_id as u32).unwrap()[0]; + + let physis::exd::ColumnData::UInt8(town_id) = &world_row.data[33] else { + panic!("Unexpected type!"); + }; + + *town_id +} diff --git a/src/lobby/chara_make.rs b/src/lobby/chara_make.rs index 550f06e..a0daa50 100644 --- a/src/lobby/chara_make.rs +++ b/src/lobby/chara_make.rs @@ -5,12 +5,12 @@ use crate::common::CustomizeData; #[derive(Debug)] pub struct CharaMake { pub customize: CustomizeData, - pub unk1: i32, // always 1? + pub unk1: i32, pub guardian: i32, pub birth_month: i32, - pub classjob: i32, pub birth_day: i32, - pub unk6: i32, // always 1? + pub classjob_id: i32, + pub unk2: i32, } impl CharaMake { @@ -23,9 +23,9 @@ impl CharaMake { unk1: content[1].as_str().unwrap().parse::().unwrap(), guardian: content[2].as_str().unwrap().parse::().unwrap(), birth_month: content[3].as_str().unwrap().parse::().unwrap(), - classjob: content[4].as_str().unwrap().parse::().unwrap(), - birth_day: content[5].as_str().unwrap().parse::().unwrap(), - unk6: content[6].as_str().unwrap().parse::().unwrap(), + birth_day: content[4].as_str().unwrap().parse::().unwrap(), + classjob_id: content[5].as_str().unwrap().parse::().unwrap(), + unk2: content[6].as_str().unwrap().parse::().unwrap(), } } @@ -35,9 +35,9 @@ impl CharaMake { self.unk1, self.guardian, self.birth_month, - self.classjob, self.birth_day, - self.unk6, + self.classjob_id, + self.unk2, ]); let obj = json!({ @@ -60,7 +60,7 @@ mod tests { let chara_make = CharaMake::from_json(json); assert_eq!(chara_make.customize.gender, 0); - assert_eq!(chara_make.classjob, 1); + assert_eq!(chara_make.unk1, 1); // TODO: add more asserts } diff --git a/src/lobby/connection.rs b/src/lobby/connection.rs index e362435..a24b834 100644 --- a/src/lobby/connection.rs +++ b/src/lobby/connection.rs @@ -9,6 +9,7 @@ use crate::{ get_world_name, timestamp_secs, }, config::get_config, + lobby::CharaMake, oodle::OodleNetwork, packet::{ CompressionType, ConnectionType, PacketSegment, PacketState, SegmentType, @@ -207,8 +208,6 @@ impl LobbyConnection { let mut characters = characters.to_vec(); - dbg!(&characters); - for i in 0..4 { let mut characters_in_packet = Vec::new(); for _ in 0..min(characters.len(), 2) { @@ -429,6 +428,8 @@ impl LobbyConnection { let our_actor_id; let our_content_id; + dbg!(CharaMake::from_json(&character_action.json)); + // tell the world server to create this character { let ipc_segment = CustomIpcSegment { diff --git a/src/world/database.rs b/src/world/database.rs index f340830..f7dead8 100644 --- a/src/world/database.rs +++ b/src/world/database.rs @@ -16,6 +16,7 @@ pub struct WorldDatabase { pub struct CharacterData { pub name: String, pub chara_make: CharaMake, // probably not the ideal way to store this? + pub city_state: u8, } impl WorldDatabase { @@ -30,7 +31,7 @@ impl WorldDatabase { // Create characters data table { - let query = "CREATE TABLE IF NOT EXISTS character_data (content_id INTEGER PRIMARY KEY, name STRING, chara_make STRING);"; + let query = "CREATE TABLE IF NOT EXISTS character_data (content_id INTEGER PRIMARY KEY, name STRING, chara_make STRING, city_state INTEGER);"; connection.execute(query, ()).unwrap(); } @@ -165,7 +166,7 @@ impl WorldDatabase { } /// Gives (content_id, actor_id) - pub fn create_player_data(&self, name: &str, chara_make: &str) -> (u64, u32) { + pub fn create_player_data(&self, name: &str, chara_make: &str, city_state: u8) -> (u64, u32) { let content_id = Self::generate_content_id(); let actor_id = Self::generate_actor_id(); @@ -182,8 +183,8 @@ impl WorldDatabase { // insert char data connection .execute( - "INSERT INTO character_data VALUES (?1, ?2, ?3);", - (content_id, name, chara_make), + "INSERT INTO character_data VALUES (?1, ?2, ?3, ?4);", + (content_id, name, chara_make, city_state), ) .unwrap(); @@ -205,15 +206,20 @@ impl WorldDatabase { let connection = self.connection.lock().unwrap(); let mut stmt = connection - .prepare("SELECT name, chara_make FROM character_data WHERE content_id = ?1") + .prepare( + "SELECT name, chara_make, city_state FROM character_data WHERE content_id = ?1", + ) .unwrap(); - let (name, chara_make_json): (String, String) = stmt - .query_row((content_id,), |row| Ok((row.get(0)?, row.get(1)?))) + let (name, chara_make_json, city_state): (String, String, u8) = stmt + .query_row((content_id,), |row| { + Ok((row.get(0)?, row.get(1)?, row.get(2)?)) + }) .unwrap(); CharacterData { name, chara_make: CharaMake::from_json(&chara_make_json), + city_state, } }