1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-07-12 00:37:46 +00:00

Refactor large portions of database.rs, and fix the remaining clippy warnings (#111)

* Address warning "warning: very complex type used. Consider factoring parts into `type` definitions"
-But instead of making a new type we just create PlayerData directly

* Address "warning: this expression creates a reference which is immediately dereferenced by the compiler"


* Address "warning: calling .bytes() is very inefficient when data is not in memory"
This commit is contained in:
thedax 2025-07-09 22:59:39 -04:00 committed by GitHub
parent ade08f9697
commit eb2e7a9987
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 107 additions and 119 deletions

View file

@ -1,3 +1,4 @@
use rusqlite::types::{FromSql, FromSqlResult, ValueRef};
use serde_json::{Value, json};
use crate::common::CustomizeData;
@ -13,6 +14,12 @@ pub struct CharaMake {
pub unk2: i32,
}
impl FromSql for CharaMake {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
Ok(CharaMake::from_json(&String::column_result(value)?))
}
}
impl CharaMake {
pub fn from_json(json: &str) -> Self {
let v: Value = serde_json::from_str(json).unwrap();

View file

@ -1,3 +1,4 @@
use rusqlite::types::{FromSql, FromSqlResult, ValueRef};
use serde::{Deserialize, Serialize};
use serde_json::json;
@ -28,6 +29,12 @@ impl TryFrom<i32> for RemakeMode {
}
}
impl FromSql for RemakeMode {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
Ok(RemakeMode::try_from(i32::column_result(value)?).unwrap())
}
}
/// See https://github.com/aers/FFXIVClientStructs/blob/main/FFXIVClientStructs/FFXIV/Application/Network/WorkDefinitions/ClientSelectData.cs
#[derive(Debug)]
pub struct ClientSelectData {

View file

@ -1,9 +1,9 @@
use crate::common::GameData;
use icarus::{ClassJob::ClassJobSheet, Race::RaceSheet};
use physis::common::Language;
use rusqlite::types::{FromSql, FromSqlResult, ValueRef};
use serde::{Deserialize, Serialize};
use crate::common::GameData;
use crate::ipc::zone::ItemOperation;
mod equipped;
@ -78,6 +78,12 @@ impl<'a> IntoIterator for &'a Inventory {
}
}
impl FromSql for Inventory {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
Ok(serde_json::from_str(&String::column_result(value)?).unwrap())
}
}
pub struct InventoryIterator<'a> {
inventory: &'a Inventory,
curr: u16,

View file

