1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-04-20 14:47:45 +00:00

Multiple fixes attempting to fix packet errors

It doesn't unfortunately, there's still a bunch of errors when decoding
packets. These are all decent fixes though.
This commit is contained in:
Joshua Goins 2025-03-30 21:42:46 -04:00
parent b01ec22950
commit eae962cc85
10 changed files with 106 additions and 47 deletions

View file

@ -1,3 +1,4 @@
use kawari::RECEIVE_BUFFER_SIZE;
use kawari::common::GameData; use kawari::common::GameData;
use kawari::common::custom_ipc::CustomIpcData; use kawari::common::custom_ipc::CustomIpcData;
use kawari::common::custom_ipc::CustomIpcSegment; use kawari::common::custom_ipc::CustomIpcSegment;
@ -45,7 +46,7 @@ async fn main() {
}; };
tokio::spawn(async move { tokio::spawn(async move {
let mut buf = [0; 2056]; let mut buf = vec![0; RECEIVE_BUFFER_SIZE];
loop { loop {
let n = connection let n = connection
.socket .socket
@ -54,8 +55,6 @@ async fn main() {
.expect("Failed to read data!"); .expect("Failed to read data!");
if n != 0 { if n != 0 {
tracing::info!("read {} bytes", n);
let (segments, _) = connection.parse_packet(&buf[..n]).await; let (segments, _) = connection.parse_packet(&buf[..n]).await;
for segment in &segments { for segment in &segments {
match &segment.segment_type { match &segment.segment_type {

View file

@ -1,6 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use kawari::RECEIVE_BUFFER_SIZE;
use kawari::common::custom_ipc::{CustomIpcData, CustomIpcSegment, CustomIpcType}; use kawari::common::custom_ipc::{CustomIpcData, CustomIpcSegment, CustomIpcType};
use kawari::common::{GameData, ObjectId, timestamp_secs}; use kawari::common::{GameData, ObjectId, timestamp_secs};
use kawari::common::{Position, determine_initial_starting_zone}; use kawari::common::{Position, determine_initial_starting_zone};
@ -28,6 +29,7 @@ use kawari::world::{
SocialList, SocialList,
}, },
}; };
use mlua::{Function, Lua}; use mlua::{Function, Lua};
use tokio::io::AsyncReadExt; use tokio::io::AsyncReadExt;
use tokio::join; use tokio::join;
@ -162,18 +164,17 @@ pub fn spawn_client(info: ZoneConnection) {
id: *id, id: *id,
ip: *ip, ip: *ip,
channel: send, channel: send,
kill, //kill,
}; };
let _ = my_send.send(handle); let _ = my_send.send(handle);
} }
async fn start_client(my_handle: oneshot::Receiver<ClientHandle>, mut data: ClientData) { async fn start_client(my_handle: oneshot::Receiver<ClientHandle>, data: ClientData) {
// Recieve client information from global // Recieve client information from global
let my_handle = match my_handle.await { let my_handle = match my_handle.await {
Ok(my_handle) => my_handle, Ok(my_handle) => my_handle,
Err(_) => return, Err(_) => return,
}; };
data.handle.send(ToServer::NewClient(my_handle)).await;
let connection = data.connection; let connection = data.connection;
let recv = data.recv; let recv = data.recv;
@ -182,7 +183,7 @@ async fn start_client(my_handle: oneshot::Receiver<ClientHandle>, mut data: Clie
let (internal_send, internal_recv) = unbounded_channel(); let (internal_send, internal_recv) = unbounded_channel();
join! { join! {
client_loop(connection, internal_recv), client_loop(connection, internal_recv, my_handle),
client_server_loop(recv, internal_send) client_server_loop(recv, internal_send)
}; };
} }
@ -202,6 +203,7 @@ async fn client_server_loop(
async fn client_loop( async fn client_loop(
mut connection: ZoneConnection, mut connection: ZoneConnection,
mut internal_recv: UnboundedReceiver<FromServer>, mut internal_recv: UnboundedReceiver<FromServer>,
client_handle: ClientHandle,
) { ) {
let database = connection.database.clone(); let database = connection.database.clone();
let game_data = connection.gamedata.clone(); let game_data = connection.gamedata.clone();
@ -213,9 +215,10 @@ async fn client_loop(
let mut lua_player = LuaPlayer::default(); let mut lua_player = LuaPlayer::default();
let mut buf = [0; 4096]; let mut buf = vec![0; RECEIVE_BUFFER_SIZE];
loop { loop {
tokio::select! { tokio::select! {
biased; // client data should always be prioritized
Ok(n) = connection.socket.read(&mut buf) => { Ok(n) = connection.socket.read(&mut buf) => {
if n > 0 { if n > 0 {
let (segments, connection_type) = connection.parse_packet(&buf[..n]).await; let (segments, connection_type) = connection.parse_packet(&buf[..n]).await;
@ -225,11 +228,21 @@ async fn client_loop(
// for some reason they send a string representation // for some reason they send a string representation
let actor_id = actor_id.parse::<u32>().unwrap(); let actor_id = actor_id.parse::<u32>().unwrap();
// collect actor data // initialize player data if it doesn't exist'
if connection.player_data.actor_id == 0 {
connection.player_data = database.find_player_data(actor_id); connection.player_data = database.find_player_data(actor_id);
}
// collect actor data
connection.initialize(&connection_type, actor_id).await; connection.initialize(&connection_type, actor_id).await;
if connection_type == ConnectionType::Zone {
exit_position = Some(connection.player_data.position); exit_position = Some(connection.player_data.position);
exit_rotation = Some(connection.player_data.rotation); exit_rotation = Some(connection.player_data.rotation);
// tell the server we exist, now that we confirmed we are a legitimate connection
connection.handle.send(ToServer::NewClient(client_handle.clone())).await;
}
} }
SegmentType::Ipc { data } => { SegmentType::Ipc { data } => {
match &data.data { match &data.data {
@ -1040,14 +1053,17 @@ async fn client_loop(
lua_player.status_effects = connection.status_effects.clone(); lua_player.status_effects = connection.status_effects.clone();
} }
} }
msg = internal_recv.recv() => match msg { /*msg = internal_recv.recv() => match msg {
Some(msg) => match msg { Some(msg) => match msg {
FromServer::Message(msg)=>connection.send_message(&msg).await, FromServer::Message(msg)=>connection.send_message(&msg).await,
FromServer::ActorSpawn(actor) => connection.spawn_actor(actor).await, FromServer::ActorSpawn(actor) => {
tracing::info!("connection {:?} is recieving an actorspawn!", connection.id);
connection.spawn_actor(actor).await
},
FromServer::ActorMove(actor_id, position) => connection.set_actor_position(actor_id, position).await, FromServer::ActorMove(actor_id, position) => connection.set_actor_position(actor_id, position).await,
}, },
None => break, None => break,
} }*/
} }
} }
} }

View file

@ -21,7 +21,7 @@ impl ReadWriteIpcSegment for CustomIpcSegment {
CustomIpcType::CheckNameIsAvailable => CHAR_NAME_MAX_LENGTH as u32, CustomIpcType::CheckNameIsAvailable => CHAR_NAME_MAX_LENGTH as u32,
CustomIpcType::NameIsAvailableResponse => 1, CustomIpcType::NameIsAvailableResponse => 1,
CustomIpcType::RequestCharacterList => 4, CustomIpcType::RequestCharacterList => 4,
CustomIpcType::RequestCharacterListRepsonse => 1184 * 8, CustomIpcType::RequestCharacterListRepsonse => 1 + (1184 * 8),
CustomIpcType::DeleteCharacter => 4, CustomIpcType::DeleteCharacter => 4,
CustomIpcType::CharacterDeleted => 1, CustomIpcType::CharacterDeleted => 1,
} }
@ -95,6 +95,7 @@ pub enum CustomIpcData {
#[bw(calc = characters.len() as u8)] #[bw(calc = characters.len() as u8)]
num_characters: u8, num_characters: u8,
#[br(count = num_characters)] #[br(count = num_characters)]
#[brw(pad_size_to = 1184 * 8)]
characters: Vec<CharacterDetails>, // TODO: maybe chunk this into 4 parts ala the lobby server? characters: Vec<CharacterDetails>, // TODO: maybe chunk this into 4 parts ala the lobby server?
}, },
#[br(pre_assert(*magic == CustomIpcType::DeleteCharacter))] #[br(pre_assert(*magic == CustomIpcType::DeleteCharacter))]

View file

@ -7,6 +7,12 @@ pub struct GameData {
pub game_data: physis::gamedata::GameData, pub game_data: physis::gamedata::GameData,
} }
impl Default for GameData {
fn default() -> Self {
Self::new()
}
}
impl GameData { impl GameData {
pub fn new() -> Self { pub fn new() -> Self {
let config = get_config(); let config = get_config();

View file

@ -82,7 +82,7 @@ pub(crate) fn read_packed_float(packed: u16) -> f32 {
} }
pub(crate) fn write_packed_float(float: f32) -> u16 { pub(crate) fn write_packed_float(float: f32) -> u16 {
(((float + 1000.0) * 100.0) * 0.32767501) as u16 (((float + 1000.0) * 100.0) * 0.327_675) as u16
} }
pub(crate) fn read_packed_position(packed: [u16; 3]) -> Position { pub(crate) fn read_packed_position(packed: [u16; 3]) -> Position {

View file

@ -40,6 +40,8 @@ pub mod opcodes;
/// Used in the encryption key. /// Used in the encryption key.
const GAME_VERSION: u16 = 7000; const GAME_VERSION: u16 = 7000;
pub const RECEIVE_BUFFER_SIZE: usize = 32000;
/// Supported boot version. /// Supported boot version.
pub const SUPPORTED_BOOT_VERSION: Version = Version("2025.01.10.0000.0001"); pub const SUPPORTED_BOOT_VERSION: Version = Version("2025.01.10.0000.0001");

View file

@ -3,6 +3,7 @@ use std::cmp::min;
use tokio::{io::AsyncReadExt, net::TcpStream}; use tokio::{io::AsyncReadExt, net::TcpStream};
use crate::{ use crate::{
RECEIVE_BUFFER_SIZE,
blowfish::Blowfish, blowfish::Blowfish,
common::{ common::{
custom_ipc::{CustomIpcData, CustomIpcSegment, CustomIpcType}, custom_ipc::{CustomIpcData, CustomIpcSegment, CustomIpcType},
@ -589,11 +590,9 @@ pub async fn send_custom_world_packet(segment: CustomIpcSegment) -> Option<Custo
.await; .await;
// read response // read response
let mut buf = [0; 10024]; // TODO: this large buffer is just working around these packets not being compressed, but they really should be! let mut buf = vec![0; RECEIVE_BUFFER_SIZE];
let n = stream.read(&mut buf).await.expect("Failed to read data!"); let n = stream.read(&mut buf).await.expect("Failed to read data!");
if n != 0 { if n != 0 {
println!("Got {n} bytes of response!");
let (segments, _) = parse_packet::<CustomIpcSegment>(&buf[..n], &mut packet_state).await; let (segments, _) = parse_packet::<CustomIpcSegment>(&buf[..n], &mut packet_state).await;
return match &segments[0].segment_type { return match &segments[0].segment_type {

View file

@ -105,6 +105,7 @@ pub struct OodleNetwork {
const HT_BITS: i32 = 0x11; const HT_BITS: i32 = 0x11;
const WINDOW_SIZE: usize = 0x100000; const WINDOW_SIZE: usize = 0x100000;
const OODLENETWORK1_DECOMP_BUF_OVERREAD_LEN: usize = 5;
impl OodleNetwork { impl OodleNetwork {
pub fn new() -> OodleNetwork { pub fn new() -> OodleNetwork {
@ -137,13 +138,19 @@ impl OodleNetwork {
} }
} }
pub fn decode(&mut self, mut input: Vec<u8>, decompressed_size: u32) -> Vec<u8> { pub fn decode(&mut self, input: Vec<u8>, decompressed_size: u32) -> Vec<u8> {
let mut padded_buffer = input.clone();
padded_buffer.resize(
padded_buffer.len() + OODLENETWORK1_DECOMP_BUF_OVERREAD_LEN,
0,
);
unsafe { unsafe {
let mut out_buf: Vec<u8> = vec![0u8; decompressed_size.try_into().unwrap()]; let mut out_buf: Vec<u8> = vec![0u8; decompressed_size.try_into().unwrap()];
let success = OodleNetwork1TCP_Decode( let success = OodleNetwork1TCP_Decode(
self.state.as_mut_ptr() as *mut c_void, self.state.as_mut_ptr() as *mut c_void,
self.shared.as_mut_ptr() as *mut c_void, self.shared.as_mut_ptr() as *const c_void,
input.as_mut_ptr() as *const c_void, padded_buffer.as_mut_ptr() as *const c_void,
input.len().try_into().unwrap(), input.len().try_into().unwrap(),
out_buf.as_mut_ptr() as *mut c_void, out_buf.as_mut_ptr() as *mut c_void,
out_buf.len().try_into().unwrap(), out_buf.len().try_into().unwrap(),
@ -162,7 +169,7 @@ impl OodleNetwork {
let mut out_buf: Vec<u8> = vec![0u8; input.len()]; let mut out_buf: Vec<u8> = vec![0u8; input.len()];
let len = OodleNetwork1TCP_Encode( let len = OodleNetwork1TCP_Encode(
self.state.as_mut_ptr() as *mut c_void, self.state.as_mut_ptr() as *mut c_void,
self.shared.as_mut_ptr() as *mut c_void, self.shared.as_mut_ptr() as *const c_void,
input.as_mut_ptr() as *const c_void, input.as_mut_ptr() as *const c_void,
input.len().try_into().unwrap(), input.len().try_into().unwrap(),
out_buf.as_mut_ptr() as *mut c_void, out_buf.as_mut_ptr() as *mut c_void,

View file

@ -6,7 +6,7 @@ use std::{
}, },
}; };
use tokio::{net::TcpStream, sync::mpsc::Sender, task::JoinHandle}; use tokio::{net::TcpStream, sync::mpsc::Sender};
use crate::{ use crate::{
common::{GameData, ObjectId, Position, timestamp_secs}, common::{GameData, ObjectId, Position, timestamp_secs},
@ -61,12 +61,13 @@ pub enum FromServer {
ActorMove(u32, Position), ActorMove(u32, Position),
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct ClientHandle { pub struct ClientHandle {
pub id: ClientId, pub id: ClientId,
pub ip: SocketAddr, pub ip: SocketAddr,
pub channel: Sender<FromServer>, pub channel: Sender<FromServer>,
pub kill: JoinHandle<()>, // TODO: restore, i guess
//pub kill: JoinHandle<()>,
} }
impl ClientHandle { impl ClientHandle {
@ -161,6 +162,17 @@ impl ZoneConnection {
.await; .await;
} }
pub async fn send_chat_segment(&mut self, segment: PacketSegment<ServerZoneIpcSegment>) {
send_packet(
&mut self.socket,
&mut self.state,
ConnectionType::Chat,
CompressionType::Oodle,
&[segment],
)
.await;
}
pub async fn initialize(&mut self, connection_type: &ConnectionType, actor_id: u32) { pub async fn initialize(&mut self, connection_type: &ConnectionType, actor_id: u32) {
// some still hardcoded values // some still hardcoded values
self.player_data.classjob_id = 1; self.player_data.classjob_id = 1;
@ -170,6 +182,10 @@ impl ZoneConnection {
self.player_data.curr_mp = 10000; self.player_data.curr_mp = 10000;
self.player_data.max_mp = 10000; self.player_data.max_mp = 10000;
match connection_type {
ConnectionType::Zone => {
tracing::info!("Client {actor_id} is initializing zone session...");
// We have send THEM a keep alive // We have send THEM a keep alive
{ {
self.send_segment(PacketSegment { self.send_segment(PacketSegment {
@ -183,10 +199,6 @@ impl ZoneConnection {
.await; .await;
} }
match connection_type {
ConnectionType::Zone => {
tracing::info!("Client {actor_id} is initializing zone session...");
self.send_segment(PacketSegment { self.send_segment(PacketSegment {
source_actor: 0, source_actor: 0,
target_actor: 0, target_actor: 0,
@ -200,8 +212,21 @@ impl ZoneConnection {
ConnectionType::Chat => { ConnectionType::Chat => {
tracing::info!("Client {actor_id} is initializing chat session..."); tracing::info!("Client {actor_id} is initializing chat session...");
// We have send THEM a keep alive
{ {
self.send_segment(PacketSegment { self.send_chat_segment(PacketSegment {
source_actor: 0,
target_actor: 0,
segment_type: SegmentType::KeepAlive {
id: 0xE0037603u32,
timestamp: timestamp_secs(),
},
})
.await;
}
{
self.send_chat_segment(PacketSegment {
source_actor: 0, source_actor: 0,
target_actor: 0, target_actor: 0,
segment_type: SegmentType::ZoneInitialize { segment_type: SegmentType::ZoneInitialize {
@ -212,6 +237,9 @@ impl ZoneConnection {
.await; .await;
} }
// we need the actor id at this point!
assert!(self.player_data.actor_id != 0);
{ {
let ipc = ServerZoneIpcSegment { let ipc = ServerZoneIpcSegment {
op_code: ServerZoneIpcType::InitializeChat, op_code: ServerZoneIpcType::InitializeChat,
@ -220,7 +248,7 @@ impl ZoneConnection {
..Default::default() ..Default::default()
}; };
self.send_segment(PacketSegment { self.send_chat_segment(PacketSegment {
source_actor: self.player_data.actor_id, source_actor: self.player_data.actor_id,
target_actor: self.player_data.actor_id, target_actor: self.player_data.actor_id,
segment_type: SegmentType::Ipc { data: ipc }, segment_type: SegmentType::Ipc { data: ipc },
@ -276,6 +304,9 @@ impl ZoneConnection {
} }
pub async fn spawn_actor(&mut self, actor: Actor) { pub async fn spawn_actor(&mut self, actor: Actor) {
// There is no reason for us to spawn our own player again. It's probably a bug!'
assert!(actor.id.0 != self.player_data.actor_id);
let ipc = ServerZoneIpcSegment { let ipc = ServerZoneIpcSegment {
unk1: 20, unk1: 20,
unk2: 0, unk2: 0,
@ -351,7 +382,7 @@ impl ZoneConnection {
} }
// link shell information // link shell information
{ /*{
let ipc = ServerZoneIpcSegment { let ipc = ServerZoneIpcSegment {
op_code: ServerZoneIpcType::LinkShellInformation, op_code: ServerZoneIpcType::LinkShellInformation,
timestamp: timestamp_secs(), timestamp: timestamp_secs(),
@ -365,7 +396,7 @@ impl ZoneConnection {
segment_type: SegmentType::Ipc { data: ipc }, segment_type: SegmentType::Ipc { data: ipc },
}) })
.await; .await;
} }*/
// TODO: send unk16? // TODO: send unk16?
@ -573,7 +604,7 @@ impl ZoneConnection {
} }
} }
return None; None
} }
pub async fn actor_control_self(&mut self, actor_control: ActorControlSelf) { pub async fn actor_control_self(&mut self, actor_control: ActorControlSelf) {

View file

@ -1,13 +1,11 @@
use physis::{ use physis::{
common::{Language, Platform}, common::Language,
gamedata::GameData, gamedata::GameData,
layer::{ layer::{
ExitRangeInstanceObject, InstanceObject, LayerEntryData, LayerGroup, PopRangeInstanceObject, ExitRangeInstanceObject, InstanceObject, LayerEntryData, LayerGroup, PopRangeInstanceObject,
}, },
}; };
use crate::config::get_config;
/// Represents a loaded zone /// Represents a loaded zone
#[derive(Default)] #[derive(Default)]
pub struct Zone { pub struct Zone {