1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-07-10 07:57:46 +00:00

Fill out more unknowns in lobby IPC packets

Mostly based off of Sapphire, but fixed in the more modern age.
This commit is contained in:
Joshua Goins 2025-07-04 15:40:10 -04:00
parent 24edee6548
commit 2f1aee9d09
6 changed files with 119 additions and 49 deletions

View file

@ -280,7 +280,7 @@ async fn main() {
connection.send_error(*sequence, 2002, 13006).await; connection.send_error(*sequence, 2002, 13006).await;
} }
} }
ClientLobbyIpcData::ServiceLogin { sequence } => { ClientLobbyIpcData::ServiceLogin { sequence, .. } => {
// TODO: support selecting a service account // TODO: support selecting a service account
connection.selected_service_account = connection.selected_service_account =
Some(connection.service_accounts[0].id); Some(connection.service_accounts[0].id);

View file

@ -0,0 +1,43 @@
use binrw::binrw;
use crate::common::{CHAR_NAME_MAX_LENGTH, read_string, write_string};
#[binrw]
#[derive(Debug, Clone, Default)]
pub struct RetainerInfo {
id: u64,
owner_id: u64,
slot_id: u8,
param1: u8,
status: u16,
param2: u32,
#[bw(pad_size_to = CHAR_NAME_MAX_LENGTH)]
#[br(count = CHAR_NAME_MAX_LENGTH)]
#[br(map = read_string)]
#[bw(map = write_string)]
name: String,
}
impl RetainerInfo {
pub const SIZE: usize = 56;
}
#[binrw]
#[derive(Debug, Clone, Default)]
pub struct DistRetainerInfo {
pub sequence: u64,
pub timestamp: u32,
pub index: u8,
pub count: u8,
pub option_param: u16,
pub option_arg: u32,
pub num_contracted: u16,
pub num_active: u16,
pub num_total: u16,
pub num_free_slots: u16,
pub total_retainers: u16,
pub active_retainers: u16,
#[br(count = 9)]
#[brw(pad_size_to = (9 * RetainerInfo::SIZE))]
pub characters: Vec<RetainerInfo>,
}

View file

@ -12,6 +12,12 @@ pub use server_list::{DistWorldInfo, Server};
mod login_reply; mod login_reply;
pub use login_reply::{LoginReply, ServiceAccount}; pub use login_reply::{LoginReply, ServiceAccount};
mod dist_retainer_info;
pub use dist_retainer_info::{DistRetainerInfo, RetainerInfo};
mod nack_reply;
pub use nack_reply::NackReply;
use crate::{ use crate::{
common::{read_string, write_string}, common::{read_string, write_string},
opcodes::{ClientLobbyIpcType, ServerLobbyIpcType}, opcodes::{ClientLobbyIpcType, ServerLobbyIpcType},
@ -48,6 +54,8 @@ impl Default for ClientLobbyIpcSegment {
session_id: String::new(), session_id: String::new(),
version_info: String::new(), version_info: String::new(),
unk1: 0, unk1: 0,
timestamp: 0,
unk2: 0,
}, },
} }
} }
@ -78,13 +86,7 @@ impl Default for ServerLobbyIpcSegment {
op_code: ServerLobbyIpcType::NackReply, op_code: ServerLobbyIpcType::NackReply,
option: 0, option: 0,
timestamp: 0, timestamp: 0,
data: ServerLobbyIpcData::NackReply { data: ServerLobbyIpcData::NackReply(NackReply::default()),
sequence: 0,
error: 0,
value: 0,
exd_error_id: 0,
unk1: 0,
},
} }
} }
} }
@ -96,9 +98,12 @@ pub enum ClientLobbyIpcData {
/// Sent by the client when it requests the character list in the lobby. /// Sent by the client when it requests the character list in the lobby.
#[br(pre_assert(*magic == ClientLobbyIpcType::ServiceLogin))] #[br(pre_assert(*magic == ClientLobbyIpcType::ServiceLogin))]
ServiceLogin { ServiceLogin {
#[brw(pad_before = 16)]
sequence: u64, sequence: u64,
// TODO: what is in here? account_index: u8,
unk1: u8,
unk2: u16,
unk3: u32, // TODO: probably multiple params
account_id: u64,
}, },
/// Sent by the client when it requests to enter a world. /// Sent by the client when it requests to enter a world.
#[br(pre_assert(*magic == ClientLobbyIpcType::GameLogin))] #[br(pre_assert(*magic == ClientLobbyIpcType::GameLogin))]
@ -113,8 +118,9 @@ pub enum ClientLobbyIpcData {
#[br(pre_assert(*magic == ClientLobbyIpcType::LoginEx))] #[br(pre_assert(*magic == ClientLobbyIpcType::LoginEx))]
LoginEx { LoginEx {
sequence: u64, sequence: u64,
timestamp: u32,
#[brw(pad_before = 10)] // full of nonsense i don't understand yet #[brw(pad_after = 2)]
unk1: u32,
#[br(count = 64)] #[br(count = 64)]
#[bw(pad_size_to = 64)] #[bw(pad_size_to = 64)]
#[br(map = read_string)] #[br(map = read_string)]
@ -128,7 +134,7 @@ pub enum ClientLobbyIpcData {
version_info: String, version_info: String,
#[brw(pad_before = 910)] // empty #[brw(pad_before = 910)] // empty
unk1: u64, unk2: u64,
}, },
#[br(pre_assert(*magic == ClientLobbyIpcType::ShandaLogin))] #[br(pre_assert(*magic == ClientLobbyIpcType::ShandaLogin))]
ShandaLogin { ShandaLogin {
@ -151,14 +157,7 @@ pub enum ClientLobbyIpcData {
pub enum ServerLobbyIpcData { pub enum ServerLobbyIpcData {
/// Sent by the server to indicate an lobby error occured. /// Sent by the server to indicate an lobby error occured.
#[br(pre_assert(*magic == ServerLobbyIpcType::NackReply))] #[br(pre_assert(*magic == ServerLobbyIpcType::NackReply))]
NackReply { NackReply(NackReply),
sequence: u64,
error: u32,
value: u32,
exd_error_id: u16,
#[brw(pad_after = 516)] // empty and garbage
unk1: u16,
},
/// Sent by the server to inform the client of their service accounts. /// Sent by the server to inform the client of their service accounts.
#[br(pre_assert(*magic == ServerLobbyIpcType::LoginReply))] #[br(pre_assert(*magic == ServerLobbyIpcType::LoginReply))]
LoginReply(LoginReply), LoginReply(LoginReply),
@ -203,12 +202,7 @@ pub enum ServerLobbyIpcData {
DistWorldInfo(DistWorldInfo), DistWorldInfo(DistWorldInfo),
/// Sent by the server to inform the client of their retainers. /// Sent by the server to inform the client of their retainers.
#[br(pre_assert(*magic == ServerLobbyIpcType::DistRetainerInfo))] #[br(pre_assert(*magic == ServerLobbyIpcType::DistRetainerInfo))]
DistRetainerInfo { DistRetainerInfo(DistRetainerInfo),
// TODO: what is in here?
#[brw(pad_before = 7)]
#[brw(pad_after = 528)]
unk1: u8,
},
Unknown { Unknown {
#[br(count = size - 32)] #[br(count = size - 32)]
unk: Vec<u8>, unk: Vec<u8>,
@ -229,13 +223,7 @@ mod tests {
let ipc_types = [ let ipc_types = [
( (
ServerLobbyIpcType::NackReply, ServerLobbyIpcType::NackReply,
ServerLobbyIpcData::NackReply { ServerLobbyIpcData::NackReply(NackReply::default()),
sequence: 0,
error: 0,
value: 0,
exd_error_id: 0,
unk1: 0,
},
), ),
( (
ServerLobbyIpcType::LoginReply, ServerLobbyIpcType::LoginReply,
@ -272,7 +260,7 @@ mod tests {
), ),
( (
ServerLobbyIpcType::DistRetainerInfo, ServerLobbyIpcType::DistRetainerInfo,
ServerLobbyIpcData::DistRetainerInfo { unk1: 0 }, ServerLobbyIpcData::DistRetainerInfo(DistRetainerInfo::default()),
), ),
]; ];
@ -306,7 +294,14 @@ mod tests {
let ipc_types = [ let ipc_types = [
( (
ClientLobbyIpcType::ServiceLogin, ClientLobbyIpcType::ServiceLogin,
ClientLobbyIpcData::ServiceLogin { sequence: 0 }, ClientLobbyIpcData::ServiceLogin {
sequence: 0,
account_index: 0,
unk1: 0,
unk2: 0,
account_id: 0,
unk3: 0,
},
), ),
( (
ClientLobbyIpcType::GameLogin, ClientLobbyIpcType::GameLogin,
@ -324,6 +319,8 @@ mod tests {
session_id: String::default(), session_id: String::default(),
version_info: String::default(), version_info: String::default(),
unk1: 0, unk1: 0,
timestamp: 0,
unk2: 0,
}, },
), ),
( (

View file

@ -0,0 +1,27 @@
#![allow(unused_variables)] // whoop binrw
use binrw::binrw;
use crate::common::{read_string, write_string};
#[binrw]
#[derive(Debug, Clone, Default)]
pub struct NackReply {
pub sequence: u64,
/// The error code shown in the client.
pub error: u32,
/// Value is specific to each error, e.g. your position in queue (i think)
pub value: u32,
/// The ID of a row in the Error Excel sheet.
pub exd_error_id: u16,
#[br(temp)]
#[bw(calc = message.len() as u16)]
pub message_size: u16,
/// Seems to be unused
#[brw(pad_after = 4)] // garbage
#[bw(pad_size_to = 512)]
#[br(count = 512)]
#[br(map = read_string)]
#[bw(map = write_string)]
pub message: String,
}

View file

@ -68,6 +68,10 @@ pub struct CharacterDetails {
pub unk3: [u32; 5], pub unk3: [u32; 5],
} }
impl CharacterDetails {
pub const SIZE: usize = 1196;
}
#[binrw] #[binrw]
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct ServiceLoginReply { pub struct ServiceLoginReply {
@ -93,6 +97,6 @@ pub struct ServiceLoginReply {
#[brw(pad_after = 12)] #[brw(pad_after = 12)]
pub entitled_expansion: u32, pub entitled_expansion: u32,
#[br(count = 2)] #[br(count = 2)]
#[brw(pad_size_to = (1196 * 2))] #[brw(pad_size_to = (CharacterDetails::SIZE * 2))]
pub characters: Vec<CharacterDetails>, pub characters: Vec<CharacterDetails>,
} }

View file

@ -7,11 +7,11 @@ use crate::{
blowfish::Blowfish, blowfish::Blowfish,
common::timestamp_secs, common::timestamp_secs,
config::get_config, config::get_config,
ipc::lobby::{DistRetainerInfo, NackReply},
opcodes::ServerLobbyIpcType, opcodes::ServerLobbyIpcType,
packet::oodle::OodleNetwork,
packet::{ packet::{
CompressionType, ConnectionType, PacketSegment, PacketState, SegmentData, SegmentType, CompressionType, ConnectionType, PacketSegment, PacketState, SegmentData, SegmentType,
generate_encryption_key, parse_packet, send_packet, generate_encryption_key, oodle::OodleNetwork, parse_packet, send_packet,
}, },
}; };
@ -120,7 +120,7 @@ impl LobbyConnection {
servers.resize(6, Server::default()); servers.resize(6, Server::default());
let lobby_server_list = ServerLobbyIpcData::DistWorldInfo(DistWorldInfo { let lobby_server_list = ServerLobbyIpcData::DistWorldInfo(DistWorldInfo {
sequence: 0, sequence,
unk1: 1, unk1: 1,
num_servers: 1, num_servers: 1,
servers, servers,
@ -144,7 +144,8 @@ impl LobbyConnection {
// send them the retainer list // send them the retainer list
{ {
let lobby_retainer_list = ServerLobbyIpcData::DistRetainerInfo { unk1: 1 }; let lobby_retainer_list =
ServerLobbyIpcData::DistRetainerInfo(DistRetainerInfo::default());
let ipc = ServerLobbyIpcSegment { let ipc = ServerLobbyIpcSegment {
op_code: ServerLobbyIpcType::DistRetainerInfo, op_code: ServerLobbyIpcType::DistRetainerInfo,
@ -272,13 +273,12 @@ impl LobbyConnection {
/// Send a lobby error to the client. /// Send a lobby error to the client.
pub async fn send_error(&mut self, sequence: u64, error: u32, exd_error: u16) { pub async fn send_error(&mut self, sequence: u64, error: u32, exd_error: u16) {
let lobby_error = ServerLobbyIpcData::NackReply { let lobby_error = ServerLobbyIpcData::NackReply(NackReply {
sequence, sequence,
error, error,
value: 0,
exd_error_id: exd_error, exd_error_id: exd_error,
unk1: 1, ..Default::default()
}; });
let ipc = ServerLobbyIpcSegment { let ipc = ServerLobbyIpcSegment {
op_code: ServerLobbyIpcType::NackReply, op_code: ServerLobbyIpcType::NackReply,
@ -350,13 +350,12 @@ impl LobbyConnection {
} else { } else {
let ipc = ServerLobbyIpcSegment { let ipc = ServerLobbyIpcSegment {
op_code: ServerLobbyIpcType::NackReply, op_code: ServerLobbyIpcType::NackReply,
data: ServerLobbyIpcData::NackReply { data: ServerLobbyIpcData::NackReply(NackReply {
sequence: character_action.sequence, sequence: character_action.sequence,
error: 0x00000bdb, error: 0x00000bdb,
exd_error_id: 0x32cc, exd_error_id: 0x32cc,
value: 0, ..Default::default()
unk1: 0, }),
},
..Default::default() ..Default::default()
}; };