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

Fix a few problems with ClientSelectData, use the same character in both servers

I apparently had a few fields missing here. Also just some general clean up,
make it the same looking character in both the Lobby and World server. I also
chose Gridania as the test zone.
This commit is contained in:
Joshua Goins 2025-03-14 00:30:37 -04:00
parent 5c22532474
commit 640aeb797c
7 changed files with 122 additions and 149 deletions

View file

@ -13,7 +13,7 @@ use kawari::oodle::FFXIVOodle;
use kawari::packet::{
CompressionType, PacketSegment, SegmentType, State, parse_packet, send_keep_alive, send_packet,
};
use kawari::{CONTENT_ID, WORLD_ID, WORLD_NAME, ZONE_ID};
use kawari::{CHAR_NAME, CONTENT_ID, CUSTOMIZE_DATA, WORLD_ID, WORLD_NAME, ZONE_ID};
use tokio::io::{AsyncReadExt, WriteHalf};
use tokio::net::{TcpListener, TcpStream};
@ -396,33 +396,7 @@ async fn send_lobby_info(socket: &mut WriteHalf<TcpStream>, state: &mut State, s
unk9: 0,
zone_id: ZONE_ID as i32,
unk11: 0,
customize: ClientCustomizeData {
race: 3,
gender: 1,
height: 0,
subrace: 0,
face: 1,
hair: 1,
enable_highlights: 1,
skin_tone: 1,
right_eye_color: 1,
hair_tone: 1,
highlights: 1,
facial_features: 1,
facial_feature_color: 1,
eyebrows: 1,
left_eye_color: 1,
eyes: 1,
nose: 1,
jaw: 1,
mouth: 1,
lips_tone_fur_pattern: 1,
race_feature_size: 1,
race_feature_type: 1,
bust: 0,
face_paint: 1,
face_paint_color: 0,
},
customize: CUSTOMIZE_DATA,
unk12: 0,
unk13: 0,
unk14: [0; 10],
@ -432,7 +406,7 @@ async fn send_lobby_info(socket: &mut WriteHalf<TcpStream>, state: &mut State, s
unk18: 0,
unk19: 0,
unk20: 0,
unk21: "hello".to_string(),
unk21: String::new(),
unk22: 0,
unk23: 0,
};
@ -444,7 +418,7 @@ async fn send_lobby_info(socket: &mut WriteHalf<TcpStream>, state: &mut State, s
unk1: [0; 16],
origin_server_id: WORLD_ID,
current_server_id: WORLD_ID,
character_name: "test".to_string(),
character_name: CHAR_NAME.to_string(),
origin_server_name: WORLD_NAME.to_string(),
current_server_name: WORLD_NAME.to_string(),
character_detail_json: select_data.to_json(),

View file

@ -7,10 +7,10 @@ use kawari::packet::{
CompressionType, PacketSegment, SegmentType, State, parse_packet, send_keep_alive, send_packet,
};
use kawari::world::{
ActorControlSelf, ActorControlType, CustomizeData, InitZone, PlayerSetup, PlayerSpawn,
PlayerStats, Position, UpdateClassInfo,
ActorControlSelf, ActorControlType, InitZone, PlayerSetup, PlayerSpawn, PlayerStats, Position,
UpdateClassInfo,
};
use kawari::{CONTENT_ID, WORLD_ID, ZONE_ID};
use kawari::{CHAR_NAME, CONTENT_ID, CUSTOMIZE_DATA, WORLD_ID, ZONE_ID};
use tokio::io::AsyncReadExt;
use tokio::net::TcpListener;
@ -268,7 +268,7 @@ async fn main() {
content_id: CONTENT_ID,
exp: [10000; 32],
levels: [100; 32],
name: "KAWARI".to_string(),
name: CHAR_NAME.to_string(),
..Default::default()
}),
};
@ -499,7 +499,7 @@ async fn main() {
home_world_id: WORLD_ID,
title: 1,
class_job: 35,
name: "Test".to_string(),
name: CHAR_NAME.to_string(),
hp_curr: 100,
hp_max: 100,
mp_curr: 100,
@ -508,35 +508,20 @@ async fn main() {
spawn_index: 1,
state: 1,
gm_rank: 3,
look: CustomizeData {
race: 3,
age: 0,
gender: 1,
height: 5,
subrace: 0,
face: 1,
hair: 2,
enable_highlights: 0,
skin_tone: 4,
right_eye_color: 5,
hair_tone: 2,
highlights: 7,
facial_features: 1,
facial_feature_color: 1,
eyebrows: 2,
left_eye_color: 1,
eyes: 1,
nose: 0,
jaw: 1,
mouth: 0,
lips_tone_fur_pattern: 1,
race_feature_size: 1,
race_feature_type: 1,
bust: 0,
face_paint: 1,
face_paint_color: 1,
},
look: CUSTOMIZE_DATA,
fc_tag: "LOCAL".to_string(),
models: [
0, // head
89, // body
89, // hands
89, // legs
89, // feet
0, // ears
0, // neck
0, // wrists
0, // left finger
0, // right finger
],
..Default::default()
}),
};
@ -731,6 +716,9 @@ async fn main() {
}
}
}
IPCStructData::Unk12 { .. } => {
tracing::info!("Recieved Unk12!");
}
_ => panic!(
"The server is recieving a IPC response or unknown packet!"
),

