1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-07-09 15:37:45 +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;
}
}
ClientLobbyIpcData::ServiceLogin { sequence } => {
ClientLobbyIpcData::ServiceLogin { sequence, .. } => {
// TODO: support selecting a service account
connection.selected_service_account =
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;
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::{
common::{read_string, write_string},
opcodes::{ClientLobbyIpcType, ServerLobbyIpcType},
@ -48,6 +54,8 @@ impl Default for ClientLobbyIpcSegment {
session_id: String::new(),
version_info: String::new(),
unk1: 0,
timestamp: 0,
unk2: 0,
},
}
}
@ -78,13 +86,7 @@ impl Default for ServerLobbyIpcSegment {
op_code: ServerLobbyIpcType::NackReply,
option: 0,
timestamp: 0,
data: ServerLobbyIpcData::NackReply {
sequence: 0,
error: 0,
value: 0,
exd_error_id: 0,
unk1: 0,
},
data: ServerLobbyIpcData::NackReply(NackReply::default()),
}
}
}
@ -96,9 +98,12 @@ pub enum ClientLobbyIpcData {
/// Sent by the client when it requests the character list in the lobby.
#[br(pre_assert(*magic == ClientLobbyIpcType::ServiceLogin))]
ServiceLogin {
#[brw(pad_before = 16)]
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.
#[br(pre_assert(*magic == ClientLobbyIpcType::GameLogin))]
@ -113,8 +118,9 @@ pub enum ClientLobbyIpcData {
#[br(pre_assert(*magic == ClientLobbyIpcType::LoginEx))]
LoginEx {
sequence: u64,
#[brw(pad_before = 10)] // full of nonsense i don't understand yet
timestamp: u32,
#[brw(pad_after = 2)]
unk1: u32,
#[br(count = 64)]
#[bw(pad_size_to = 64)]
#[br(map = read_string)]
@ -128,7 +134,7 @@ pub enum ClientLobbyIpcData {
version_info: String,
#[brw(pad_before = 910)] // empty
unk1: u64,
unk2: u64,
},
#[br(pre_assert(*magic == ClientLobbyIpcType::ShandaLogin))]
ShandaLogin {
@ -151,14 +157,7 @@ pub enum ClientLobbyIpcData {
pub enum ServerLobbyIpcData {
/// Sent by the server to indicate an lobby error occured.
#[br(pre_assert(*magic == ServerLobbyIpcType::NackReply))]
NackReply {
sequence: u64,
error: u32,
value: u32,
exd_error_id: u16,
#[brw(pad_after = 516)] // empty and garbage
unk1: u16,
},
NackReply(NackReply),
/// Sent by the server to inform the client of their service accounts.
#[br(pre_assert(*magic == ServerLobbyIpcType::LoginReply))]
LoginReply(LoginReply),
@ -203,12 +202,7 @@ pub enum ServerLobbyIpcData {
DistWorldInfo(DistWorldInfo),
/// Sent by the server to inform the client of their retainers.
#[br(pre_assert(*magic == ServerLobbyIpcType::DistRetainerInfo))]
DistRetainerInfo {
// TODO: what is in here?
#[brw(pad_before = 7)]
#[brw(pad_after = 528)]
unk1: u8,
},
DistRetainerInfo(DistRetainerInfo),
Unknown {
#[br(count = size - 32)]
unk: Vec<u8>,
@ -229,13 +223,7 @@ mod tests {
let ipc_types = [
(
ServerLobbyIpcType::NackReply,
ServerLobbyIpcData::NackReply {
sequence: 0,
error: 0,
value: 0,
exd_error_id: 0,
unk1: 0,
},
ServerLobbyIpcData::NackReply(NackReply::default()),
),
(
ServerLobbyIpcType::LoginReply,
@ -272,7 +260,7 @@ mod tests {
),
(
ServerLobbyIpcType::DistRetainerInfo,
ServerLobbyIpcData::DistRetainerInfo { unk1: 0 },
ServerLobbyIpcData::DistRetainerInfo(DistRetainerInfo::default()),
),
];
@ -306,7 +294,14 @@ mod tests {
let ipc_types = [
(
ClientLobbyIpcType::ServiceLogin,
ClientLobbyIpcData::ServiceLogin { sequence: 0 },
ClientLobbyIpcData::ServiceLogin {
sequence: 0,
account_index: 0,
unk1: 0,
unk2: 0,
account_id: 0,
unk3: 0,
},
),
(
ClientLobbyIpcType::GameLogin,
@ -324,6 +319,8 @@ mod tests {
session_id: String::default(),
version_info: String::default(),
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],
}
impl CharacterDetails {
pub const SIZE: usize = 1196;
}
#[binrw]
#[derive(Debug, Clone, Default)]
pub struct ServiceLoginReply {
@ -93,6 +97,6 @@ pub struct ServiceLoginReply {
#[brw(pad_after = 12)]
pub entitled_expansion: u32,
#[br(count = 2)]
#[brw(pad_size_to = (1196 * 2))]
#[brw(pad_size_to = (CharacterDetails::SIZE * 2))]
pub characters: Vec<CharacterDetails>,
}

View file

@ -7,11 +7,11 @@ use crate::{
blowfish::Blowfish,
common::timestamp_secs,
config::get_config,
ipc::lobby::{DistRetainerInfo, NackReply},
opcodes::ServerLobbyIpcType,
packet::oodle::OodleNetwork,
packet::{
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());
let lobby_server_list = ServerLobbyIpcData::DistWorldInfo(DistWorldInfo {
sequence: 0,
sequence,
unk1: 1,
num_servers: 1,
servers,
@ -144,7 +144,8 @@ impl LobbyConnection {
// send them the retainer list
{
let lobby_retainer_list = ServerLobbyIpcData::DistRetainerInfo { unk1: 1 };
let lobby_retainer_list =
ServerLobbyIpcData::DistRetainerInfo(DistRetainerInfo::default());
let ipc = ServerLobbyIpcSegment {
op_code: ServerLobbyIpcType::DistRetainerInfo,
@ -272,13 +273,12 @@ impl LobbyConnection {
/// Send a lobby error to the client.
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,
error,
value: 0,
exd_error_id: exd_error,
unk1: 1,
};
..Default::default()
});
let ipc = ServerLobbyIpcSegment {
op_code: ServerLobbyIpcType::NackReply,
@ -350,13 +350,12 @@ impl LobbyConnection {
} else {
let ipc = ServerLobbyIpcSegment {
op_code: ServerLobbyIpcType::NackReply,
data: ServerLobbyIpcData::NackReply {
data: ServerLobbyIpcData::NackReply(NackReply {
sequence: character_action.sequence,
error: 0x00000bdb,
exd_error_id: 0x32cc,
value: 0,
unk1: 0,
},
..Default::default()
}),
..Default::default()
};