1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-04-25 16:27:46 +00:00
kawari/src/lobby/ipc/mod.rs
Joshua Goins dae20a1e0c Rename IpcSegmentTrait to ReadWriteIpcSegment, add docs
This also adds a default calc_size() implementation so client packets don't have
to implement it.
2025-03-17 17:12:40 -04:00

285 lines
8.4 KiB
Rust

use binrw::binrw;
mod character_action;
pub use character_action::{LobbyCharacterAction, LobbyCharacterActionKind};
mod character_list;
pub use character_list::{CharacterDetails, LobbyCharacterList};
mod client_version_info;
mod server_list;
pub use server_list::{LobbyServerList, Server};
mod service_account_list;
pub use service_account_list::{LobbyServiceAccountList, ServiceAccount};
use crate::{
common::{read_string, write_string},
packet::{IpcSegment, ReadWriteIpcSegment},
};
pub type ClientLobbyIpcSegment = IpcSegment<ClientLobbyIpcType, ClientLobbyIpcData>;
impl ReadWriteIpcSegment for ClientLobbyIpcSegment {}
// TODO: make generic
impl Default for ClientLobbyIpcSegment {
fn default() -> Self {
Self {
unk1: 0x14,
unk2: 0,
op_code: ClientLobbyIpcType::ClientVersionInfo,
server_id: 0,
timestamp: 0,
data: ClientLobbyIpcData::ClientVersionInfo {
sequence: 0,
session_id: String::new(),
version_info: String::new(),
},
}
}
}
pub type ServerLobbyIpcSegment = IpcSegment<ServerLobbyIpcType, ServerLobbyIpcData>;
impl ReadWriteIpcSegment for ServerLobbyIpcSegment {
fn calc_size(&self) -> u32 {
// 16 is the size of the IPC header
16 + match self.op_code {
ServerLobbyIpcType::LobbyError => 536,
ServerLobbyIpcType::LobbyServiceAccountList => 656,
ServerLobbyIpcType::LobbyCharacterList => 2472,
ServerLobbyIpcType::LobbyEnterWorld => 160,
ServerLobbyIpcType::LobbyServerList => 528,
ServerLobbyIpcType::LobbyRetainerList => 210,
ServerLobbyIpcType::CharacterCreated => 2568,
}
}
}
// TODO: make generic
impl Default for ServerLobbyIpcSegment {
fn default() -> Self {
Self {
unk1: 0x14,
unk2: 0,
op_code: ServerLobbyIpcType::LobbyError,
server_id: 0,
timestamp: 0,
data: ServerLobbyIpcData::LobbyError {
sequence: 0,
error: 0,
value: 0,
exd_error_id: 0,
unk1: 0,
},
}
}
}
#[binrw]
#[brw(repr = u16)]
#[derive(Clone, PartialEq, Debug)]
pub enum ServerLobbyIpcType {
/// Sent by the server to indicate an lobby error occured
LobbyError = 0x2,
/// Sent by the server to inform the client of their service accounts.
LobbyServiceAccountList = 0xC,
/// Sent by the server to inform the client of their characters.
LobbyCharacterList = 0xD,
/// Sent by the server to tell the client how to connect to the world server.
LobbyEnterWorld = 0xF,
/// Sent by the server to inform the client of their servers.
LobbyServerList = 0x15,
/// Sent by the server to inform the client of their retainers.
LobbyRetainerList = 0x17,
// Assumed what this is, but probably incorrect
CharacterCreated = 0xE,
}
#[binrw]
#[brw(repr = u16)]
#[derive(Clone, PartialEq, Debug)]
pub enum ClientLobbyIpcType {
/// Sent by the client when it requests the character list in the lobby.
RequestCharacterList = 0x3,
/// Sent by the client when it requests to enter a world.
RequestEnterWorld = 0x4,
/// Sent by the client after exchanging encryption information with the lobby server.
ClientVersionInfo = 0x5,
/// Sent by the client when they request something about the character (e.g. deletion.)
LobbyCharacterAction = 0xB,
}
#[binrw]
#[br(import(magic: &ClientLobbyIpcType))]
#[derive(Debug, Clone)]
pub enum ClientLobbyIpcData {
// Client->Server IPC
#[br(pre_assert(*magic == ClientLobbyIpcType::ClientVersionInfo))]
ClientVersionInfo {
sequence: u64,
#[brw(pad_before = 10)] // full of nonsense i don't understand yet
#[br(count = 64)]
#[br(map = read_string)]
#[bw(ignore)]
session_id: String,
#[brw(pad_before = 8)] // empty
#[br(count = 128)]
#[br(map = read_string)]
#[bw(ignore)]
version_info: String,
// unknown stuff at the end, it's not completely empty'
},
#[br(pre_assert(*magic == ClientLobbyIpcType::RequestCharacterList))]
RequestCharacterList {
#[brw(pad_before = 16)]
sequence: u64,
// TODO: what is in here?
},
#[br(pre_assert(*magic == ClientLobbyIpcType::LobbyCharacterAction))]
LobbyCharacterAction(LobbyCharacterAction),
#[br(pre_assert(*magic == ClientLobbyIpcType::RequestEnterWorld))]
RequestEnterWorld {
#[brw(pad_before = 16)]
sequence: u64,
lookup_id: u64,
// TODO: what else is in here?
},
}
#[binrw]
#[br(import(_magic: &ServerLobbyIpcType))]
#[derive(Debug, Clone)]
pub enum ServerLobbyIpcData {
LobbyServiceAccountList(LobbyServiceAccountList),
LobbyServerList(LobbyServerList),
LobbyRetainerList {
// TODO: what is in here?
#[brw(pad_before = 7)]
#[brw(pad_after = 202)]
unk1: u8,
},
LobbyCharacterList(LobbyCharacterList),
LobbyEnterWorld {
sequence: u64,
character_id: u32,
#[brw(pad_before = 4)]
content_id: u64,
#[brw(pad_before = 4)]
#[bw(pad_size_to = 66)]
#[br(count = 66)]
#[br(map = read_string)]
#[bw(map = write_string)]
session_id: String,
port: u16,
#[brw(pad_after = 16)] // garbage?
#[br(count = 48)]
#[brw(pad_size_to = 48)]
#[br(map = read_string)]
#[bw(map = write_string)]
host: String,
},
LobbyError {
sequence: u64,
error: u32,
value: u32,
exd_error_id: u16,
#[brw(pad_after = 516)] // empty and garbage
unk1: u16,
},
CharacterCreated {
sequence: u64,
unk: u32,
#[brw(pad_before = 36)] // empty
#[brw(pad_after = 1336)] // empty and garbage
details: CharacterDetails,
},
}
#[cfg(test)]
mod tests {
use std::io::Cursor;
use binrw::BinWrite;
use super::*;
/// Ensure that the IPC data size as reported matches up with what we write
#[test]
fn lobby_ipc_sizes() {
let ipc_types = [
(
ServerLobbyIpcType::LobbyServiceAccountList,
ServerLobbyIpcData::LobbyServiceAccountList(LobbyServiceAccountList::default()),
),
(
ServerLobbyIpcType::LobbyServerList,
ServerLobbyIpcData::LobbyServerList(LobbyServerList::default()),
),
(
ServerLobbyIpcType::LobbyRetainerList,
ServerLobbyIpcData::LobbyRetainerList { unk1: 0 },
),
(
ServerLobbyIpcType::LobbyCharacterList,
ServerLobbyIpcData::LobbyCharacterList(LobbyCharacterList::default()),
),
(
ServerLobbyIpcType::LobbyEnterWorld,
ServerLobbyIpcData::LobbyEnterWorld {
sequence: 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,
},
),
(
ServerLobbyIpcType::CharacterCreated,
ServerLobbyIpcData::CharacterCreated {
sequence: 0,
unk: 0,
details: CharacterDetails::default(),
},
),
];
for (opcode, ipc) in &ipc_types {
let mut cursor = Cursor::new(Vec::new());
let ipc_segment = ServerLobbyIpcSegment {
unk1: 0,
unk2: 0,
op_code: opcode.clone(),
server_id: 0,
timestamp: 0,
data: ipc.clone(),
};
ipc_segment.write_le(&mut cursor).unwrap();
let buffer = cursor.into_inner();
assert_eq!(
buffer.len(),
ipc_segment.calc_size() as usize,
"{:#?} did not match size!",
opcode
);
}
}
}