View file

@ -4,31 +4,32 @@ use serde_json::{Value, json};
#[binrw]
#[derive(Debug, Clone, Default)]
pub struct ClientCustomizeData {
pub race: i32,
pub gender: i32,
pub height: i32,
pub subrace: i32,
pub face: i32,
pub hair: i32,
pub enable_highlights: i32,
pub skin_tone: i32,
pub right_eye_color: i32,
pub hair_tone: i32,
pub highlights: i32,
pub facial_features: i32,
pub facial_feature_color: i32,
pub eyebrows: i32,
pub left_eye_color: i32,
pub eyes: i32,
pub nose: i32,
pub jaw: i32,
pub mouth: i32,
pub lips_tone_fur_pattern: i32,
pub race_feature_size: i32,
pub race_feature_type: i32,
pub bust: i32,
pub face_paint: i32,
pub face_paint_color: i32,
pub race: u8,
pub gender: u8,
pub age: u8,
pub height: u8,
pub subrace: u8,
pub face: u8,
pub hair: u8,
pub enable_highlights: u8,
pub skin_tone: u8,
pub right_eye_color: u8,
pub hair_tone: u8,
pub highlights: u8,
pub facial_features: u8,
pub facial_feature_color: u8,
pub eyebrows: u8,
pub left_eye_color: u8,
pub eyes: u8,
pub nose: u8,
pub jaw: u8,
pub mouth: u8,
pub lips_tone_fur_pattern: u8,
pub race_feature_size: u8,
pub race_feature_type: u8,
pub bust: u8,
pub face_paint: u8,
pub face_paint_color: u8,
}
impl ClientCustomizeData {
@ -36,9 +37,11 @@ impl ClientCustomizeData {
json!([
self.race.to_string(),
self.gender.to_string(),
self.age.to_string(),
self.height.to_string(),
self.subrace.to_string(),
self.face.to_string(),
self.hair.to_string(),
self.enable_highlights.to_string(),
self.skin_tone.to_string(),
self.right_eye_color.to_string(),
@ -63,31 +66,32 @@ impl ClientCustomizeData {
pub fn from_json(json: &Value) -> Self {
Self {
race: json[0].as_str().unwrap().parse::<i32>().unwrap(),
gender: json[1].as_str().unwrap().parse::<i32>().unwrap(),
height: json[2].as_str().unwrap().parse::<i32>().unwrap(),
subrace: json[3].as_str().unwrap().parse::<i32>().unwrap(),
face: json[4].as_str().unwrap().parse::<i32>().unwrap(),
hair: json[5].as_str().unwrap().parse::<i32>().unwrap(),
enable_highlights: json[6].as_str().unwrap().parse::<i32>().unwrap(),
skin_tone: json[7].as_str().unwrap().parse::<i32>().unwrap(),
right_eye_color: json[8].as_str().unwrap().parse::<i32>().unwrap(),
hair_tone: json[9].as_str().unwrap().parse::<i32>().unwrap(),
highlights: json[10].as_str().unwrap().parse::<i32>().unwrap(),
facial_features: json[11].as_str().unwrap().parse::<i32>().unwrap(),
facial_feature_color: json[12].as_str().unwrap().parse::<i32>().unwrap(),
eyebrows: json[13].as_str().unwrap().parse::<i32>().unwrap(),
left_eye_color: json[14].as_str().unwrap().parse::<i32>().unwrap(),
eyes: json[15].as_str().unwrap().parse::<i32>().unwrap(),
nose: json[16].as_str().unwrap().parse::<i32>().unwrap(),
jaw: json[17].as_str().unwrap().parse::<i32>().unwrap(),
mouth: json[18].as_str().unwrap().parse::<i32>().unwrap(),
lips_tone_fur_pattern: json[19].as_str().unwrap().parse::<i32>().unwrap(),
race_feature_size: json[20].as_str().unwrap().parse::<i32>().unwrap(),
race_feature_type: json[21].as_str().unwrap().parse::<i32>().unwrap(),
bust: json[22].as_str().unwrap().parse::<i32>().unwrap(),
face_paint: json[23].as_str().unwrap().parse::<i32>().unwrap(),
face_paint_color: json[24].as_str().unwrap().parse::<i32>().unwrap(),
race: json[0].as_str().unwrap().parse::<u8>().unwrap(),
gender: json[1].as_str().unwrap().parse::<u8>().unwrap(),
height: json[2].as_str().unwrap().parse::<u8>().unwrap(),
subrace: json[3].as_str().unwrap().parse::<u8>().unwrap(),
face: json[4].as_str().unwrap().parse::<u8>().unwrap(),
hair: json[5].as_str().unwrap().parse::<u8>().unwrap(),
enable_highlights: json[6].as_str().unwrap().parse::<u8>().unwrap(),
skin_tone: json[7].as_str().unwrap().parse::<u8>().unwrap(),
right_eye_color: json[8].as_str().unwrap().parse::<u8>().unwrap(),
hair_tone: json[9].as_str().unwrap().parse::<u8>().unwrap(),
highlights: json[10].as_str().unwrap().parse::<u8>().unwrap(),
facial_features: json[11].as_str().unwrap().parse::<u8>().unwrap(),
facial_feature_color: json[12].as_str().unwrap().parse::<u8>().unwrap(),
eyebrows: json[13].as_str().unwrap().parse::<u8>().unwrap(),
left_eye_color: json[14].as_str().unwrap().parse::<u8>().unwrap(),
eyes: json[15].as_str().unwrap().parse::<u8>().unwrap(),
nose: json[16].as_str().unwrap().parse::<u8>().unwrap(),
jaw: json[17].as_str().unwrap().parse::<u8>().unwrap(),
mouth: json[18].as_str().unwrap().parse::<u8>().unwrap(),
lips_tone_fur_pattern: json[19].as_str().unwrap().parse::<u8>().unwrap(),
race_feature_size: json[20].as_str().unwrap().parse::<u8>().unwrap(),
race_feature_type: json[21].as_str().unwrap().parse::<u8>().unwrap(),
bust: json[22].as_str().unwrap().parse::<u8>().unwrap(),
face_paint: json[23].as_str().unwrap().parse::<u8>().unwrap(),
face_paint_color: json[24].as_str().unwrap().parse::<u8>().unwrap(),
age: 1,
}
}
}
@ -109,7 +113,7 @@ pub struct ClientSelectData {
pub customize: ClientCustomizeData,
pub unk12: i32,
pub unk13: i32,
pub unk14: [i32; 10],
pub unk14: [i32; 10], // probably model ids
pub unk15: i32,
pub unk16: i32,
/// If set to 1, the user is granted one opportunity to edit their character and are prompted to re-choose their class.
@ -159,6 +163,6 @@ impl ClientSelectData {
"classid": 116,
});
serde_json::to_string_pretty(&obj).unwrap()
serde_json::to_string(&obj).unwrap()
}
}

View file

@ -98,6 +98,8 @@ pub enum IPCOpCode {
Unk11 = 0x156,
// Assumed what this is, but probably incorrect
CharacterCreated = 0xE,
// Unknown, client sends this for ???
Unk12 = 0x0E9,
}
#[binrw]
@ -357,6 +359,10 @@ pub enum IPCStructData {
#[br(dbg)]
unk: [u8; 24],
},
#[br(pre_assert(*magic == IPCOpCode::Unk12))]
Unk12 {
unk: [u8; 8], // TODO: unknown
},
// Server->Client IPC
#[br(pre_assert(false))]
@ -571,6 +577,7 @@ impl IPCSegment {
IPCStructData::Unk11 { .. } => 32,
IPCStructData::NameRejection { .. } => 536,
IPCStructData::CharacterCreated { .. } => 2568,
IPCStructData::Unk12 { .. } => todo!(),
}
}
}

