diff --git a/src/bin/kawari-world.rs b/src/bin/kawari-world.rs index f285cab..717a8eb 100644 --- a/src/bin/kawari-world.rs +++ b/src/bin/kawari-world.rs @@ -1,6 +1,6 @@ use std::time::{SystemTime, UNIX_EPOCH}; -use kawari::ipc::{ActorControlType, IPCOpCode, IPCSegment, IPCStructData, Position}; +use kawari::ipc::{ActorControlType, IPCOpCode, IPCSegment, IPCStructData, Position, StatusEffect}; use kawari::oodle::FFXIVOodle; use kawari::packet::{ CompressionType, PacketSegment, SegmentType, State, parse_packet, send_keep_alive, send_packet, @@ -38,6 +38,15 @@ async fn main() { println!("recieved {n} bytes..."); let (segments, connection_type) = parse_packet(&buf[..n], &mut state).await; for segment in &segments { + let timestamp_secs = || { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("Failed to get UNIX timestamp!") + .as_secs() + .try_into() + .unwrap() + }; + match &segment.segment_type { SegmentType::InitializeSession { player_id } => { state.player_id = Some(*player_id); @@ -147,15 +156,6 @@ async fn main() { "Client is now requesting zone information. Sending!" ); - let timestamp_secs = || { - SystemTime::now() - .duration_since(UNIX_EPOCH) - .expect("Failed to get UNIX timestamp!") - .as_secs() - .try_into() - .unwrap() - }; - // IPC Init(?) { let ipc = IPCSegment { @@ -502,19 +502,97 @@ async fn main() { ) .await; } + } + IPCStructData::FinishLoading { .. } => { + tracing::info!( + "Client has finished loading... spawning in!" + ); - // ????? - /*{ + // send player spawn + { let ipc = IPCSegment { unk1: 0, unk2: 0, - op_code: IPCOpCode::InitRequest, + op_code: IPCOpCode::PlayerSpawn, server_id: 0, timestamp: timestamp_secs(), - data: IPCStructData::InitResponse { - unk1: 0, - character_id: state.player_id.unwrap(), - unk2: 0, + data: IPCStructData::PlayerSpawn { + title: 0, + u1b: 0, + current_world_id: 0, + home_world_id: 0, + gm_rank: 0, + u3c: 0, + u4: 0, + online_status: 0, + pose: 0, + u5a: 0, + u5b: 0, + u5c: 0, + target_id: 0, + u6: 0, + u7: 0, + main_weapon_model: 0, + sec_weapon_model: 0, + craft_tool_model: 0, + u14: 0, + u15: 0, + b_npc_base: 0, + b_npc_name: 0, + u18: 0, + u19: 0, + director_id: 0, + owner_id: 0, + u22: 0, + padding4: [0; 16], + hp_max: 0, + hp_curr: 0, + display_flags: 0, + fate_id: 0, + mp_curr: 0, + mp_max: 0, + unk: 0, + model_chara: 0, + rotation: 0, + current_mount: 0, + active_minion: 0, + u23: 0, + u24: 0, + u25: 0, + u26: 0, + spawn_index: 0, + state: 0, + persistent_emote: 0, + model_type: 0, + subtype: 0, + voice: 0, + enemy_type: 0, + unk27: 0, + level: 0, + class_job: 0, + unk28: 0, + unk29: 0, + unk30: 0, + mount_head: 0, + mount_body: 0, + mount_feet: 0, + mount_color: 0, + scale: 0, + element_data: [0; 6], + padding2: [0; 12], + effect: [StatusEffect::default(); 30], + pos: Position { + x: 0.0, + y: 0.0, + z: 0.0, + }, + models: [0; 10], + unknown6_58: [0; 10], + padding3: [0; 7], + name: [0; 32], + look: [0; 26], + fc_tag: [0; 6], + padding: [0; 26], }, }; @@ -523,9 +601,41 @@ async fn main() { target_actor: state.player_id.unwrap(), segment_type: SegmentType::Ipc { data: ipc }, }; - send_packet(&mut write, &[response_packet], &mut state, CompressionType::Oodle) - .await; - }*/ + send_packet( + &mut write, + &[response_packet], + &mut state, + CompressionType::Oodle, + ) + .await; + } + } + IPCStructData::Unk1 { .. } => { + tracing::info!("Recieved Unk1!"); + } + IPCStructData::Unk2 { .. } => { + tracing::info!("Recieved Unk2!"); + } + IPCStructData::Unk3 { .. } => { + tracing::info!("Recieved Unk3!"); + } + IPCStructData::Unk4 { .. } => { + tracing::info!("Recieved Unk4!"); + } + IPCStructData::SetSearchInfoHandler { .. } => { + tracing::info!("Recieved SetSearchInfoHandler!"); + } + IPCStructData::Unk5 { .. } => { + tracing::info!("Recieved Unk5!"); + } + IPCStructData::Unk6 { .. } => { + tracing::info!("Recieved Unk6!"); + } + IPCStructData::Unk7 { .. } => { + tracing::info!("Recieved Unk7!"); + } + IPCStructData::UpdatePositionHandler { .. } => { + tracing::info!("Recieved UpdatePositionHandler!"); } _ => panic!( "The server is recieving a IPC response or unknown packet!" diff --git a/src/ipc.rs b/src/ipc.rs index 469b3ed..9d34078 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -43,6 +43,27 @@ pub enum IPCOpCode { PlayerSetup = 0x006B, // Sent by the server to setup class info UpdateClassInfo = 0x006A, + // Sent by the client when they're done loading and they need to be spawned in + FinishLoading = 0x397, // TODO: assumed + // Sent by the server to spawn the player in + PlayerSpawn = 0x1AB, + + // FIXME: 32 bytes of something from the client, not sure what yet + Unk1 = 0x37C, + // FIXME: 16 bytes of something from the client, not sure what yet + Unk2 = 0x1A1, + // FIXME: 8 bytes of something from the client, not sure what yet + Unk3 = 0x326, + // FIXME: 8 bytes of something from the client, not sure what yet + Unk4 = 0x143, + SetSearchInfoHandler = 0x3B2, // TODO: assumed, + // FIXME: 8 bytes of something from the client, not sure what yet + Unk5 = 0x2D0, + // FIXME: 8 bytes of something from the client, not sure what yet + Unk6 = 0x2E5, + // FIXME: 32 bytes of something from the client, not sure what yet + Unk7 = 0x2B5, + UpdatePositionHandler = 0x249, // TODO: assumed } #[binrw] @@ -141,6 +162,15 @@ pub enum ActorControlType { SetCharaGearParamUI = 0x260, } +#[binrw] +#[derive(Debug, Clone, Copy, Default)] +pub struct StatusEffect { + effect_id: u16, + param: u16, + duration: f32, + source_actor_id: u32, +} + #[binrw] #[br(import(magic: &IPCOpCode))] #[derive(Debug, Clone)] @@ -197,6 +227,56 @@ pub enum IPCStructData { #[br(dbg)] unk: [u8; 105], }, + #[br(pre_assert(*magic == IPCOpCode::FinishLoading))] + FinishLoading { + // TODO: full of possibly interesting information + unk: [u8; 72], + }, + #[br(pre_assert(*magic == IPCOpCode::Unk1))] + Unk1 { + // TODO: full of possibly interesting information + unk: [u8; 32], + }, + #[br(pre_assert(*magic == IPCOpCode::Unk2))] + Unk2 { + // TODO: full of possibly interesting information + unk: [u8; 16], + }, + #[br(pre_assert(*magic == IPCOpCode::Unk3))] + Unk3 { + // TODO: full of possibly interesting information + unk: [u8; 8], + }, + #[br(pre_assert(*magic == IPCOpCode::Unk4))] + Unk4 { + // TODO: full of possibly interesting information + unk: [u8; 8], + }, + #[br(pre_assert(*magic == IPCOpCode::SetSearchInfoHandler))] + SetSearchInfoHandler { + // TODO: full of possibly interesting information + unk: [u8; 8], + }, + #[br(pre_assert(*magic == IPCOpCode::Unk5))] + Unk5 { + // TODO: full of possibly interesting information + unk: [u8; 8], + }, + #[br(pre_assert(*magic == IPCOpCode::Unk6))] + Unk6 { + // TODO: full of possibly interesting information + unk: [u8; 8], + }, + #[br(pre_assert(*magic == IPCOpCode::Unk7))] + Unk7 { + // TODO: full of possibly interesting information + unk: [u8; 32], + }, + #[br(pre_assert(*magic == IPCOpCode::UpdatePositionHandler))] + UpdatePositionHandler { + // TODO: full of possibly interesting information + unk: [u8; 24], + }, // Server->Client IPC #[br(pre_assert(false))] @@ -488,6 +568,85 @@ pub enum IPCStructData { class_level: u16, role_actions: [u32; 10], }, + #[br(pre_assert(false))] + PlayerSpawn { + title: u16, + u1b: u16, + current_world_id: u16, + home_world_id: u16, + + gm_rank: u8, + u3c: u8, + u4: u8, + online_status: u8, + + pose: u8, + u5a: u8, + u5b: u8, + u5c: u8, + + target_id: u64, + u6: u32, + u7: u32, + main_weapon_model: u64, + sec_weapon_model: u64, + craft_tool_model: u64, + + u14: u32, + u15: u32, + b_npc_base: u32, + b_npc_name: u32, + u18: u32, + u19: u32, + director_id: u32, + owner_id: u32, + u22: u32, + padding4: [u8; 16], + hp_max: u32, + hp_curr: u32, + display_flags: u32, + fate_id: u16, + mp_curr: u16, + mp_max: u16, + unk: u16, + model_chara: u16, + rotation: u16, + current_mount: u16, + active_minion: u16, + u23: u8, + u24: u8, + u25: u8, + u26: u8, + spawn_index: u8, + state: u8, + persistent_emote: u8, + model_type: u8, + subtype: u8, + voice: u8, + enemy_type: u8, + unk27: u8, + level: u8, + class_job: u8, + unk28: u8, + unk29: u8, + unk30: u8, + mount_head: u8, + mount_body: u8, + mount_feet: u8, + mount_color: u8, + scale: u8, + element_data: [u8; 6], + padding2: [u8; 12], + effect: [StatusEffect; 30], + pos: Position, + models: [u32; 10], + unknown6_58: [u8; 10], + padding3: [u8; 7], + name: [u8; 32], + look: [u8; 26], + fc_tag: [u8; 6], + padding: [u8; 26], + }, } #[binrw] @@ -529,6 +688,17 @@ impl IPCSegment { IPCStructData::PlayerStats { .. } => 228, IPCStructData::PlayerSetup { .. } => 2544, IPCStructData::UpdateClassInfo { .. } => 48, + IPCStructData::FinishLoading { .. } => todo!(), + IPCStructData::PlayerSpawn { .. } => 656, + IPCStructData::Unk1 { .. } => todo!(), + IPCStructData::Unk2 { .. } => todo!(), + IPCStructData::Unk3 { .. } => todo!(), + IPCStructData::Unk4 { .. } => todo!(), + IPCStructData::SetSearchInfoHandler { .. } => todo!(), + IPCStructData::Unk5 { .. } => todo!(), + IPCStructData::Unk6 { .. } => todo!(), + IPCStructData::Unk7 { .. } => todo!(), + IPCStructData::UpdatePositionHandler { .. } => todo!(), } } }