1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-04-25 08:27:44 +00:00

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.
This commit is contained in:
Joshua Goins 2025-03-16 18:15:19 -04:00
parent f5d75301b2
commit 7315ece194
7 changed files with 129 additions and 111 deletions

View file

@ -120,8 +120,8 @@ async fn main() {
server_id: 0, server_id: 0,
timestamp: 0, timestamp: 0,
data: ServerLobbyIpcData::CharacterCreated { data: ServerLobbyIpcData::CharacterCreated {
unk1: 0x4, sequence: 0x4, // TODO: haha no
unk2: 0x00010101, unk: 0x00010101,
details: CharacterDetails { details: CharacterDetails {
content_id: CONTENT_ID, content_id: CONTENT_ID,
character_name: name.clone(), character_name: name.clone(),
@ -160,8 +160,8 @@ async fn main() {
server_id: 0, server_id: 0,
timestamp: 0, timestamp: 0,
data: ServerLobbyIpcData::CharacterCreated { data: ServerLobbyIpcData::CharacterCreated {
unk1: 0x5, sequence: 0x5,
unk2: 0x00020101, unk: 0x00020101,
details: CharacterDetails { details: CharacterDetails {
id: 0x07369f3a, // notice that we give them an id now id: 0x07369f3a, // notice that we give them an id now
content_id: CONTENT_ID, content_id: CONTENT_ID,

View file

@ -16,8 +16,8 @@ use crate::{
use super::{ use super::{
client_select_data::ClientSelectData, client_select_data::ClientSelectData,
ipc::{ ipc::{
CharacterDetails, Server, ServerLobbyIpcData, ServerLobbyIpcSegment, ServerLobbyIpcType, CharacterDetails, LobbyCharacterList, LobbyServerList, LobbyServiceAccountList, Server,
ServiceAccount, ServerLobbyIpcData, ServerLobbyIpcSegment, ServerLobbyIpcType, ServiceAccount,
}, },
}; };
use crate::lobby::ipc::ClientLobbyIpcSegment; use crate::lobby::ipc::ClientLobbyIpcSegment;
@ -74,13 +74,14 @@ impl LobbyConnection {
}] }]
.to_vec(); .to_vec();
let service_account_list = ServerLobbyIpcData::LobbyServiceAccountList { let service_account_list =
sequence: 0, ServerLobbyIpcData::LobbyServiceAccountList(LobbyServiceAccountList {
num_service_accounts: service_accounts.len() as u8, sequence: 0,
unk1: 3, num_service_accounts: service_accounts.len() as u8,
unk2: 0x99, unk1: 3,
service_accounts: service_accounts.to_vec(), unk2: 0x99,
}; service_accounts: service_accounts.to_vec(),
});
let ipc = ServerLobbyIpcSegment { let ipc = ServerLobbyIpcSegment {
unk1: 0, unk1: 0,
@ -114,13 +115,13 @@ impl LobbyConnection {
// add any empty boys // add any empty boys
servers.resize(6, Server::default()); servers.resize(6, Server::default());
let lobby_server_list = ServerLobbyIpcData::LobbyServerList { let lobby_server_list = ServerLobbyIpcData::LobbyServerList(LobbyServerList {
sequence: 0, sequence: 0,
unk1: 1, unk1: 1,
offset: 0, offset: 0,
num_servers: 1, num_servers: 1,
servers, servers,
}; });
let ipc = ServerLobbyIpcSegment { let ipc = ServerLobbyIpcSegment {
unk1: 0, unk1: 0,
@ -223,7 +224,7 @@ impl LobbyConnection {
let lobby_character_list = if i == 3 { let lobby_character_list = if i == 3 {
// On the last packet, add the account-wide information // On the last packet, add the account-wide information
ServerLobbyIpcData::LobbyCharacterList { LobbyCharacterList {
sequence, sequence,
counter: (i * 4) + 1, // TODO: why the + 1 here? counter: (i * 4) + 1, // TODO: why the + 1 here?
num_in_packet: characters_in_packet.len() as u8, num_in_packet: characters_in_packet.len() as u8,
@ -244,7 +245,7 @@ impl LobbyConnection {
characters: characters_in_packet, characters: characters_in_packet,
} }
} else { } else {
ServerLobbyIpcData::LobbyCharacterList { LobbyCharacterList {
sequence, sequence,
counter: i * 4, counter: i * 4,
num_in_packet: characters_in_packet.len() as u8, num_in_packet: characters_in_packet.len() as u8,
@ -272,7 +273,7 @@ impl LobbyConnection {
op_code: ServerLobbyIpcType::LobbyCharacterList, op_code: ServerLobbyIpcType::LobbyCharacterList,
server_id: 0, server_id: 0,
timestamp: timestamp_secs(), timestamp: timestamp_secs(),
data: lobby_character_list, data: ServerLobbyIpcData::LobbyCharacterList(lobby_character_list),
}; };
self.send_segment(PacketSegment { self.send_segment(PacketSegment {

View file

@ -37,3 +37,32 @@ pub struct CharacterDetails {
pub character_detail_json: String, pub character_detail_json: String,
pub unk2: [u8; 20], 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<CharacterDetails>,
}

View file

@ -4,15 +4,15 @@ mod character_action;
pub use character_action::LobbyCharacterAction; pub use character_action::LobbyCharacterAction;
mod character_list; mod character_list;
pub use character_list::CharacterDetails; pub use character_list::{CharacterDetails, LobbyCharacterList};
mod client_version_info; mod client_version_info;
mod server_list; mod server_list;
pub use server_list::Server; pub use server_list::{LobbyServerList, Server};
mod service_account_list; mod service_account_list;
pub use service_account_list::ServiceAccount; pub use service_account_list::{LobbyServiceAccountList, ServiceAccount};
use crate::{ use crate::{
CHAR_NAME_MAX_LENGTH, CHAR_NAME_MAX_LENGTH,
@ -53,10 +53,10 @@ impl IpcSegmentTrait for ServerLobbyIpcSegment {
// 16 is the size of the IPC header // 16 is the size of the IPC header
16 + match self.op_code { 16 + match self.op_code {
ServerLobbyIpcType::LobbyError => 536, ServerLobbyIpcType::LobbyError => 536,
ServerLobbyIpcType::LobbyServiceAccountList => 24 + (8 * 80), ServerLobbyIpcType::LobbyServiceAccountList => 656,
ServerLobbyIpcType::LobbyCharacterList => 80 + (2 * 1184), ServerLobbyIpcType::LobbyCharacterList => 2472,
ServerLobbyIpcType::LobbyEnterWorld => 160, ServerLobbyIpcType::LobbyEnterWorld => 160,
ServerLobbyIpcType::LobbyServerList => 24 + (6 * 84), ServerLobbyIpcType::LobbyServerList => 528,
ServerLobbyIpcType::LobbyRetainerList => 210, ServerLobbyIpcType::LobbyRetainerList => 210,
ServerLobbyIpcType::CharacterCreated => 2568, ServerLobbyIpcType::CharacterCreated => 2568,
} }
@ -178,59 +178,15 @@ pub enum ClientLobbyIpcData {
#[br(import(_magic: &ServerLobbyIpcType))] #[br(import(_magic: &ServerLobbyIpcType))]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ServerLobbyIpcData { pub enum ServerLobbyIpcData {
LobbyServiceAccountList { LobbyServiceAccountList(LobbyServiceAccountList),
#[br(dbg)] LobbyServerList(LobbyServerList),
sequence: u64,
#[brw(pad_before = 1)]
num_service_accounts: u8,
unk1: u8,
#[brw(pad_after = 4)]
unk2: u8,
#[br(count = 8)]
service_accounts: Vec<ServiceAccount>,
},
LobbyServerList {
sequence: u64,
unk1: u16,
offset: u16,
#[brw(pad_after = 8)]
num_servers: u32,
#[br(count = 6)]
#[brw(pad_size_to = 504)]
servers: Vec<Server>,
},
LobbyRetainerList { LobbyRetainerList {
// TODO: what is in here? // TODO: what is in here?
#[brw(pad_before = 7)] #[brw(pad_before = 7)]
#[brw(pad_after = 202)] #[brw(pad_after = 202)]
unk1: u8, unk1: u8,
}, },
LobbyCharacterList { LobbyCharacterList(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<CharacterDetails>,
},
LobbyEnterWorld { LobbyEnterWorld {
sequence: u64, sequence: u64,
character_id: u32, character_id: u32,
@ -243,8 +199,9 @@ pub enum ServerLobbyIpcData {
#[bw(map = write_string)] #[bw(map = write_string)]
session_id: String, session_id: String,
port: u16, port: u16,
#[brw(pad_after = 16)] #[brw(pad_after = 16)] // garbage?
#[br(count = 48)] #[br(count = 48)]
#[brw(pad_size_to = 48)]
#[br(map = read_string)] #[br(map = read_string)]
#[bw(map = write_string)] #[bw(map = write_string)]
host: String, host: String,
@ -254,24 +211,14 @@ pub enum ServerLobbyIpcData {
error: u32, error: u32,
value: u32, value: u32,
exd_error_id: u16, exd_error_id: u16,
#[brw(pad_after = 516)] // empty and garbage
unk1: u16, 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 { CharacterCreated {
#[brw(pad_after = 4)] // empty sequence: u64,
unk1: u32, unk: u32,
#[brw(pad_after = 4)] // empty #[brw(pad_before = 36)] // empty
unk2: u32, #[brw(pad_after = 1336)] // empty and garbage
#[brw(pad_before = 32)] // empty
#[brw(pad_after = 1136)] // empty
details: CharacterDetails, details: CharacterDetails,
}, },
} }
@ -288,37 +235,49 @@ mod tests {
#[test] #[test]
fn lobby_ipc_sizes() { fn lobby_ipc_sizes() {
let ipc_types = [ let ipc_types = [
(
ServerLobbyIpcType::LobbyServiceAccountList,
ServerLobbyIpcData::LobbyServiceAccountList(LobbyServiceAccountList::default()),
),
( (
ServerLobbyIpcType::LobbyServerList, ServerLobbyIpcType::LobbyServerList,
ServerLobbyIpcData::LobbyServerList { ServerLobbyIpcData::LobbyServerList(LobbyServerList::default()),
sequence: 0, ),
unk1: 0, (
offset: 0, ServerLobbyIpcType::LobbyRetainerList,
num_servers: 0, ServerLobbyIpcData::LobbyRetainerList { unk1: 0 },
servers: Vec::new(),
},
), ),
( (
ServerLobbyIpcType::LobbyCharacterList, ServerLobbyIpcType::LobbyCharacterList,
ServerLobbyIpcData::LobbyCharacterList { ServerLobbyIpcData::LobbyCharacterList(LobbyCharacterList::default()),
),
(
ServerLobbyIpcType::LobbyEnterWorld,
ServerLobbyIpcData::LobbyEnterWorld {
sequence: 0, sequence: 0,
counter: 0, character_id: 0,
num_in_packet: 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, unk1: 0,
unk2: 0, },
unk3: 0, ),
unk4: 0, (
unk5: [0; 7], ServerLobbyIpcType::CharacterCreated,
unk6: 0, ServerLobbyIpcData::CharacterCreated {
veteran_rank: 0, sequence: 0,
unk7: 0, unk: 0,
days_subscribed: 0, details: CharacterDetails::default(),
remaining_days: 0,
days_to_next_rank: 0,
max_characters_on_world: 0,
unk8: 0,
entitled_expansion: 0,
characters: Vec::new(),
}, },
), ),
]; ];

View file

@ -17,3 +17,16 @@ pub struct Server {
#[bw(map = write_string)] #[bw(map = write_string)]
pub name: 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<Server>,
}

View file

@ -14,3 +14,17 @@ pub struct ServiceAccount {
#[bw(map = write_string)] #[bw(map = write_string)]
pub name: 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<ServiceAccount>,
}

View file

@ -3,6 +3,8 @@ use binrw::{BinRead, BinWrite, binrw};
pub trait IpcSegmentTrait: pub trait IpcSegmentTrait:
for<'a> BinRead<Args<'a> = ()> + for<'a> BinWrite<Args<'a> = ()> + std::fmt::Debug + 'static for<'a> BinRead<Args<'a> = ()> + for<'a> BinWrite<Args<'a> = ()> + 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; fn calc_size(&self) -> u32;
} }