View file

@ -1,3 +1,4 @@
use client_select_data::ClientCustomizeData;
use minijinja::Environment;
use rand::Rng;
use rand::distributions::Alphanumeric;
@ -20,13 +21,44 @@ pub mod world;
pub const WORLD_ID: u16 = 63;
pub const WORLD_NAME: &str = "KAWARI";
pub const ZONE_ID: u16 = 1255;
pub const ZONE_ID: u16 = 132;
pub const CONTENT_ID: u64 = 11111111111111111;
pub const CUSTOMIZE_DATA: ClientCustomizeData = ClientCustomizeData {
race: 4,
gender: 1,
age: 1,
height: 50,
subrace: 7,
face: 3,
hair: 5,
enable_highlights: 0,
skin_tone: 10,
right_eye_color: 75,
hair_tone: 50,
highlights: 0,
facial_features: 1,
facial_feature_color: 19,
eyebrows: 1,
left_eye_color: 75,
eyes: 1,
nose: 0,
jaw: 1,
mouth: 1,
lips_tone_fur_pattern: 169,
race_feature_size: 100,
race_feature_type: 1,
bust: 100,
face_paint: 0,
face_paint_color: 167,
};
/// Maxmimum length of a character's name.
pub const CHAR_NAME_MAX_LENGTH: usize = 32;
pub const CHAR_NAME: &str = "Test User";
pub fn generate_sid() -> String {
let random_id: String = rand::thread_rng()
.sample_iter(&Alphanumeric)

View file

@ -1,5 +1,4 @@
mod player_spawn;
pub use player_spawn::CustomizeData;
pub use player_spawn::PlayerSpawn;
mod position;

View file

@ -88,7 +88,7 @@ pub struct PlayerSpawn {
#[br(map = read_string)]
#[bw(map = write_string)]
pub name: String,
pub look: CustomizeData,
pub look: ClientCustomizeData,
#[br(count = 6)]
#[bw(pad_size_to = 6)]
#[br(map = read_string)]
@ -97,37 +97,6 @@ pub struct PlayerSpawn {
pub padding: [u8; 2],
}
#[binrw]
#[derive(Debug, Clone, Default)]
pub struct CustomizeData {
pub race: u8,
pub gender: u8,
pub age: u8,
pub height: u8,
pub subrace: u8,
pub face: u8,
pub hair: u8,
pub enable_highlights: u8,
pub skin_tone: u8,
pub right_eye_color: u8,
pub hair_tone: u8,
pub highlights: u8,
pub facial_features: u8,
pub facial_feature_color: u8,
pub eyebrows: u8,
pub left_eye_color: u8,
pub eyes: u8,
pub nose: u8,
pub jaw: u8,
pub mouth: u8,
pub lips_tone_fur_pattern: u8,
pub race_feature_size: u8,
pub race_feature_type: u8,
pub bust: u8,
pub face_paint: u8,
pub face_paint_color: u8,
}
#[cfg(test)]
mod tests {
use std::{fs::read, io::Cursor, path::PathBuf};