From 4d4720b192bd9a1e132b90b877a392b8d318801f Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Thu, 13 Mar 2025 22:22:02 -0400 Subject: [PATCH] Add new character name rejection Next is being able to create a new character (but not really, because there is no persistent.) This is a good first step, though. --- src/bin/kawari-lobby.rs | 64 ++++++++++++++++++++++++++++++++++++++--- src/ipc.rs | 55 +++++++++++++++++++++++++---------- 2 files changed, 100 insertions(+), 19 deletions(-) diff --git a/src/bin/kawari-lobby.rs b/src/bin/kawari-lobby.rs index 61a4379..1da21ab 100644 --- a/src/bin/kawari-lobby.rs +++ b/src/bin/kawari-lobby.rs @@ -4,7 +4,10 @@ use std::time::{SystemTime, UNIX_EPOCH}; use kawari::blowfish::Blowfish; use kawari::client_select_data::{ClientCustomizeData, ClientSelectData}; use kawari::encryption::generate_encryption_key; -use kawari::ipc::{CharacterDetails, IPCOpCode, IPCSegment, IPCStructData, Server, ServiceAccount}; +use kawari::ipc::{ + CharacterDetails, IPCOpCode, IPCSegment, IPCStructData, LobbyCharacterAction, Server, + ServiceAccount, +}; use kawari::oodle::FFXIVOodle; use kawari::packet::{ CompressionType, PacketSegment, SegmentType, State, parse_packet, send_keep_alive, send_packet, @@ -65,9 +68,62 @@ async fn main() { send_lobby_info(&mut write, &mut state, *sequence).await; } - IPCStructData::LobbyCharacterAction { .. } => tracing::info!( - "Client is doing a character-related action in the lobby, but we don't support any yet! Ignoring..." - ), + IPCStructData::LobbyCharacterAction { + character_id, + character_index, + action, + world_id, + name, + .. + } => { + match action { + LobbyCharacterAction::ReserveName => { + tracing::info!( + "Player is requesting {name} as a new character name!" + ); + + // reject + { + let ipc = IPCSegment { + unk1: 0, + unk2: 0, + op_code: IPCOpCode::InitializeChat, // wrong but technically right + server_id: 0, + timestamp: 0, + data: IPCStructData::NameRejection { + unk1: 0x03, + unk2: 0x0bdb, + unk3: 0x000132cc, + }, + }; + + let response_packet = PacketSegment { + source_actor: 0x0, + target_actor: 0x0, + segment_type: SegmentType::Ipc { data: ipc }, + }; + send_packet( + &mut write, + &[response_packet], + &mut state, + CompressionType::Uncompressed, + ) + .await; + } + } + LobbyCharacterAction::Create => todo!(), + LobbyCharacterAction::Rename => todo!(), + LobbyCharacterAction::Delete => todo!(), + LobbyCharacterAction::Move => todo!(), + LobbyCharacterAction::RemakeRetainer => todo!(), + LobbyCharacterAction::RemakeChara => todo!(), + LobbyCharacterAction::SettingsUploadBegin => todo!(), + LobbyCharacterAction::SettingsUpload => todo!(), + LobbyCharacterAction::WorldVisit => todo!(), + LobbyCharacterAction::DataCenterToken => todo!(), + LobbyCharacterAction::Request => todo!(), + } + } IPCStructData::RequestEnterWorld { sequence, lookup_id, diff --git a/src/ipc.rs b/src/ipc.rs index 3d344ff..b9837f6 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -171,21 +171,32 @@ pub struct CharacterDetails { } #[binrw] -#[brw(repr = u8)] #[derive(Clone, PartialEq, Debug)] pub enum LobbyCharacterAction { - ReserveName = 0x1, - Create = 0x2, - Rename = 0x3, - Delete = 0x4, - Move = 0x5, - RemakeRetainer = 0x6, - RemakeChara = 0x7, - SettingsUploadBegin = 0x8, - SettingsUpload = 0xC, - WorldVisit = 0xE, - DataCenterToken = 0xF, - Request = 0x15, + #[brw(magic = 0x1u8)] + ReserveName, + #[brw(magic = 0x2u8)] + Create, + #[brw(magic = 0x3u8)] + Rename, + #[brw(magic = 0x4u8)] + Delete, + #[brw(magic = 0x5u8)] + Move, + #[brw(magic = 0x6u8)] + RemakeRetainer, + #[brw(magic = 0x7u8)] + RemakeChara, + #[brw(magic = 0x8u8)] + SettingsUploadBegin, + #[brw(magic = 0xCu8)] + SettingsUpload, + #[brw(magic = 0xEu8)] + WorldVisit, + #[brw(magic = 0xFu8)] + DataCenterToken, + #[brw(magic = 0x15u8)] + Request, } #[binrw] @@ -235,8 +246,11 @@ pub enum IPCStructData { #[br(map = read_string)] #[bw(map = write_string)] name: String, - // TODO: what else is in here? - // according to TemporalStatis, chara make data? (probably op specific) + #[bw(pad_size_to = 436)] + #[br(count = 436)] + #[br(map = read_string)] + #[bw(map = write_string)] + json: String, }, #[br(pre_assert(*magic == IPCOpCode::RequestEnterWorld))] RequestEnterWorld { @@ -469,6 +483,16 @@ pub enum IPCStructData { #[brw(pad_after = 24)] // empty bytes unk: u32, }, + #[br(pre_assert(false))] + NameRejection { + // FIXME: This is opcode 0x2, which is InitializeChat. We need to separate the lobby/zone IPC codes. + unk1: u8, + #[brw(pad_before = 7)] // empty + unk2: u16, + #[brw(pad_before = 6)] // empty + #[brw(pad_after = 516)] // mostly empty + unk3: u32, + }, } #[binrw] @@ -533,6 +557,7 @@ impl IPCSegment { IPCStructData::Unk9 { .. } => 24, IPCStructData::Unk10 { .. } => 8, IPCStructData::Unk11 { .. } => 32, + IPCStructData::NameRejection { .. } => 536, } } }