1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-05-02 11:07:45 +00:00

Move model id grabbing to Inventory, fixup ClientSelectData

I wanted to make the lobby screen reflect your currently equipped items,
but that doesn't yet for some reason. These are still good refactorings
though!
This commit is contained in:
Joshua Goins 2025-04-30 23:05:43 -04:00
parent 614e470669
commit ed8ccb86ee
5 changed files with 99 additions and 113 deletions

View file

@ -832,12 +832,16 @@ async fn client_loop(
} }
let mut inventory = Inventory::default(); let mut inventory = Inventory::default();
{
let mut game_data = game_data.lock().unwrap();
// fill inventory // fill inventory
inventory.equip_racial_items( inventory.equip_racial_items(
chara_make.customize.race, chara_make.customize.race,
chara_make.customize.gender, chara_make.customize.gender,
); &mut game_data,
);
}
let (content_id, actor_id) = database.create_player_data( let (content_id, actor_id) = database.create_player_data(
*service_account_id, *service_account_id,
@ -932,11 +936,17 @@ async fn client_loop(
world_name = game_data.get_world_name(config.world.world_id); world_name = game_data.get_world_name(config.world.world_id);
} }
let characters = database.get_character_list( let characters;
*service_account_id, {
config.world.world_id, let mut game_data = game_data.lock().unwrap();
&world_name,
); characters = database.get_character_list(
*service_account_id,
config.world.world_id,
&world_name,
&mut game_data,
);
}
// send response // send response
{ {

View file

@ -3,7 +3,7 @@ use serde_json::json;
use crate::common::CustomizeData; use crate::common::CustomizeData;
// TODO: this isn't really an enum in the game, nor is it a flag either. it's weird! // TODO: this isn't really an enum in the game, nor is it a flag either. it's weird!
#[derive(Clone, Copy)] #[derive(Debug, Clone, Copy)]
#[repr(i32)] #[repr(i32)]
pub enum RemakeMode { pub enum RemakeMode {
/// No remake options are available. /// No remake options are available.
@ -15,10 +15,11 @@ pub enum RemakeMode {
} }
/// See https://github.com/aers/FFXIVClientStructs/blob/main/FFXIVClientStructs/FFXIV/Application/Network/WorkDefinitions/ClientSelectData.cs /// See https://github.com/aers/FFXIVClientStructs/blob/main/FFXIVClientStructs/FFXIV/Application/Network/WorkDefinitions/ClientSelectData.cs
#[derive(Debug)]
pub struct ClientSelectData { pub struct ClientSelectData {
pub game_name_unk: String, pub character_name: String,
pub current_class: i32, pub current_class: i32,
pub class_levels: [i32; 30], pub class_levels: [i32; 32],
pub race: i32, pub race: i32,
pub subrace: i32, pub subrace: i32,
pub gender: i32, pub gender: i32,
@ -32,9 +33,11 @@ pub struct ClientSelectData {
/// The most notable is if your character can be remade, it says "Your character is currently bound by duty..." /// The most notable is if your character can be remade, it says "Your character is currently bound by duty..."
pub content_finder_condition: i32, pub content_finder_condition: i32,
pub customize: CustomizeData, pub customize: CustomizeData,
pub model_main_weapon: i32, pub model_main_weapon: u64,
pub model_sub_weapon: i32, pub model_sub_weapon: i32,
pub unk14: [i32; 10], // probably model ids pub model_ids: [u32; 10],
pub equip_stain: [u32; 10],
pub glasses: [u32; 2],
pub unk15: i32, pub unk15: i32,
pub unk16: i32, pub unk16: i32,
pub remake_mode: RemakeMode, // TODO: upstream a comment about this to FFXIVClientStructs pub remake_mode: RemakeMode, // TODO: upstream a comment about this to FFXIVClientStructs
@ -44,13 +47,12 @@ pub struct ClientSelectData {
pub unk20: i32, pub unk20: i32,
pub world_name: String, pub world_name: String,
pub unk22: i32, pub unk22: i32,
pub unk23: i32,
} }
impl ClientSelectData { impl ClientSelectData {
pub fn to_json(&self) -> String { pub fn to_json(&self) -> String {
let content = json!([ let content = json!([
self.game_name_unk, self.character_name,
self.current_class.to_string(), self.current_class.to_string(),
self.class_levels.map(|x| x.to_string()), self.class_levels.map(|x| x.to_string()),
self.race.to_string(), self.race.to_string(),
@ -66,7 +68,9 @@ impl ClientSelectData {
self.customize.to_json(), self.customize.to_json(),
self.model_main_weapon.to_string(), self.model_main_weapon.to_string(),
self.model_sub_weapon.to_string(), self.model_sub_weapon.to_string(),
self.unk14.map(|x| x.to_string()), self.model_ids.map(|x| x.to_string()),
self.equip_stain.map(|x| x.to_string()),
self.glasses.map(|x| x.to_string()),
self.unk15.to_string(), self.unk15.to_string(),
self.unk16.to_string(), self.unk16.to_string(),
(self.remake_mode as i32).to_string(), (self.remake_mode as i32).to_string(),
@ -75,7 +79,6 @@ impl ClientSelectData {
self.unk20.to_string(), self.unk20.to_string(),
self.world_name, self.world_name,
self.unk22.to_string(), self.unk22.to_string(),
self.unk23.to_string(),
]); ]);
let obj = json!({ let obj = json!({

View file

@ -613,50 +613,17 @@ impl ZoneConnection {
let ipc; let ipc;
{ {
let mut game_data = self.gamedata.lock().unwrap(); let mut game_data = self.gamedata.lock().unwrap();
let equipped = &self.player_data.inventory.equipped; let inventory = &self.player_data.inventory;
ipc = ServerZoneIpcSegment { ipc = ServerZoneIpcSegment {
op_code: ServerZoneIpcType::Equip, op_code: ServerZoneIpcType::Equip,
timestamp: timestamp_secs(), timestamp: timestamp_secs(),
data: ServerZoneIpcData::Equip(Equip { data: ServerZoneIpcData::Equip(Equip {
main_weapon_id: game_data main_weapon_id: inventory.get_main_weapon_id(&mut game_data),
.get_primary_model_id(equipped.main_hand.id)
.unwrap_or(0),
sub_weapon_id: 0, sub_weapon_id: 0,
crest_enable: 0, crest_enable: 0,
pattern_invalid: 0, pattern_invalid: 0,
model_ids: [ model_ids: inventory.get_model_ids(&mut game_data),
game_data
.get_primary_model_id(equipped.head.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(equipped.body.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(equipped.hands.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(equipped.legs.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(equipped.feet.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(equipped.ears.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(equipped.neck.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(equipped.wrists.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(equipped.left_ring.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(equipped.right_ring.id)
.unwrap_or(0) as u32,
],
}), }),
..Default::default() ..Default::default()
}; };
@ -785,7 +752,8 @@ impl ZoneConnection {
let chara_details = self.database.find_chara_make(self.player_data.content_id); let chara_details = self.database.find_chara_make(self.player_data.content_id);
let equipped = &self.player_data.inventory.equipped; let inventory = &self.player_data.inventory;
CommonSpawn { CommonSpawn {
class_job: self.player_data.classjob_id, class_job: self.player_data.classjob_id,
name: chara_details.name, name: chara_details.name,
@ -797,41 +765,8 @@ impl ZoneConnection {
object_kind: ObjectKind::Player(PlayerSubKind::Player), object_kind: ObjectKind::Player(PlayerSubKind::Player),
look: chara_details.chara_make.customize, look: chara_details.chara_make.customize,
display_flags: DisplayFlag::UNK, display_flags: DisplayFlag::UNK,
main_weapon_model: game_data main_weapon_model: inventory.get_main_weapon_id(&mut game_data),
.get_primary_model_id(equipped.main_hand.id) models: inventory.get_model_ids(&mut game_data),
.unwrap_or(0),
models: [
game_data
.get_primary_model_id(equipped.head.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(equipped.body.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(equipped.hands.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(equipped.legs.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(equipped.feet.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(equipped.ears.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(equipped.neck.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(equipped.wrists.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(equipped.left_ring.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(equipped.right_ring.id)
.unwrap_or(0) as u32,
],
pos: exit_position.unwrap_or_default(), pos: exit_position.unwrap_or_default(),
rotation: exit_rotation.unwrap_or(0.0), rotation: exit_rotation.unwrap_or(0.0),
..Default::default() ..Default::default()

View file

@ -5,7 +5,7 @@ use serde::Deserialize;
use crate::{ use crate::{
common::{ common::{
CustomizeData, Position, CustomizeData, GameData, Position,
workdefinitions::{CharaMake, ClientSelectData, RemakeMode}, workdefinitions::{CharaMake, ClientSelectData, RemakeMode},
}, },
lobby::ipc::{CharacterDetails, CharacterFlag}, lobby::ipc::{CharacterDetails, CharacterFlag},
@ -225,6 +225,7 @@ impl WorldDatabase {
service_account_id: u32, service_account_id: u32,
world_id: u16, world_id: u16,
world_name: &str, world_name: &str,
game_data: &mut GameData,
) -> Vec<CharacterDetails> { ) -> Vec<CharacterDetails> {
let connection = self.connection.lock().unwrap(); let connection = self.connection.lock().unwrap();
@ -250,22 +251,24 @@ impl WorldDatabase {
for (index, (content_id, actor_id)) in content_actor_ids.iter().enumerate() { for (index, (content_id, actor_id)) in content_actor_ids.iter().enumerate() {
let mut stmt = connection let mut stmt = connection
.prepare( .prepare(
"SELECT name, chara_make, zone_id FROM character_data WHERE content_id = ?1", "SELECT name, chara_make, zone_id, inventory FROM character_data WHERE content_id = ?1",
) )
.unwrap(); .unwrap();
let result: Result<(String, String, u16), rusqlite::Error> = stmt let result: Result<(String, String, u16, String), rusqlite::Error> = stmt
.query_row((content_id,), |row| { .query_row((content_id,), |row| {
Ok((row.get(0)?, row.get(1)?, row.get(2)?)) Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?))
}); });
if let Ok((name, chara_make, zone_id)) = result { if let Ok((name, chara_make, zone_id, inventory_json)) = result {
let chara_make = CharaMake::from_json(&chara_make); let chara_make = CharaMake::from_json(&chara_make);
let inventory: Inventory = serde_json::from_str(&inventory_json).unwrap();
let select_data = ClientSelectData { let select_data = ClientSelectData {
game_name_unk: "Final Fantasy".to_string(), character_name: name.clone(),
current_class: 2, current_class: 2,
class_levels: [5; 30], class_levels: [5; 32],
race: chara_make.customize.race as i32, race: chara_make.customize.race as i32,
subrace: chara_make.customize.subrace as i32, subrace: chara_make.customize.subrace as i32,
gender: chara_make.customize.gender as i32, gender: chara_make.customize.gender as i32,
@ -277,9 +280,11 @@ impl WorldDatabase {
zone_id: zone_id as i32, zone_id: zone_id as i32,
content_finder_condition: 0, content_finder_condition: 0,
customize: chara_make.customize, customize: chara_make.customize,
model_main_weapon: 0, model_main_weapon: inventory.get_main_weapon_id(game_data),
model_sub_weapon: 0, model_sub_weapon: 0,
unk14: [0; 10], model_ids: inventory.get_model_ids(game_data),
equip_stain: [0; 10],
glasses: [0; 2],
unk15: 0, unk15: 0,
unk16: 0, unk16: 0,
remake_mode: RemakeMode::None, remake_mode: RemakeMode::None,
@ -288,7 +293,6 @@ impl WorldDatabase {
unk20: 0, unk20: 0,
world_name: String::new(), world_name: String::new(),
unk22: 0, unk22: 0,
unk23: 0,
}; };
characters.push(CharacterDetails { characters.push(CharacterDetails {

View file

@ -1,10 +1,7 @@
use physis::{ use physis::common::Language;
common::{Language, Platform},
gamedata::GameData,
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::config::get_config; use crate::common::GameData;
use super::ipc::{ContainerType, InventoryModify}; use super::ipc::{ContainerType, InventoryModify};
@ -179,14 +176,10 @@ impl Default for Inventory {
impl Inventory { impl Inventory {
/// Equip the starting items for a given race /// Equip the starting items for a given race
pub fn equip_racial_items(&mut self, race_id: u8, gender: u8) { pub fn equip_racial_items(&mut self, race_id: u8, gender: u8, game_data: &mut GameData) {
let config = get_config(); let exh = game_data.game_data.read_excel_sheet_header("Race").unwrap();
let mut game_data =
GameData::from_existing(Platform::Win32, &config.game_location).unwrap();
let exh = game_data.read_excel_sheet_header("Race").unwrap();
let exd = game_data let exd = game_data
.game_data
.read_excel_sheet("Race", &exh, Language::English, 0) .read_excel_sheet("Race", &exh, Language::English, 0)
.unwrap(); .unwrap();
@ -278,4 +271,45 @@ impl Inventory {
ContainerType::ArmouryBody => todo!(), ContainerType::ArmouryBody => todo!(),
} }
} }
pub fn get_main_weapon_id(&self, game_data: &mut GameData) -> u64 {
game_data
.get_primary_model_id(self.equipped.main_hand.id)
.unwrap_or(0)
}
pub fn get_model_ids(&self, game_data: &mut GameData) -> [u32; 10] {
[
game_data
.get_primary_model_id(self.equipped.head.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(self.equipped.body.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(self.equipped.hands.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(self.equipped.legs.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(self.equipped.feet.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(self.equipped.ears.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(self.equipped.neck.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(self.equipped.wrists.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(self.equipped.left_ring.id)
.unwrap_or(0) as u32,
game_data
.get_primary_model_id(self.equipped.right_ring.id)
.unwrap_or(0) as u32,
]
}
} }