@ -1,11 +1,11 @@
use binrw::binrw;
use bitflags::bitflags;
use crate::common::{
CHAR_NAME_MAX_LENGTH, CustomizeData, ObjectId, ObjectTypeId, Position, read_quantized_rotation,
read_string, write_quantized_rotation, write_string,
};
use bitflags::bitflags;
use rusqlite::types::{FromSql, FromSqlResult, ValueRef};
use super::StatusEffect;
@ -116,6 +116,12 @@ pub enum GameMasterRank {
Debug = 90,
}
impl FromSql for GameMasterRank {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
Ok(GameMasterRank::try_from(u8::column_result(value)?).unwrap())
}
}
impl TryFrom<u8> for GameMasterRank {
type Error = ();

View file

@ -1,4 +1,4 @@
use std::{io::Read, sync::Mutex};
use std::{io::BufReader, io::Read, sync::Mutex};
use rusqlite::Connection;
use serde::Deserialize;
@ -10,10 +10,7 @@ use crate::{
workdefinitions::{CharaMake, ClientSelectData, RemakeMode},
},
inventory::{Inventory, Item, Storage},
ipc::{
lobby::{CharacterDetails, CharacterFlag},
zone::GameMasterRank,
},
ipc::lobby::{CharacterDetails, CharacterFlag},
};
use super::PlayerData;
@ -36,6 +33,10 @@ impl Default for WorldDatabase {
}
}
fn json_unpack<T: for<'a> Deserialize<'a>>(json_str: String) -> T {
serde_json::from_str(&json_str).unwrap()
}
impl WorldDatabase {
pub fn new() -> Self {
let connection = Connection::open("world.db").expect("Failed to open database!");
@ -146,7 +147,15 @@ impl WorldDatabase {
}
let charsave_file = archive.by_name("FFXIV_CHARA_01.dat").unwrap();
let charsave_bytes: Vec<u8> = charsave_file.bytes().map(|x| x.unwrap()).collect();
let mut charsave_bytes = Vec::<u8>::new();
let mut bufrdr = BufReader::new(charsave_file);
if let Err(err) = bufrdr.read_to_end(&mut charsave_bytes) {
tracing::error!(
"Unable to read FFXIV_CHARA_01.dat from archive! Additional information: {err}"
);
return;
};
let charsave =
physis::savedata::chardat::CharacterData::from_existing(&charsave_bytes).unwrap();
@ -273,84 +282,40 @@ impl WorldDatabase {
let mut stmt = connection
.prepare("SELECT content_id, service_account_id FROM characters WHERE actor_id = ?1")
.unwrap();
let (content_id, account_id) = stmt
let (content_id, account_id): (u64, u32) = stmt
.query_row((actor_id,), |row| Ok((row.get(0)?, row.get(1)?)))
.unwrap();
stmt = connection
.prepare("SELECT pos_x, pos_y, pos_z, rotation, zone_id, inventory, gm_rank, classjob_id, classjob_levels, classjob_exp, unlocks, aetherytes, completed_quests FROM character_data WHERE content_id = ?1")
.unwrap();
let (
pos_x,
pos_y,
pos_z,
rotation,
zone_id,
inventory_json,
gm_rank,
classjob_id,
classjob_levels,
classjob_exp,
unlocks,
aetherytes,
completed_quests,
): (
f32,
f32,
f32,
f32,
u16,
String,
u8,
i32,
String,
String,
String,
String,
String,
) = stmt
let player_data: PlayerData = stmt
.query_row((content_id,), |row| {
Ok((
row.get(0)?,
row.get(1)?,
row.get(2)?,
row.get(3)?,
row.get(4)?,
row.get(5)?,
row.get(6)?,
row.get(7)?,
row.get(8)?,
row.get(9)?,
row.get(10)?,
row.get(11)?,
row.get(12)?,
))
})
.unwrap();
let inventory = serde_json::from_str(&inventory_json).unwrap();
PlayerData {
Ok(PlayerData {
actor_id,
content_id,
account_id,
position: Position {
x: pos_x,
y: pos_y,
z: pos_z,
x: row.get(0)?,
y: row.get(1)?,
z: row.get(2)?,
},
rotation,
zone_id,
inventory,
gm_rank: GameMasterRank::try_from(gm_rank).unwrap(),
classjob_id: classjob_id as u8,
classjob_levels: serde_json::from_str(&classjob_levels).unwrap(),
classjob_exp: serde_json::from_str(&classjob_exp).unwrap(),
unlocks: serde_json::from_str(&unlocks).unwrap(),
aetherytes: serde_json::from_str(&aetherytes).unwrap(),
completed_quests: serde_json::from_str(&completed_quests).unwrap(),
rotation: row.get(3)?,
zone_id: row.get(4)?,
inventory: row.get(5)?,
gm_rank: row.get(6)?,
classjob_id: row.get(7)?,
classjob_levels: json_unpack::<[i32; 32]>(row.get(8)?),
classjob_exp: json_unpack::<[u32; 32]>(row.get(9)?),
unlocks: json_unpack::<Vec<u8>>(row.get(10)?),
aetherytes: json_unpack::<Vec<u8>>(row.get(11)?),
completed_quests: json_unpack::<Vec<u8>>(row.get(12)?),
..Default::default()
}
})
})
.unwrap();
player_data
}
/// Commit the dynamic player data back to the database
@ -425,56 +390,53 @@ impl WorldDatabase {
)
.unwrap();
let result: Result<(String, String, u16, String, i32, i32, String), rusqlite::Error> =
struct CharaListQuery {
name: String,
chara_make: CharaMake,
zone_id: u16,
inventory: Inventory,
remake_mode: RemakeMode,
classjob_id: i32,
classjob_levels: [i32; 32],
}
let result: Result<CharaListQuery, rusqlite::Error> =
stmt.query_row((content_id,), |row| {
Ok((
row.get(0)?,
row.get(1)?,
row.get(2)?,
row.get(3)?,
row.get(4)?,
row.get(5)?,
row.get(6)?,
))
Ok(CharaListQuery {
name: row.get(0)?,
chara_make: row.get(1)?,
zone_id: row.get(2)?,
inventory: row.get(3)?,
remake_mode: row.get(4)?,
classjob_id: row.get(5)?,
classjob_levels: json_unpack::<[i32; 32]>(row.get(6)?),
})
});
if let Ok((
name,
chara_make,
zone_id,
inventory_json,
remake_mode,
classjob_id,
classjob_levels,
)) = result
{
let chara_make = CharaMake::from_json(&chara_make);
let inventory: Inventory = serde_json::from_str(&inventory_json).unwrap();
if let Ok(query) = result {
let select_data = ClientSelectData {
character_name: name.clone(),
current_class: classjob_id,
class_levels: serde_json::from_str(&classjob_levels).unwrap(),
race: chara_make.customize.race as i32,
subrace: chara_make.customize.subrace as i32,
gender: chara_make.customize.gender as i32,
birth_month: chara_make.birth_month,
birth_day: chara_make.birth_day,
guardian: chara_make.guardian,
character_name: query.name.clone(),
current_class: query.classjob_id,
class_levels: query.classjob_levels,
race: query.chara_make.customize.race as i32,
subrace: query.chara_make.customize.subrace as i32,
gender: query.chara_make.customize.gender as i32,
birth_month: query.chara_make.birth_month,
birth_day: query.chara_make.birth_day,
guardian: query.chara_make.guardian,
unk8: 0,
unk9: 0,
zone_id: zone_id as i32,
zone_id: query.zone_id as i32,
content_finder_condition: 0,
customize: chara_make.customize,
model_main_weapon: inventory.get_main_weapon_id(game_data),
customize: query.chara_make.customize,
model_main_weapon: query.inventory.get_main_weapon_id(game_data),
model_sub_weapon: 0,
model_ids: inventory.get_model_ids(game_data),
model_ids: query.inventory.get_model_ids(game_data),
equip_stain: [0; 10],
glasses: [0; 2],
remake_mode: RemakeMode::try_from(remake_mode).unwrap(),
remake_mode: query.remake_mode,
remake_minutes_remaining: 0,
voice_id: chara_make.voice_id,
voice_id: query.chara_make.voice_id,
unk20: 0,
unk21: 0,
world_name: String::new(),
@ -490,7 +452,7 @@ impl WorldDatabase {
unk1: [255; 6],
origin_server_id: world_id,
current_server_id: world_id,
character_name: name.clone(),
character_name: query.name.clone(),
origin_server_name: world_name.to_string(),
current_server_name: world_name.to_string(),
character_detail_json: select_data.to_json(),

View file

@ -61,7 +61,7 @@ impl Zone {
let mut nvm_path = PathBuf::from(config.filesystem.navimesh_path);
nvm_path.push(&zone.navimesh_path);
Self::load_navimesh(&nvm_path.to_str().unwrap());
Self::load_navimesh(nvm_path.to_str().unwrap());
}
let mut load_lgb = |path: &str| -> Option<LayerGroup> {