mirror of
https://github.com/redstrate/Kawari.git
synced 2025-04-24 08:07:45 +00:00
Begin sending lobby information
This now puts the client onto the real lobby screen, not sure how much actually works yet.
This commit is contained in:
parent
00c5f4a10e
commit
1f43318add
2 changed files with 273 additions and 3 deletions
110
src/ipc.rs
110
src/ipc.rs
|
@ -6,10 +6,18 @@ use crate::common::{read_string, write_string};
|
||||||
#[brw(repr = u16)]
|
#[brw(repr = u16)]
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub enum IPCOpCode {
|
pub enum IPCOpCode {
|
||||||
|
/// Sent by the client when it requests the character list in the lobby.
|
||||||
|
RequestCharacterList = 0x3,
|
||||||
/// Sent by the client after exchanging encryption information with the lobby server.
|
/// Sent by the client after exchanging encryption information with the lobby server.
|
||||||
ClientVersionInfo = 0x5,
|
ClientVersionInfo = 0x5,
|
||||||
/// Sent by the server to inform the client of service accounts.
|
/// Sent by the server to inform the client of their service accounts.
|
||||||
LobbyServiceAccountList = 0xC,
|
LobbyServiceAccountList = 0xC,
|
||||||
|
/// Sent by the server to inform the client of their characters.
|
||||||
|
LobbyCharacterList = 0xD,
|
||||||
|
/// 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,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
|
@ -25,6 +33,57 @@ pub struct ServiceAccount {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[binrw]
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct Server {
|
||||||
|
pub id: u16,
|
||||||
|
pub index: u16,
|
||||||
|
pub flags: u32,
|
||||||
|
#[brw(pad_before = 4)]
|
||||||
|
#[brw(pad_after = 4)]
|
||||||
|
pub icon: u32,
|
||||||
|
#[bw(pad_size_to = 0x40)]
|
||||||
|
#[br(count = 0x40)]
|
||||||
|
#[br(map = read_string)]
|
||||||
|
#[bw(map = write_string)]
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[binrw]
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct CharacterDetails {
|
||||||
|
#[brw(pad_after = 4)]
|
||||||
|
pub id: u32,
|
||||||
|
pub content_id: u64,
|
||||||
|
#[brw(pad_after = 4)]
|
||||||
|
pub index: u32,
|
||||||
|
// TODO: lol? why is there server_id1?
|
||||||
|
pub server_id: u16,
|
||||||
|
pub server_id1: u16,
|
||||||
|
pub unk1: [u8; 16],
|
||||||
|
#[bw(pad_size_to = 16)]
|
||||||
|
#[br(count = 16)]
|
||||||
|
#[br(map = read_string)]
|
||||||
|
#[bw(map = write_string)]
|
||||||
|
pub character_name: String,
|
||||||
|
#[bw(pad_size_to = 32)]
|
||||||
|
#[br(count = 32)]
|
||||||
|
#[br(map = read_string)]
|
||||||
|
#[bw(map = write_string)]
|
||||||
|
pub character_server_name: String,
|
||||||
|
#[bw(pad_size_to = 32)]
|
||||||
|
#[br(count = 32)]
|
||||||
|
#[br(map = read_string)]
|
||||||
|
#[bw(map = write_string)]
|
||||||
|
pub character_server_name1: String,
|
||||||
|
#[bw(pad_size_to = 1024)]
|
||||||
|
#[br(count = 1024)]
|
||||||
|
#[br(map = read_string)]
|
||||||
|
#[bw(map = write_string)]
|
||||||
|
pub character_detail_json: String,
|
||||||
|
pub unk2: [u8; 20],
|
||||||
|
}
|
||||||
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
#[br(import(magic: &IPCOpCode))]
|
#[br(import(magic: &IPCOpCode))]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -45,6 +104,12 @@ pub enum IPCStructData {
|
||||||
version_info: String,
|
version_info: String,
|
||||||
// unknown stuff at the end, it's not completely empty'
|
// unknown stuff at the end, it's not completely empty'
|
||||||
},
|
},
|
||||||
|
#[br(pre_assert(*magic == IPCOpCode::RequestCharacterList))]
|
||||||
|
RequestCharacterList {
|
||||||
|
#[brw(pad_before = 16)]
|
||||||
|
sequence: u64,
|
||||||
|
// TODO: what is in here?
|
||||||
|
},
|
||||||
|
|
||||||
// Server->Client IPC
|
// Server->Client IPC
|
||||||
LobbyServiceAccountList {
|
LobbyServiceAccountList {
|
||||||
|
@ -57,6 +122,45 @@ pub enum IPCStructData {
|
||||||
#[br(count = 8)]
|
#[br(count = 8)]
|
||||||
service_accounts: Vec<ServiceAccount>,
|
service_accounts: Vec<ServiceAccount>,
|
||||||
},
|
},
|
||||||
|
LobbyServerList {
|
||||||
|
sequence: u64,
|
||||||
|
unk1: u16,
|
||||||
|
offset: u16,
|
||||||
|
#[brw(pad_after = 8)]
|
||||||
|
num_servers: u32,
|
||||||
|
#[br(count = 6)]
|
||||||
|
servers: Vec<Server>,
|
||||||
|
},
|
||||||
|
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,
|
||||||
|
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: u32,
|
||||||
|
unk8: u16,
|
||||||
|
#[brw(pad_after = 12)]
|
||||||
|
entitled_expansion: u32,
|
||||||
|
#[br(count = 2)]
|
||||||
|
characters: Vec<CharacterDetails>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
|
@ -80,6 +184,10 @@ impl IPCSegment {
|
||||||
+ match self.data {
|
+ match self.data {
|
||||||
IPCStructData::ClientVersionInfo { .. } => todo!(),
|
IPCStructData::ClientVersionInfo { .. } => todo!(),
|
||||||
IPCStructData::LobbyServiceAccountList { .. } => 24 + (8 * 80),
|
IPCStructData::LobbyServiceAccountList { .. } => 24 + (8 * 80),
|
||||||
|
IPCStructData::RequestCharacterList { .. } => todo!(),
|
||||||
|
IPCStructData::LobbyServerList { .. } => 24 + (6 * 84),
|
||||||
|
IPCStructData::LobbyRetainerList { .. } => 210,
|
||||||
|
IPCStructData::LobbyCharacterList { .. } => 82 + (2 * 1168),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
166
src/packet.rs
166
src/packet.rs
|
@ -1,4 +1,5 @@
|
||||||
use std::{
|
use std::{
|
||||||
|
cmp::{max, min},
|
||||||
fs::write,
|
fs::write,
|
||||||
io::Cursor,
|
io::Cursor,
|
||||||
time::{SystemTime, UNIX_EPOCH},
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
|
@ -13,7 +14,7 @@ use tokio::{
|
||||||
use crate::{
|
use crate::{
|
||||||
common::{read_bool_from, read_string, write_bool_as},
|
common::{read_bool_from, read_string, write_bool_as},
|
||||||
encryption::{blowfish_encode, decrypt, encrypt, generate_encryption_key},
|
encryption::{blowfish_encode, decrypt, encrypt, generate_encryption_key},
|
||||||
ipc::{IPCOpCode, IPCSegment, IPCStructData, ServiceAccount},
|
ipc::{CharacterDetails, IPCOpCode, IPCSegment, IPCStructData, Server, ServiceAccount},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
|
@ -236,7 +237,8 @@ pub async fn parse_packet(socket: &mut WriteHalf<TcpStream>, data: &[u8], state:
|
||||||
unk1: 0,
|
unk1: 0,
|
||||||
index: 0,
|
index: 0,
|
||||||
name: "FINAL FANTASY XIV".to_string(),
|
name: "FINAL FANTASY XIV".to_string(),
|
||||||
}].to_vec();
|
}]
|
||||||
|
.to_vec();
|
||||||
// add any empty boys
|
// add any empty boys
|
||||||
service_accounts.resize(8, ServiceAccount::default());
|
service_accounts.resize(8, ServiceAccount::default());
|
||||||
|
|
||||||
|
@ -264,6 +266,166 @@ pub async fn parse_packet(socket: &mut WriteHalf<TcpStream>, data: &[u8], state:
|
||||||
};
|
};
|
||||||
send_packet(socket, &[response_packet], state).await;
|
send_packet(socket, &[response_packet], state).await;
|
||||||
}
|
}
|
||||||
|
IPCStructData::RequestCharacterList { sequence } => {
|
||||||
|
tracing::info!("Client is requesting character list...");
|
||||||
|
|
||||||
|
let timestamp: u32 = SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.expect("Failed to get UNIX timestamp!")
|
||||||
|
.as_secs()
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut packets = Vec::new();
|
||||||
|
// send them the character list
|
||||||
|
{
|
||||||
|
let mut servers = [Server {
|
||||||
|
id: 21,
|
||||||
|
index: 0,
|
||||||
|
flags: 0,
|
||||||
|
icon: 0,
|
||||||
|
name: "KAWARI".to_string(),
|
||||||
|
}]
|
||||||
|
.to_vec();
|
||||||
|
// add any empty boys
|
||||||
|
servers.resize(6, Server::default());
|
||||||
|
|
||||||
|
let lobby_server_list = IPCStructData::LobbyServerList {
|
||||||
|
sequence: 0,
|
||||||
|
unk1: 1,
|
||||||
|
offset: 0,
|
||||||
|
num_servers: 1,
|
||||||
|
servers,
|
||||||
|
};
|
||||||
|
|
||||||
|
let ipc = IPCSegment {
|
||||||
|
unk1: 0,
|
||||||
|
unk2: 0,
|
||||||
|
op_code: IPCOpCode::LobbyServerList,
|
||||||
|
server_id: 0,
|
||||||
|
timestamp,
|
||||||
|
data: lobby_server_list,
|
||||||
|
};
|
||||||
|
|
||||||
|
let response_packet = PacketSegment {
|
||||||
|
source_actor: 0,
|
||||||
|
target_actor: 0,
|
||||||
|
segment_type: SegmentType::IPC { data: ipc },
|
||||||
|
};
|
||||||
|
packets.push(response_packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
// send them the retainer list
|
||||||
|
{
|
||||||
|
let lobby_retainer_list =
|
||||||
|
IPCStructData::LobbyRetainerList { unk1: 1 };
|
||||||
|
|
||||||
|
let ipc = IPCSegment {
|
||||||
|
unk1: 0,
|
||||||
|
unk2: 0,
|
||||||
|
op_code: IPCOpCode::LobbyRetainerList,
|
||||||
|
server_id: 0,
|
||||||
|
timestamp,
|
||||||
|
data: lobby_retainer_list,
|
||||||
|
};
|
||||||
|
|
||||||
|
let response_packet = PacketSegment {
|
||||||
|
source_actor: 0,
|
||||||
|
target_actor: 0,
|
||||||
|
segment_type: SegmentType::IPC { data: ipc },
|
||||||
|
};
|
||||||
|
packets.push(response_packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
send_packet(socket, &packets, state).await;
|
||||||
|
|
||||||
|
// now send them the character list
|
||||||
|
{
|
||||||
|
let mut characters = vec![CharacterDetails {
|
||||||
|
id: 0,
|
||||||
|
content_id: 11111111111111111,
|
||||||
|
index: 0,
|
||||||
|
server_id: 21,
|
||||||
|
server_id1: 21,
|
||||||
|
unk1: [0; 16],
|
||||||
|
character_name: "test".to_string(),
|
||||||
|
character_server_name: "test".to_string(),
|
||||||
|
character_server_name1: "test".to_string(),
|
||||||
|
character_detail_json: "test".to_string(),
|
||||||
|
unk2: [0; 20],
|
||||||
|
}];
|
||||||
|
// add any empty boys
|
||||||
|
characters.resize(2, CharacterDetails::default());
|
||||||
|
|
||||||
|
for i in 0..4 {
|
||||||
|
let mut characters_in_packet = Vec::new();
|
||||||
|
for _ in 0..min(characters.len(), 2) {
|
||||||
|
characters_in_packet.push(characters.swap_remove(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
let lobby_character_list = if i == 3 {
|
||||||
|
// On the last packet, add the account-wide information
|
||||||
|
IPCStructData::LobbyCharacterList {
|
||||||
|
sequence: *sequence,
|
||||||
|
counter: (i * 4) + 1, // TODO: why the + 1 here?
|
||||||
|
num_in_packet: characters_in_packet.len() as u8,
|
||||||
|
unk1: 0,
|
||||||
|
unk2: 0,
|
||||||
|
unk3: 0,
|
||||||
|
unk4: 128,
|
||||||
|
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: 20,
|
||||||
|
unk8: 8,
|
||||||
|
entitled_expansion: 4,
|
||||||
|
characters: characters_in_packet,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
IPCStructData::LobbyCharacterList {
|
||||||
|
sequence: *sequence,
|
||||||
|
counter: i * 4,
|
||||||
|
num_in_packet: characters_in_packet.len() as u8,
|
||||||
|
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: characters_in_packet,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let ipc = IPCSegment {
|
||||||
|
unk1: 0,
|
||||||
|
unk2: 0,
|
||||||
|
op_code: IPCOpCode::LobbyCharacterList,
|
||||||
|
server_id: 0,
|
||||||
|
timestamp,
|
||||||
|
data: lobby_character_list,
|
||||||
|
};
|
||||||
|
|
||||||
|
let response_packet = PacketSegment {
|
||||||
|
source_actor: 0,
|
||||||
|
target_actor: 0,
|
||||||
|
segment_type: SegmentType::IPC { data: ipc },
|
||||||
|
};
|
||||||
|
send_packet(socket, &[response_packet], state).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
panic!("The server is recieving a IPC response packet!")
|
panic!("The server is recieving a IPC response packet!")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue