From 7315ece19488488f031e0a004281ade6e18693b4 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Sun, 16 Mar 2025 18:15:19 -0400 Subject: [PATCH] Move some larger Lobby IPC structs to their own files, add more tests for these Also fix a bunch of mistakes when calculating size, and so on. --- src/bin/kawari-lobby.rs | 8 +- src/lobby/connection.rs | 29 +++--- src/lobby/ipc/character_list.rs | 29 ++++++ src/lobby/ipc/mod.rs | 145 +++++++++----------------- src/lobby/ipc/server_list.rs | 13 +++ src/lobby/ipc/service_account_list.rs | 14 +++ src/packet/ipc.rs | 2 + 7 files changed, 129 insertions(+), 111 deletions(-) diff --git a/src/bin/kawari-lobby.rs b/src/bin/kawari-lobby.rs index 930017c..d0bbda6 100644 --- a/src/bin/kawari-lobby.rs +++ b/src/bin/kawari-lobby.rs @@ -120,8 +120,8 @@ async fn main() { server_id: 0, timestamp: 0, data: ServerLobbyIpcData::CharacterCreated { - unk1: 0x4, - unk2: 0x00010101, + sequence: 0x4, // TODO: haha no + unk: 0x00010101, details: CharacterDetails { content_id: CONTENT_ID, character_name: name.clone(), @@ -160,8 +160,8 @@ async fn main() { server_id: 0, timestamp: 0, data: ServerLobbyIpcData::CharacterCreated { - unk1: 0x5, - unk2: 0x00020101, + sequence: 0x5, + unk: 0x00020101, details: CharacterDetails { id: 0x07369f3a, // notice that we give them an id now content_id: CONTENT_ID, diff --git a/src/lobby/connection.rs b/src/lobby/connection.rs index 8edfc4b..55eb466 100644 --- a/src/lobby/connection.rs +++ b/src/lobby/connection.rs @@ -16,8 +16,8 @@ use crate::{ use super::{ client_select_data::ClientSelectData, ipc::{ - CharacterDetails, Server, ServerLobbyIpcData, ServerLobbyIpcSegment, ServerLobbyIpcType, - ServiceAccount, + CharacterDetails, LobbyCharacterList, LobbyServerList, LobbyServiceAccountList, Server, + ServerLobbyIpcData, ServerLobbyIpcSegment, ServerLobbyIpcType, ServiceAccount, }, }; use crate::lobby::ipc::ClientLobbyIpcSegment; @@ -74,13 +74,14 @@ impl LobbyConnection { }] .to_vec(); - let service_account_list = ServerLobbyIpcData::LobbyServiceAccountList { - sequence: 0, - num_service_accounts: service_accounts.len() as u8, - unk1: 3, - unk2: 0x99, - service_accounts: service_accounts.to_vec(), - }; + let service_account_list = + ServerLobbyIpcData::LobbyServiceAccountList(LobbyServiceAccountList { + sequence: 0, + num_service_accounts: service_accounts.len() as u8, + unk1: 3, + unk2: 0x99, + service_accounts: service_accounts.to_vec(), + }); let ipc = ServerLobbyIpcSegment { unk1: 0, @@ -114,13 +115,13 @@ impl LobbyConnection { // add any empty boys servers.resize(6, Server::default()); - let lobby_server_list = ServerLobbyIpcData::LobbyServerList { + let lobby_server_list = ServerLobbyIpcData::LobbyServerList(LobbyServerList { sequence: 0, unk1: 1, offset: 0, num_servers: 1, servers, - }; + }); let ipc = ServerLobbyIpcSegment { unk1: 0, @@ -223,7 +224,7 @@ impl LobbyConnection { let lobby_character_list = if i == 3 { // On the last packet, add the account-wide information - ServerLobbyIpcData::LobbyCharacterList { + LobbyCharacterList { sequence, counter: (i * 4) + 1, // TODO: why the + 1 here? num_in_packet: characters_in_packet.len() as u8, @@ -244,7 +245,7 @@ impl LobbyConnection { characters: characters_in_packet, } } else { - ServerLobbyIpcData::LobbyCharacterList { + LobbyCharacterList { sequence, counter: i * 4, num_in_packet: characters_in_packet.len() as u8, @@ -272,7 +273,7 @@ impl LobbyConnection { op_code: ServerLobbyIpcType::LobbyCharacterList, server_id: 0, timestamp: timestamp_secs(), - data: lobby_character_list, + data: ServerLobbyIpcData::LobbyCharacterList(lobby_character_list), }; self.send_segment(PacketSegment { diff --git a/src/lobby/ipc/character_list.rs b/src/lobby/ipc/character_list.rs index 93e99e5..9ebaaf1 100644 --- a/src/lobby/ipc/character_list.rs +++ b/src/lobby/ipc/character_list.rs @@ -37,3 +37,32 @@ pub struct CharacterDetails { pub character_detail_json: String, pub unk2: [u8; 20], } + +#[binrw] +#[derive(Debug, Clone, Default)] +pub struct LobbyCharacterList { + pub sequence: u64, + pub counter: u8, + #[brw(pad_after = 2)] + pub num_in_packet: u8, + pub unk1: u8, + pub unk2: u8, + pub unk3: u8, + /// Set to 128 if legacy character + pub unk4: u8, + pub unk5: [u32; 7], + pub unk6: u8, + pub veteran_rank: u8, + #[brw(pad_after = 1)] + pub unk7: u8, + pub days_subscribed: u32, + pub remaining_days: u32, + pub days_to_next_rank: u32, + pub max_characters_on_world: u16, + pub unk8: u16, + #[brw(pad_after = 12)] + pub entitled_expansion: u32, + #[br(count = 2)] + #[brw(pad_size_to = (1196 * 2))] + pub characters: Vec, +} diff --git a/src/lobby/ipc/mod.rs b/src/lobby/ipc/mod.rs index bec883d..a1b6be7 100644 --- a/src/lobby/ipc/mod.rs +++ b/src/lobby/ipc/mod.rs @@ -4,15 +4,15 @@ mod character_action; pub use character_action::LobbyCharacterAction; mod character_list; -pub use character_list::CharacterDetails; +pub use character_list::{CharacterDetails, LobbyCharacterList}; mod client_version_info; mod server_list; -pub use server_list::Server; +pub use server_list::{LobbyServerList, Server}; mod service_account_list; -pub use service_account_list::ServiceAccount; +pub use service_account_list::{LobbyServiceAccountList, ServiceAccount}; use crate::{ CHAR_NAME_MAX_LENGTH, @@ -53,10 +53,10 @@ impl IpcSegmentTrait for ServerLobbyIpcSegment { // 16 is the size of the IPC header 16 + match self.op_code { ServerLobbyIpcType::LobbyError => 536, - ServerLobbyIpcType::LobbyServiceAccountList => 24 + (8 * 80), - ServerLobbyIpcType::LobbyCharacterList => 80 + (2 * 1184), + ServerLobbyIpcType::LobbyServiceAccountList => 656, + ServerLobbyIpcType::LobbyCharacterList => 2472, ServerLobbyIpcType::LobbyEnterWorld => 160, - ServerLobbyIpcType::LobbyServerList => 24 + (6 * 84), + ServerLobbyIpcType::LobbyServerList => 528, ServerLobbyIpcType::LobbyRetainerList => 210, ServerLobbyIpcType::CharacterCreated => 2568, } @@ -178,59 +178,15 @@ pub enum ClientLobbyIpcData { #[br(import(_magic: &ServerLobbyIpcType))] #[derive(Debug, Clone)] pub enum ServerLobbyIpcData { - LobbyServiceAccountList { - #[br(dbg)] - sequence: u64, - #[brw(pad_before = 1)] - num_service_accounts: u8, - unk1: u8, - #[brw(pad_after = 4)] - unk2: u8, - #[br(count = 8)] - service_accounts: Vec, - }, - LobbyServerList { - sequence: u64, - unk1: u16, - offset: u16, - #[brw(pad_after = 8)] - num_servers: u32, - #[br(count = 6)] - #[brw(pad_size_to = 504)] - servers: Vec, - }, + LobbyServiceAccountList(LobbyServiceAccountList), + LobbyServerList(LobbyServerList), LobbyRetainerList { // TODO: what is in here? #[brw(pad_before = 7)] #[brw(pad_after = 202)] unk1: u8, }, - LobbyCharacterList { - sequence: u64, - counter: u8, - #[brw(pad_after = 2)] - num_in_packet: u8, - unk1: u8, - unk2: u8, - unk3: u8, - /// Set to 128 if legacy character - unk4: u8, - unk5: [u32; 7], - unk6: u8, - veteran_rank: u8, - #[brw(pad_after = 1)] - unk7: u8, - days_subscribed: u32, - remaining_days: u32, - days_to_next_rank: u32, - max_characters_on_world: u16, - unk8: u16, - #[brw(pad_after = 12)] - entitled_expansion: u32, - #[br(count = 2)] - #[brw(pad_size_to = 2368)] - characters: Vec, - }, + LobbyCharacterList(LobbyCharacterList), LobbyEnterWorld { sequence: u64, character_id: u32, @@ -243,8 +199,9 @@ pub enum ServerLobbyIpcData { #[bw(map = write_string)] session_id: String, port: u16, - #[brw(pad_after = 16)] + #[brw(pad_after = 16)] // garbage? #[br(count = 48)] + #[brw(pad_size_to = 48)] #[br(map = read_string)] #[bw(map = write_string)] host: String, @@ -254,24 +211,14 @@ pub enum ServerLobbyIpcData { error: u32, value: u32, exd_error_id: u16, + #[brw(pad_after = 516)] // empty and garbage unk1: u16, }, - 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, - }, CharacterCreated { - #[brw(pad_after = 4)] // empty - unk1: u32, - #[brw(pad_after = 4)] // empty - unk2: u32, - #[brw(pad_before = 32)] // empty - #[brw(pad_after = 1136)] // empty + sequence: u64, + unk: u32, + #[brw(pad_before = 36)] // empty + #[brw(pad_after = 1336)] // empty and garbage details: CharacterDetails, }, } @@ -288,37 +235,49 @@ mod tests { #[test] fn lobby_ipc_sizes() { let ipc_types = [ + ( + ServerLobbyIpcType::LobbyServiceAccountList, + ServerLobbyIpcData::LobbyServiceAccountList(LobbyServiceAccountList::default()), + ), ( ServerLobbyIpcType::LobbyServerList, - ServerLobbyIpcData::LobbyServerList { - sequence: 0, - unk1: 0, - offset: 0, - num_servers: 0, - servers: Vec::new(), - }, + ServerLobbyIpcData::LobbyServerList(LobbyServerList::default()), + ), + ( + ServerLobbyIpcType::LobbyRetainerList, + ServerLobbyIpcData::LobbyRetainerList { unk1: 0 }, ), ( ServerLobbyIpcType::LobbyCharacterList, - ServerLobbyIpcData::LobbyCharacterList { + ServerLobbyIpcData::LobbyCharacterList(LobbyCharacterList::default()), + ), + ( + ServerLobbyIpcType::LobbyEnterWorld, + ServerLobbyIpcData::LobbyEnterWorld { sequence: 0, - counter: 0, - num_in_packet: 0, + character_id: 0, + content_id: 0, + session_id: String::new(), + port: 0, + host: String::new(), + }, + ), + ( + ServerLobbyIpcType::LobbyError, + ServerLobbyIpcData::LobbyError { + sequence: 0, + error: 0, + value: 0, + exd_error_id: 0, unk1: 0, - unk2: 0, - unk3: 0, - unk4: 0, - unk5: [0; 7], - unk6: 0, - veteran_rank: 0, - unk7: 0, - days_subscribed: 0, - remaining_days: 0, - days_to_next_rank: 0, - max_characters_on_world: 0, - unk8: 0, - entitled_expansion: 0, - characters: Vec::new(), + }, + ), + ( + ServerLobbyIpcType::CharacterCreated, + ServerLobbyIpcData::CharacterCreated { + sequence: 0, + unk: 0, + details: CharacterDetails::default(), }, ), ]; diff --git a/src/lobby/ipc/server_list.rs b/src/lobby/ipc/server_list.rs index 1af6456..8f54c61 100644 --- a/src/lobby/ipc/server_list.rs +++ b/src/lobby/ipc/server_list.rs @@ -17,3 +17,16 @@ pub struct Server { #[bw(map = write_string)] pub name: String, } + +#[binrw] +#[derive(Debug, Clone, Default)] +pub struct LobbyServerList { + pub sequence: u64, + pub unk1: u16, + pub offset: u16, + #[brw(pad_after = 8)] + pub num_servers: u32, + #[br(count = 6)] + #[brw(pad_size_to = 504)] + pub servers: Vec, +} diff --git a/src/lobby/ipc/service_account_list.rs b/src/lobby/ipc/service_account_list.rs index f25fe2d..f20b352 100644 --- a/src/lobby/ipc/service_account_list.rs +++ b/src/lobby/ipc/service_account_list.rs @@ -14,3 +14,17 @@ pub struct ServiceAccount { #[bw(map = write_string)] pub name: String, } + +#[binrw] +#[derive(Debug, Clone, Default)] +pub struct LobbyServiceAccountList { + pub sequence: u64, + #[brw(pad_before = 1)] + pub num_service_accounts: u8, + pub unk1: u8, + #[brw(pad_after = 4)] + pub unk2: u8, + #[br(count = 8)] + #[brw(pad_size_to = (8 * 80))] + pub service_accounts: Vec, +} diff --git a/src/packet/ipc.rs b/src/packet/ipc.rs index 76e6eec..8b5ced7 100644 --- a/src/packet/ipc.rs +++ b/src/packet/ipc.rs @@ -3,6 +3,8 @@ use binrw::{BinRead, BinWrite, binrw}; pub trait IpcSegmentTrait: for<'a> BinRead = ()> + for<'a> BinWrite = ()> + std::fmt::Debug + 'static { + /// Calculate the size of this Ipc segment *including* the 16 byte header. + /// When implementing this, please use the size as seen in retail. fn calc_size(&self) -> u32; }