mirror of
https://github.com/redstrate/Kawari.git
synced 2025-04-28 01:17:45 +00:00
Fix numerous issues with the client<->zone communication and Oodle
Yet again TemporalStasis is awesome, and it shows that we need two separate compressors (one for clientbound packets, one for serverbound ones.) I also updated some opcodes for the latest patch, and support for compressing with Oodle when sending packets.
This commit is contained in:
parent
7cd5233598
commit
7b0c41a478
6 changed files with 207 additions and 59 deletions
|
@ -7,7 +7,7 @@ use kawari::encryption::generate_encryption_key;
|
||||||
use kawari::ipc::{CharacterDetails, IPCOpCode, IPCSegment, IPCStructData, Server, ServiceAccount};
|
use kawari::ipc::{CharacterDetails, IPCOpCode, IPCSegment, IPCStructData, Server, ServiceAccount};
|
||||||
use kawari::oodle::FFXIVOodle;
|
use kawari::oodle::FFXIVOodle;
|
||||||
use kawari::packet::{
|
use kawari::packet::{
|
||||||
PacketSegment, SegmentType, State, parse_packet, send_keep_alive, send_packet,
|
CompressionType, PacketSegment, SegmentType, State, parse_packet, send_keep_alive, send_packet,
|
||||||
};
|
};
|
||||||
use kawari::{CONTENT_ID, WORLD_ID, WORLD_NAME, ZONE_ID};
|
use kawari::{CONTENT_ID, WORLD_ID, WORLD_NAME, ZONE_ID};
|
||||||
use tokio::io::{AsyncReadExt, WriteHalf};
|
use tokio::io::{AsyncReadExt, WriteHalf};
|
||||||
|
@ -28,7 +28,8 @@ async fn main() {
|
||||||
let mut state = State {
|
let mut state = State {
|
||||||
client_key: None,
|
client_key: None,
|
||||||
session_id: None,
|
session_id: None,
|
||||||
oodle: FFXIVOodle::new(),
|
clientbound_oodle: FFXIVOodle::new(),
|
||||||
|
serverbound_oodle: FFXIVOodle::new(),
|
||||||
player_id: None,
|
player_id: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -114,7 +115,13 @@ async fn initialize_encryption(
|
||||||
target_actor: 0,
|
target_actor: 0,
|
||||||
segment_type: SegmentType::InitializationEncryptionResponse { data },
|
segment_type: SegmentType::InitializationEncryptionResponse { data },
|
||||||
};
|
};
|
||||||
send_packet(socket, &[response_packet], state).await;
|
send_packet(
|
||||||
|
socket,
|
||||||
|
&[response_packet],
|
||||||
|
state,
|
||||||
|
CompressionType::Uncompressed,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_account_list(socket: &mut WriteHalf<TcpStream>, state: &mut State) {
|
async fn send_account_list(socket: &mut WriteHalf<TcpStream>, state: &mut State) {
|
||||||
|
@ -156,7 +163,13 @@ async fn send_account_list(socket: &mut WriteHalf<TcpStream>, state: &mut State)
|
||||||
target_actor: 0,
|
target_actor: 0,
|
||||||
segment_type: SegmentType::Ipc { data: ipc },
|
segment_type: SegmentType::Ipc { data: ipc },
|
||||||
};
|
};
|
||||||
send_packet(socket, &[response_packet], state).await;
|
send_packet(
|
||||||
|
socket,
|
||||||
|
&[response_packet],
|
||||||
|
state,
|
||||||
|
CompressionType::Uncompressed,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_lobby_info(socket: &mut WriteHalf<TcpStream>, state: &mut State, sequence: u64) {
|
async fn send_lobby_info(socket: &mut WriteHalf<TcpStream>, state: &mut State, sequence: u64) {
|
||||||
|
@ -227,7 +240,7 @@ async fn send_lobby_info(socket: &mut WriteHalf<TcpStream>, state: &mut State, s
|
||||||
packets.push(response_packet);
|
packets.push(response_packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
send_packet(socket, &packets, state).await;
|
send_packet(socket, &packets, state, CompressionType::Uncompressed).await;
|
||||||
|
|
||||||
// now send them the character list
|
// now send them the character list
|
||||||
{
|
{
|
||||||
|
@ -367,7 +380,13 @@ async fn send_lobby_info(socket: &mut WriteHalf<TcpStream>, state: &mut State, s
|
||||||
target_actor: 0,
|
target_actor: 0,
|
||||||
segment_type: SegmentType::Ipc { data: ipc },
|
segment_type: SegmentType::Ipc { data: ipc },
|
||||||
};
|
};
|
||||||
send_packet(socket, &[response_packet], state).await;
|
send_packet(
|
||||||
|
socket,
|
||||||
|
&[response_packet],
|
||||||
|
state,
|
||||||
|
CompressionType::Uncompressed,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -412,5 +431,11 @@ async fn send_enter_world(
|
||||||
target_actor: 0,
|
target_actor: 0,
|
||||||
segment_type: SegmentType::Ipc { data: ipc },
|
segment_type: SegmentType::Ipc { data: ipc },
|
||||||
};
|
};
|
||||||
send_packet(socket, &[response_packet], state).await;
|
send_packet(
|
||||||
|
socket,
|
||||||
|
&[response_packet],
|
||||||
|
state,
|
||||||
|
CompressionType::Uncompressed,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
use kawari::ipc::{ActorControlType, IPCOpCode, IPCSegment, IPCStructData, Position};
|
use kawari::ipc::{ActorControlType, IPCOpCode, IPCSegment, IPCStructData, Position};
|
||||||
use kawari::oodle::FFXIVOodle;
|
use kawari::oodle::FFXIVOodle;
|
||||||
use kawari::packet::{
|
use kawari::packet::{
|
||||||
PacketSegment, SegmentType, State, parse_packet, send_keep_alive, send_packet,
|
CompressionType, PacketSegment, SegmentType, State, parse_packet, send_keep_alive, send_packet,
|
||||||
};
|
};
|
||||||
use kawari::{CONTENT_ID, WORLD_ID, ZONE_ID};
|
use kawari::{CONTENT_ID, WORLD_ID, ZONE_ID};
|
||||||
use tokio::io::AsyncReadExt;
|
use tokio::io::AsyncReadExt;
|
||||||
|
@ -24,7 +24,8 @@ async fn main() {
|
||||||
let mut state = State {
|
let mut state = State {
|
||||||
client_key: None,
|
client_key: None,
|
||||||
session_id: None,
|
session_id: None,
|
||||||
oodle: FFXIVOodle::new(),
|
clientbound_oodle: FFXIVOodle::new(),
|
||||||
|
serverbound_oodle: FFXIVOodle::new(),
|
||||||
player_id: None,
|
player_id: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -58,7 +59,13 @@ async fn main() {
|
||||||
timestamp,
|
timestamp,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
send_packet(&mut write, &[response_packet], &mut state).await;
|
send_packet(
|
||||||
|
&mut write,
|
||||||
|
&[response_packet],
|
||||||
|
&mut state,
|
||||||
|
CompressionType::Oodle,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
match connection_type {
|
match connection_type {
|
||||||
|
@ -74,8 +81,13 @@ async fn main() {
|
||||||
player_id: *player_id,
|
player_id: *player_id,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
send_packet(&mut write, &[response_packet], &mut state)
|
send_packet(
|
||||||
.await;
|
&mut write,
|
||||||
|
&[response_packet],
|
||||||
|
&mut state,
|
||||||
|
CompressionType::Oodle,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
kawari::packet::ConnectionType::Chat => {
|
kawari::packet::ConnectionType::Chat => {
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
|
@ -90,8 +102,13 @@ async fn main() {
|
||||||
player_id: *player_id,
|
player_id: *player_id,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
send_packet(&mut write, &[response_packet], &mut state)
|
send_packet(
|
||||||
.await;
|
&mut write,
|
||||||
|
&[response_packet],
|
||||||
|
&mut state,
|
||||||
|
CompressionType::Oodle,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -101,9 +118,7 @@ async fn main() {
|
||||||
op_code: IPCOpCode::InitializeChat,
|
op_code: IPCOpCode::InitializeChat,
|
||||||
server_id: 0,
|
server_id: 0,
|
||||||
timestamp: 0,
|
timestamp: 0,
|
||||||
data: IPCStructData::InitializeChat {
|
data: IPCStructData::InitializeChat { unk: [0; 8] },
|
||||||
unk: [0; 24],
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let response_packet = PacketSegment {
|
let response_packet = PacketSegment {
|
||||||
|
@ -111,8 +126,13 @@ async fn main() {
|
||||||
target_actor: *player_id,
|
target_actor: *player_id,
|
||||||
segment_type: SegmentType::Ipc { data: ipc },
|
segment_type: SegmentType::Ipc { data: ipc },
|
||||||
};
|
};
|
||||||
send_packet(&mut write, &[response_packet], &mut state)
|
send_packet(
|
||||||
.await;
|
&mut write,
|
||||||
|
&[response_packet],
|
||||||
|
&mut state,
|
||||||
|
CompressionType::Oodle,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => panic!(
|
_ => panic!(
|
||||||
|
@ -156,8 +176,13 @@ async fn main() {
|
||||||
target_actor: state.player_id.unwrap(),
|
target_actor: state.player_id.unwrap(),
|
||||||
segment_type: SegmentType::Ipc { data: ipc },
|
segment_type: SegmentType::Ipc { data: ipc },
|
||||||
};
|
};
|
||||||
send_packet(&mut write, &[response_packet], &mut state)
|
send_packet(
|
||||||
.await;
|
&mut write,
|
||||||
|
&[response_packet],
|
||||||
|
&mut state,
|
||||||
|
CompressionType::Oodle,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Control Data
|
// Control Data
|
||||||
|
@ -184,8 +209,13 @@ async fn main() {
|
||||||
target_actor: state.player_id.unwrap(),
|
target_actor: state.player_id.unwrap(),
|
||||||
segment_type: SegmentType::Ipc { data: ipc },
|
segment_type: SegmentType::Ipc { data: ipc },
|
||||||
};
|
};
|
||||||
send_packet(&mut write, &[response_packet], &mut state)
|
send_packet(
|
||||||
.await;
|
&mut write,
|
||||||
|
&[response_packet],
|
||||||
|
&mut state,
|
||||||
|
CompressionType::Oodle,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stats
|
// Stats
|
||||||
|
@ -236,8 +266,13 @@ async fn main() {
|
||||||
target_actor: state.player_id.unwrap(),
|
target_actor: state.player_id.unwrap(),
|
||||||
segment_type: SegmentType::Ipc { data: ipc },
|
segment_type: SegmentType::Ipc { data: ipc },
|
||||||
};
|
};
|
||||||
send_packet(&mut write, &[response_packet], &mut state)
|
send_packet(
|
||||||
.await;
|
&mut write,
|
||||||
|
&[response_packet],
|
||||||
|
&mut state,
|
||||||
|
CompressionType::Oodle,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Player Setup
|
// Player Setup
|
||||||
|
@ -376,8 +411,13 @@ async fn main() {
|
||||||
target_actor: state.player_id.unwrap(),
|
target_actor: state.player_id.unwrap(),
|
||||||
segment_type: SegmentType::Ipc { data: ipc },
|
segment_type: SegmentType::Ipc { data: ipc },
|
||||||
};
|
};
|
||||||
send_packet(&mut write, &[response_packet], &mut state)
|
send_packet(
|
||||||
.await;
|
&mut write,
|
||||||
|
&[response_packet],
|
||||||
|
&mut state,
|
||||||
|
CompressionType::Oodle,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Player Class Info
|
// Player Class Info
|
||||||
|
@ -403,8 +443,13 @@ async fn main() {
|
||||||
target_actor: state.player_id.unwrap(),
|
target_actor: state.player_id.unwrap(),
|
||||||
segment_type: SegmentType::Ipc { data: ipc },
|
segment_type: SegmentType::Ipc { data: ipc },
|
||||||
};
|
};
|
||||||
send_packet(&mut write, &[response_packet], &mut state)
|
send_packet(
|
||||||
.await;
|
&mut write,
|
||||||
|
&[response_packet],
|
||||||
|
&mut state,
|
||||||
|
CompressionType::Oodle,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init Zone
|
// Init Zone
|
||||||
|
@ -449,9 +494,38 @@ async fn main() {
|
||||||
target_actor: state.player_id.unwrap(),
|
target_actor: state.player_id.unwrap(),
|
||||||
segment_type: SegmentType::Ipc { data: ipc },
|
segment_type: SegmentType::Ipc { data: ipc },
|
||||||
};
|
};
|
||||||
send_packet(&mut write, &[response_packet], &mut state)
|
send_packet(
|
||||||
.await;
|
&mut write,
|
||||||
|
&[response_packet],
|
||||||
|
&mut state,
|
||||||
|
CompressionType::Oodle,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ?????
|
||||||
|
/*{
|
||||||
|
let ipc = IPCSegment {
|
||||||
|
unk1: 0,
|
||||||
|
unk2: 0,
|
||||||
|
op_code: IPCOpCode::InitRequest,
|
||||||
|
server_id: 0,
|
||||||
|
timestamp: timestamp_secs(),
|
||||||
|
data: IPCStructData::InitResponse {
|
||||||
|
unk1: 0,
|
||||||
|
character_id: state.player_id.unwrap(),
|
||||||
|
unk2: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let response_packet = PacketSegment {
|
||||||
|
source_actor: state.player_id.unwrap(),
|
||||||
|
target_actor: state.player_id.unwrap(),
|
||||||
|
segment_type: SegmentType::Ipc { data: ipc },
|
||||||
|
};
|
||||||
|
send_packet(&mut write, &[response_packet], &mut state, CompressionType::Oodle)
|
||||||
|
.await;
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
_ => panic!(
|
_ => panic!(
|
||||||
"The server is recieving a IPC response or unknown packet!"
|
"The server is recieving a IPC response or unknown packet!"
|
||||||
|
|
|
@ -33,6 +33,14 @@ pub(crate) fn decompress(
|
||||||
crate::packet::CompressionType::Oodle => oodle.decode(data, header.uncompressed_size),
|
crate::packet::CompressionType::Oodle => oodle.decode(data, header.uncompressed_size),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if header.compression_type == crate::packet::CompressionType::Oodle {
|
||||||
|
assert_eq!(
|
||||||
|
data.len(),
|
||||||
|
header.uncompressed_size as usize,
|
||||||
|
"Decompressed data does not match the expected length!"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
write("decompressed.bin", &data).unwrap();
|
write("decompressed.bin", &data).unwrap();
|
||||||
|
|
||||||
let mut cursor = Cursor::new(&data);
|
let mut cursor = Cursor::new(&data);
|
||||||
|
|
20
src/ipc.rs
20
src/ipc.rs
|
@ -32,7 +32,7 @@ pub enum IPCOpCode {
|
||||||
/// Sent by the client when they successfully initialize with the server, and they need several bits of information (e.g. what zone to load)
|
/// Sent by the client when they successfully initialize with the server, and they need several bits of information (e.g. what zone to load)
|
||||||
InitRequest = 0x2ED,
|
InitRequest = 0x2ED,
|
||||||
/// Sent by the server as response to ZoneInitRequest.
|
/// Sent by the server as response to ZoneInitRequest.
|
||||||
InitResponse = 280, // TODO: probably wrong!
|
InitResponse = 0x1EF,
|
||||||
/// Sent by the server that tells the client which zone to load
|
/// Sent by the server that tells the client which zone to load
|
||||||
InitZone = 0x0311,
|
InitZone = 0x0311,
|
||||||
/// Sent by the server for... something
|
/// Sent by the server for... something
|
||||||
|
@ -276,7 +276,7 @@ pub enum IPCStructData {
|
||||||
host: String,
|
host: String,
|
||||||
},
|
},
|
||||||
#[br(pre_assert(false))]
|
#[br(pre_assert(false))]
|
||||||
InitializeChat { unk: [u8; 24] },
|
InitializeChat { unk: [u8; 8] },
|
||||||
#[br(pre_assert(false))]
|
#[br(pre_assert(false))]
|
||||||
InitResponse {
|
InitResponse {
|
||||||
unk1: u64,
|
unk1: u64,
|
||||||
|
@ -521,8 +521,8 @@ impl IPCSegment {
|
||||||
IPCStructData::LobbyCharacterAction { .. } => todo!(),
|
IPCStructData::LobbyCharacterAction { .. } => todo!(),
|
||||||
IPCStructData::LobbyEnterWorld { .. } => 160,
|
IPCStructData::LobbyEnterWorld { .. } => 160,
|
||||||
IPCStructData::RequestEnterWorld { .. } => todo!(),
|
IPCStructData::RequestEnterWorld { .. } => todo!(),
|
||||||
IPCStructData::InitializeChat { .. } => 24,
|
IPCStructData::InitializeChat { .. } => 8,
|
||||||
IPCStructData::InitRequest { .. } => todo!(),
|
IPCStructData::InitRequest { .. } => 16,
|
||||||
IPCStructData::InitResponse { .. } => 16,
|
IPCStructData::InitResponse { .. } => 16,
|
||||||
IPCStructData::InitZone { .. } => 103,
|
IPCStructData::InitZone { .. } => 103,
|
||||||
IPCStructData::ActorControlSelf { .. } => 32,
|
IPCStructData::ActorControlSelf { .. } => 32,
|
||||||
|
@ -571,7 +571,17 @@ mod tests {
|
||||||
unk8: 0,
|
unk8: 0,
|
||||||
entitled_expansion: 0,
|
entitled_expansion: 0,
|
||||||
characters: Vec::new(),
|
characters: Vec::new(),
|
||||||
}
|
},
|
||||||
|
IPCStructData::ActorControlSelf {
|
||||||
|
category: ActorControlType::SetCharaGearParamUI,
|
||||||
|
param1: 0,
|
||||||
|
param2: 0,
|
||||||
|
param3: 0,
|
||||||
|
param4: 0,
|
||||||
|
param5: 0,
|
||||||
|
param6: 0,
|
||||||
|
},
|
||||||
|
IPCStructData::InitializeChat { unk: [0; 8] },
|
||||||
];
|
];
|
||||||
|
|
||||||
for ipc in &ipc_types {
|
for ipc in &ipc_types {
|
||||||
|
|
|
@ -151,4 +151,21 @@ impl FFXIVOodle {
|
||||||
out_buf
|
out_buf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn encode(&mut self, input: Vec<u8>) -> Vec<u8> {
|
||||||
|
unsafe {
|
||||||
|
let mut out_buf: Vec<u8> = vec![0u8; input.len()];
|
||||||
|
let mut in_buf = input.to_vec();
|
||||||
|
let len = OodleNetwork1TCP_Encode(
|
||||||
|
self.state.as_mut_ptr() as *mut c_void,
|
||||||
|
self.shared.as_mut_ptr() as *mut c_void,
|
||||||
|
in_buf.as_mut_ptr() as *const c_void,
|
||||||
|
in_buf.len().try_into().unwrap(),
|
||||||
|
out_buf.as_mut_ptr() as *mut c_void,
|
||||||
|
);
|
||||||
|
|
||||||
|
out_buf.truncate(len as usize);
|
||||||
|
out_buf
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,7 @@ pub enum SegmentType {
|
||||||
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
#[brw(repr = u8)]
|
#[brw(repr = u8)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum CompressionType {
|
pub enum CompressionType {
|
||||||
Uncompressed = 0,
|
Uncompressed = 0,
|
||||||
Oodle = 2,
|
Oodle = 2,
|
||||||
|
@ -153,6 +153,7 @@ pub async fn send_packet(
|
||||||
socket: &mut WriteHalf<TcpStream>,
|
socket: &mut WriteHalf<TcpStream>,
|
||||||
segments: &[PacketSegment],
|
segments: &[PacketSegment],
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
|
compression_type: CompressionType,
|
||||||
) {
|
) {
|
||||||
let timestamp: u64 = SystemTime::now()
|
let timestamp: u64 = SystemTime::now()
|
||||||
.duration_since(UNIX_EPOCH)
|
.duration_since(UNIX_EPOCH)
|
||||||
|
@ -161,39 +162,45 @@ pub async fn send_packet(
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut total_segment_size = 0;
|
let mut segments_buffer = Cursor::new(Vec::new());
|
||||||
for segment in segments {
|
for segment in segments {
|
||||||
total_segment_size += segment.calc_size();
|
segment
|
||||||
|
.write_le_args(
|
||||||
|
&mut segments_buffer,
|
||||||
|
(state.client_key.as_ref().map(|s: &[u8; 16]| s.as_slice()),),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let segments_buffer = segments_buffer.into_inner();
|
||||||
|
|
||||||
|
let mut uncompressed_size = 0;
|
||||||
|
let data = match compression_type {
|
||||||
|
CompressionType::Uncompressed => segments_buffer,
|
||||||
|
CompressionType::Oodle => {
|
||||||
|
uncompressed_size = segments_buffer.len();
|
||||||
|
state.clientbound_oodle.encode(segments_buffer)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let size = std::mem::size_of::<PacketHeader>() + data.len();
|
||||||
|
|
||||||
let header = PacketHeader {
|
let header = PacketHeader {
|
||||||
unk1: 0xE2465DFF41A05252, // wtf?
|
unk1: 0xE2465DFF41A05252, // wtf?
|
||||||
unk2: 0x75C4997B4D642A7F, // wtf? x2
|
unk2: 0x75C4997B4D642A7F, // wtf? x2
|
||||||
timestamp,
|
timestamp,
|
||||||
size: std::mem::size_of::<PacketHeader>() as u32 + total_segment_size,
|
size: size as u32,
|
||||||
connection_type: ConnectionType::Lobby,
|
connection_type: ConnectionType::Lobby,
|
||||||
segment_count: segments.len() as u16,
|
segment_count: segments.len() as u16,
|
||||||
unk3: 0,
|
unk3: 0,
|
||||||
compression_type: CompressionType::Uncompressed,
|
compression_type,
|
||||||
unk4: 0,
|
unk4: 0,
|
||||||
uncompressed_size: 0,
|
uncompressed_size: uncompressed_size as u32,
|
||||||
};
|
|
||||||
|
|
||||||
let packet = Packet {
|
|
||||||
header,
|
|
||||||
segments: segments.to_vec(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut cursor = Cursor::new(Vec::new());
|
let mut cursor = Cursor::new(Vec::new());
|
||||||
packet
|
header.write_le(&mut cursor).unwrap();
|
||||||
.write_le_args(
|
std::io::Write::write_all(&mut cursor, &data).unwrap();
|
||||||
&mut cursor,
|
|
||||||
(
|
|
||||||
&mut state.oodle,
|
|
||||||
state.client_key.as_ref().map(|s: &[u8; 16]| s.as_slice()),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let buffer = cursor.into_inner();
|
let buffer = cursor.into_inner();
|
||||||
|
|
||||||
|
@ -210,7 +217,8 @@ pub async fn send_packet(
|
||||||
pub struct State {
|
pub struct State {
|
||||||
pub client_key: Option<[u8; 16]>,
|
pub client_key: Option<[u8; 16]>,
|
||||||
pub session_id: Option<String>,
|
pub session_id: Option<String>,
|
||||||
pub oodle: FFXIVOodle,
|
pub serverbound_oodle: FFXIVOodle,
|
||||||
|
pub clientbound_oodle: FFXIVOodle,
|
||||||
pub player_id: Option<u32>,
|
pub player_id: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,7 +228,7 @@ pub async fn parse_packet(data: &[u8], state: &mut State) -> (Vec<PacketSegment>
|
||||||
match Packet::read_le_args(
|
match Packet::read_le_args(
|
||||||
&mut cursor,
|
&mut cursor,
|
||||||
(
|
(
|
||||||
&mut state.oodle,
|
&mut state.serverbound_oodle,
|
||||||
state.client_key.as_ref().map(|s: &[u8; 16]| s.as_slice()),
|
state.client_key.as_ref().map(|s: &[u8; 16]| s.as_slice()),
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
|
@ -256,7 +264,13 @@ pub async fn send_keep_alive(
|
||||||
target_actor: 0,
|
target_actor: 0,
|
||||||
segment_type: SegmentType::KeepAliveResponse { id, timestamp },
|
segment_type: SegmentType::KeepAliveResponse { id, timestamp },
|
||||||
};
|
};
|
||||||
send_packet(socket, &[response_packet], state).await;
|
send_packet(
|
||||||
|
socket,
|
||||||
|
&[response_packet],
|
||||||
|
state,
|
||||||
|
CompressionType::Uncompressed,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
Loading…
Add table
Reference in a new issue