From 640aeb797cc3a4a19dea29a16596c05dccb86c66 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Fri, 14 Mar 2025 00:30:37 -0400 Subject: [PATCH] Fix a few problems with ClientSelectData, use the same character in both servers I apparently had a few fields missing here. Also just some general clean up, make it the same looking character in both the Lobby and World server. I also chose Gridania as the test zone. --- src/bin/kawari-lobby.rs | 34 ++---------- src/bin/kawari-world.rs | 54 ++++++++----------- src/client_select_data.rs | 108 ++++++++++++++++++++------------------ src/ipc.rs | 7 +++ src/lib.rs | 34 +++++++++++- src/world/mod.rs | 1 - src/world/player_spawn.rs | 33 +----------- 7 files changed, 122 insertions(+), 149 deletions(-) diff --git a/src/bin/kawari-lobby.rs b/src/bin/kawari-lobby.rs index c360de1..b8cdeb7 100644 --- a/src/bin/kawari-lobby.rs +++ b/src/bin/kawari-lobby.rs @@ -13,7 +13,7 @@ use kawari::oodle::FFXIVOodle; use kawari::packet::{ CompressionType, PacketSegment, SegmentType, State, parse_packet, send_keep_alive, send_packet, }; -use kawari::{CONTENT_ID, WORLD_ID, WORLD_NAME, ZONE_ID}; +use kawari::{CHAR_NAME, CONTENT_ID, CUSTOMIZE_DATA, WORLD_ID, WORLD_NAME, ZONE_ID}; use tokio::io::{AsyncReadExt, WriteHalf}; use tokio::net::{TcpListener, TcpStream}; @@ -396,33 +396,7 @@ async fn send_lobby_info(socket: &mut WriteHalf, state: &mut State, s unk9: 0, zone_id: ZONE_ID as i32, unk11: 0, - customize: ClientCustomizeData { - race: 3, - gender: 1, - height: 0, - subrace: 0, - face: 1, - hair: 1, - enable_highlights: 1, - skin_tone: 1, - right_eye_color: 1, - hair_tone: 1, - highlights: 1, - facial_features: 1, - facial_feature_color: 1, - eyebrows: 1, - left_eye_color: 1, - eyes: 1, - nose: 1, - jaw: 1, - mouth: 1, - lips_tone_fur_pattern: 1, - race_feature_size: 1, - race_feature_type: 1, - bust: 0, - face_paint: 1, - face_paint_color: 0, - }, + customize: CUSTOMIZE_DATA, unk12: 0, unk13: 0, unk14: [0; 10], @@ -432,7 +406,7 @@ async fn send_lobby_info(socket: &mut WriteHalf, state: &mut State, s unk18: 0, unk19: 0, unk20: 0, - unk21: "hello".to_string(), + unk21: String::new(), unk22: 0, unk23: 0, }; @@ -444,7 +418,7 @@ async fn send_lobby_info(socket: &mut WriteHalf, state: &mut State, s unk1: [0; 16], origin_server_id: WORLD_ID, current_server_id: WORLD_ID, - character_name: "test".to_string(), + character_name: CHAR_NAME.to_string(), origin_server_name: WORLD_NAME.to_string(), current_server_name: WORLD_NAME.to_string(), character_detail_json: select_data.to_json(), diff --git a/src/bin/kawari-world.rs b/src/bin/kawari-world.rs index 60f3b19..f854388 100644 --- a/src/bin/kawari-world.rs +++ b/src/bin/kawari-world.rs @@ -7,10 +7,10 @@ use kawari::packet::{ CompressionType, PacketSegment, SegmentType, State, parse_packet, send_keep_alive, send_packet, }; use kawari::world::{ - ActorControlSelf, ActorControlType, CustomizeData, InitZone, PlayerSetup, PlayerSpawn, - PlayerStats, Position, UpdateClassInfo, + ActorControlSelf, ActorControlType, InitZone, PlayerSetup, PlayerSpawn, PlayerStats, Position, + UpdateClassInfo, }; -use kawari::{CONTENT_ID, WORLD_ID, ZONE_ID}; +use kawari::{CHAR_NAME, CONTENT_ID, CUSTOMIZE_DATA, WORLD_ID, ZONE_ID}; use tokio::io::AsyncReadExt; use tokio::net::TcpListener; @@ -268,7 +268,7 @@ async fn main() { content_id: CONTENT_ID, exp: [10000; 32], levels: [100; 32], - name: "KAWARI".to_string(), + name: CHAR_NAME.to_string(), ..Default::default() }), }; @@ -499,7 +499,7 @@ async fn main() { home_world_id: WORLD_ID, title: 1, class_job: 35, - name: "Test".to_string(), + name: CHAR_NAME.to_string(), hp_curr: 100, hp_max: 100, mp_curr: 100, @@ -508,35 +508,20 @@ async fn main() { spawn_index: 1, state: 1, gm_rank: 3, - look: CustomizeData { - race: 3, - age: 0, - gender: 1, - height: 5, - subrace: 0, - face: 1, - hair: 2, - enable_highlights: 0, - skin_tone: 4, - right_eye_color: 5, - hair_tone: 2, - highlights: 7, - facial_features: 1, - facial_feature_color: 1, - eyebrows: 2, - left_eye_color: 1, - eyes: 1, - nose: 0, - jaw: 1, - mouth: 0, - lips_tone_fur_pattern: 1, - race_feature_size: 1, - race_feature_type: 1, - bust: 0, - face_paint: 1, - face_paint_color: 1, - }, + look: CUSTOMIZE_DATA, fc_tag: "LOCAL".to_string(), + models: [ + 0, // head + 89, // body + 89, // hands + 89, // legs + 89, // feet + 0, // ears + 0, // neck + 0, // wrists + 0, // left finger + 0, // right finger + ], ..Default::default() }), }; @@ -731,6 +716,9 @@ async fn main() { } } } + IPCStructData::Unk12 { .. } => { + tracing::info!("Recieved Unk12!"); + } _ => panic!( "The server is recieving a IPC response or unknown packet!" ), diff --git a/src/client_select_data.rs b/src/client_select_data.rs index ed32fdc..dc5235a 100644 --- a/src/client_select_data.rs +++ b/src/client_select_data.rs @@ -4,31 +4,32 @@ use serde_json::{Value, json}; #[binrw] #[derive(Debug, Clone, Default)] pub struct ClientCustomizeData { - pub race: i32, - pub gender: i32, - pub height: i32, - pub subrace: i32, - pub face: i32, - pub hair: i32, - pub enable_highlights: i32, - pub skin_tone: i32, - pub right_eye_color: i32, - pub hair_tone: i32, - pub highlights: i32, - pub facial_features: i32, - pub facial_feature_color: i32, - pub eyebrows: i32, - pub left_eye_color: i32, - pub eyes: i32, - pub nose: i32, - pub jaw: i32, - pub mouth: i32, - pub lips_tone_fur_pattern: i32, - pub race_feature_size: i32, - pub race_feature_type: i32, - pub bust: i32, - pub face_paint: i32, - pub face_paint_color: i32, + pub race: u8, + pub gender: u8, + pub age: u8, + pub height: u8, + pub subrace: u8, + pub face: u8, + pub hair: u8, + pub enable_highlights: u8, + pub skin_tone: u8, + pub right_eye_color: u8, + pub hair_tone: u8, + pub highlights: u8, + pub facial_features: u8, + pub facial_feature_color: u8, + pub eyebrows: u8, + pub left_eye_color: u8, + pub eyes: u8, + pub nose: u8, + pub jaw: u8, + pub mouth: u8, + pub lips_tone_fur_pattern: u8, + pub race_feature_size: u8, + pub race_feature_type: u8, + pub bust: u8, + pub face_paint: u8, + pub face_paint_color: u8, } impl ClientCustomizeData { @@ -36,9 +37,11 @@ impl ClientCustomizeData { json!([ self.race.to_string(), self.gender.to_string(), + self.age.to_string(), self.height.to_string(), self.subrace.to_string(), self.face.to_string(), + self.hair.to_string(), self.enable_highlights.to_string(), self.skin_tone.to_string(), self.right_eye_color.to_string(), @@ -63,31 +66,32 @@ impl ClientCustomizeData { pub fn from_json(json: &Value) -> Self { Self { - race: json[0].as_str().unwrap().parse::().unwrap(), - gender: json[1].as_str().unwrap().parse::().unwrap(), - height: json[2].as_str().unwrap().parse::().unwrap(), - subrace: json[3].as_str().unwrap().parse::().unwrap(), - face: json[4].as_str().unwrap().parse::().unwrap(), - hair: json[5].as_str().unwrap().parse::().unwrap(), - enable_highlights: json[6].as_str().unwrap().parse::().unwrap(), - skin_tone: json[7].as_str().unwrap().parse::().unwrap(), - right_eye_color: json[8].as_str().unwrap().parse::().unwrap(), - hair_tone: json[9].as_str().unwrap().parse::().unwrap(), - highlights: json[10].as_str().unwrap().parse::().unwrap(), - facial_features: json[11].as_str().unwrap().parse::().unwrap(), - facial_feature_color: json[12].as_str().unwrap().parse::().unwrap(), - eyebrows: json[13].as_str().unwrap().parse::().unwrap(), - left_eye_color: json[14].as_str().unwrap().parse::().unwrap(), - eyes: json[15].as_str().unwrap().parse::().unwrap(), - nose: json[16].as_str().unwrap().parse::().unwrap(), - jaw: json[17].as_str().unwrap().parse::().unwrap(), - mouth: json[18].as_str().unwrap().parse::().unwrap(), - lips_tone_fur_pattern: json[19].as_str().unwrap().parse::().unwrap(), - race_feature_size: json[20].as_str().unwrap().parse::().unwrap(), - race_feature_type: json[21].as_str().unwrap().parse::().unwrap(), - bust: json[22].as_str().unwrap().parse::().unwrap(), - face_paint: json[23].as_str().unwrap().parse::().unwrap(), - face_paint_color: json[24].as_str().unwrap().parse::().unwrap(), + race: json[0].as_str().unwrap().parse::().unwrap(), + gender: json[1].as_str().unwrap().parse::().unwrap(), + height: json[2].as_str().unwrap().parse::().unwrap(), + subrace: json[3].as_str().unwrap().parse::().unwrap(), + face: json[4].as_str().unwrap().parse::().unwrap(), + hair: json[5].as_str().unwrap().parse::().unwrap(), + enable_highlights: json[6].as_str().unwrap().parse::().unwrap(), + skin_tone: json[7].as_str().unwrap().parse::().unwrap(), + right_eye_color: json[8].as_str().unwrap().parse::().unwrap(), + hair_tone: json[9].as_str().unwrap().parse::().unwrap(), + highlights: json[10].as_str().unwrap().parse::().unwrap(), + facial_features: json[11].as_str().unwrap().parse::().unwrap(), + facial_feature_color: json[12].as_str().unwrap().parse::().unwrap(), + eyebrows: json[13].as_str().unwrap().parse::().unwrap(), + left_eye_color: json[14].as_str().unwrap().parse::().unwrap(), + eyes: json[15].as_str().unwrap().parse::().unwrap(), + nose: json[16].as_str().unwrap().parse::().unwrap(), + jaw: json[17].as_str().unwrap().parse::().unwrap(), + mouth: json[18].as_str().unwrap().parse::().unwrap(), + lips_tone_fur_pattern: json[19].as_str().unwrap().parse::().unwrap(), + race_feature_size: json[20].as_str().unwrap().parse::().unwrap(), + race_feature_type: json[21].as_str().unwrap().parse::().unwrap(), + bust: json[22].as_str().unwrap().parse::().unwrap(), + face_paint: json[23].as_str().unwrap().parse::().unwrap(), + face_paint_color: json[24].as_str().unwrap().parse::().unwrap(), + age: 1, } } } @@ -109,7 +113,7 @@ pub struct ClientSelectData { pub customize: ClientCustomizeData, pub unk12: i32, pub unk13: i32, - pub unk14: [i32; 10], + pub unk14: [i32; 10], // probably model ids pub unk15: i32, pub unk16: i32, /// If set to 1, the user is granted one opportunity to edit their character and are prompted to re-choose their class. @@ -159,6 +163,6 @@ impl ClientSelectData { "classid": 116, }); - serde_json::to_string_pretty(&obj).unwrap() + serde_json::to_string(&obj).unwrap() } } diff --git a/src/ipc.rs b/src/ipc.rs index 36f6eb2..dfa902f 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -98,6 +98,8 @@ pub enum IPCOpCode { Unk11 = 0x156, // Assumed what this is, but probably incorrect CharacterCreated = 0xE, + // Unknown, client sends this for ??? + Unk12 = 0x0E9, } #[binrw] @@ -357,6 +359,10 @@ pub enum IPCStructData { #[br(dbg)] unk: [u8; 24], }, + #[br(pre_assert(*magic == IPCOpCode::Unk12))] + Unk12 { + unk: [u8; 8], // TODO: unknown + }, // Server->Client IPC #[br(pre_assert(false))] @@ -571,6 +577,7 @@ impl IPCSegment { IPCStructData::Unk11 { .. } => 32, IPCStructData::NameRejection { .. } => 536, IPCStructData::CharacterCreated { .. } => 2568, + IPCStructData::Unk12 { .. } => todo!(), } } } diff --git a/src/lib.rs b/src/lib.rs index 3d6a4db..388f5c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +use client_select_data::ClientCustomizeData; use minijinja::Environment; use rand::Rng; use rand::distributions::Alphanumeric; @@ -20,13 +21,44 @@ pub mod world; pub const WORLD_ID: u16 = 63; pub const WORLD_NAME: &str = "KAWARI"; -pub const ZONE_ID: u16 = 1255; +pub const ZONE_ID: u16 = 132; pub const CONTENT_ID: u64 = 11111111111111111; +pub const CUSTOMIZE_DATA: ClientCustomizeData = ClientCustomizeData { + race: 4, + gender: 1, + age: 1, + height: 50, + subrace: 7, + face: 3, + hair: 5, + enable_highlights: 0, + skin_tone: 10, + right_eye_color: 75, + hair_tone: 50, + highlights: 0, + facial_features: 1, + facial_feature_color: 19, + eyebrows: 1, + left_eye_color: 75, + eyes: 1, + nose: 0, + jaw: 1, + mouth: 1, + lips_tone_fur_pattern: 169, + race_feature_size: 100, + race_feature_type: 1, + bust: 100, + face_paint: 0, + face_paint_color: 167, +}; + /// Maxmimum length of a character's name. pub const CHAR_NAME_MAX_LENGTH: usize = 32; +pub const CHAR_NAME: &str = "Test User"; + pub fn generate_sid() -> String { let random_id: String = rand::thread_rng() .sample_iter(&Alphanumeric) diff --git a/src/world/mod.rs b/src/world/mod.rs index 4494798..7535a14 100644 --- a/src/world/mod.rs +++ b/src/world/mod.rs @@ -1,5 +1,4 @@ mod player_spawn; -pub use player_spawn::CustomizeData; pub use player_spawn::PlayerSpawn; mod position; diff --git a/src/world/player_spawn.rs b/src/world/player_spawn.rs index 1e99fe8..023b526 100644 --- a/src/world/player_spawn.rs +++ b/src/world/player_spawn.rs @@ -88,7 +88,7 @@ pub struct PlayerSpawn { #[br(map = read_string)] #[bw(map = write_string)] pub name: String, - pub look: CustomizeData, + pub look: ClientCustomizeData, #[br(count = 6)] #[bw(pad_size_to = 6)] #[br(map = read_string)] @@ -97,37 +97,6 @@ pub struct PlayerSpawn { pub padding: [u8; 2], } -#[binrw] -#[derive(Debug, Clone, Default)] -pub struct CustomizeData { - pub race: u8, - pub gender: u8, - pub age: u8, - pub height: u8, - pub subrace: u8, - pub face: u8, - pub hair: u8, - pub enable_highlights: u8, - pub skin_tone: u8, - pub right_eye_color: u8, - pub hair_tone: u8, - pub highlights: u8, - pub facial_features: u8, - pub facial_feature_color: u8, - pub eyebrows: u8, - pub left_eye_color: u8, - pub eyes: u8, - pub nose: u8, - pub jaw: u8, - pub mouth: u8, - pub lips_tone_fur_pattern: u8, - pub race_feature_size: u8, - pub race_feature_type: u8, - pub bust: u8, - pub face_paint: u8, - pub face_paint_color: u8, -} - #[cfg(test)] mod tests { use std::{fs::read, io::Cursor, path::PathBuf};