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

Replace (most of) the remaining Excel parsing with Physis Sheets

The only one remaining is for Item data, but that can't be ported yet
because the new API only fetches the first page for now.
This commit is contained in:
Joshua Goins 2025-05-09 18:35:44 -04:00
parent df789b52c9
commit 9787126a1b
8 changed files with 53 additions and 110 deletions

2
Cargo.lock generated
View file

@ -1028,7 +1028,7 @@ dependencies = [
[[package]]
name = "physis-sheets"
version = "0.0.0"
source = "git+https://github.com/redstrate/PhysisSheets#ca42d72fc8a76211d67f6bd4698a45c7da0037fb"
source = "git+https://github.com/redstrate/PhysisSheets#4d11e27a1e31bb482184626d1e7a51fc5e673674"
dependencies = [
"physis",
]

View file

@ -104,4 +104,4 @@ rkon = { version = "0.1" }
tower-http = { version = "0.6", features = ["fs"] }
# excel sheet data
physis-sheets = { git = "https://github.com/redstrate/PhysisSheets", features = ["Warp"], default-features = false }
physis-sheets = { git = "https://github.com/redstrate/PhysisSheets", features = ["Warp", "Tribe", "ClassJob", "World", "TerritoryType", "Race"], default-features = false }

View file

@ -27,7 +27,9 @@ async fn main() {
tracing::info!("Server started on {addr}");
let mut game_data = GameData::new();
let world_name = game_data.get_world_name(config.world.world_id);
let world_name = game_data
.get_world_name(config.world.world_id)
.expect("Unknown world name");
loop {
let (socket, _) = listener.accept().await.unwrap();

View file

@ -1,7 +1,9 @@
use physis::common::{Language, Platform};
use physis::exd::{EXD, ExcelRowKind};
use physis::exh::EXH;
use physis_sheets::Warp::Warp;
use physis_sheets::ClassJob::ClassJobSheet;
use physis_sheets::World::WorldSheet;
use physis_sheets::{Tribe::TribeSheet, Warp::WarpSheet};
use crate::{common::Attributes, config::get_config};
@ -44,72 +46,35 @@ impl GameData {
}
/// Gets the world name from an id into the World Excel sheet.
pub fn get_world_name(&mut self, world_id: u16) -> String {
let exh = self.game_data.read_excel_sheet_header("World").unwrap();
let exd = self
.game_data
.read_excel_sheet("World", &exh, Language::None, 0)
.unwrap();
pub fn get_world_name(&mut self, world_id: u16) -> Option<String> {
let sheet = WorldSheet::read_from(&mut self.game_data, Language::None)?;
let row = sheet.get_row(world_id as u32)?;
let ExcelRowKind::SingleRow(world_row) = &exd.get_row(world_id as u32).unwrap() else {
panic!("Expected a single row!")
};
let physis::exd::ColumnData::String(name) = &world_row.columns[1] else {
panic!("Unexpected type!");
};
name.clone()
row.Name().into_string().map(|x| x.clone())
}
/// Gets the starting city-state from a given class/job id.
pub fn get_citystate(&mut self, classjob_id: u16) -> u8 {
let exh = self.game_data.read_excel_sheet_header("ClassJob").unwrap();
let exd = self
.game_data
.read_excel_sheet("ClassJob", &exh, Language::English, 0)
.unwrap();
pub fn get_citystate(&mut self, classjob_id: u16) -> Option<u8> {
let sheet = ClassJobSheet::read_from(&mut self.game_data, Language::English)?;
let row = sheet.get_row(classjob_id as u32)?;
let ExcelRowKind::SingleRow(world_row) = &exd.get_row(classjob_id as u32).unwrap() else {
panic!("Expected a single row!")
};
let physis::exd::ColumnData::UInt8(town_id) = &world_row.columns[33] else {
panic!("Unexpected type!");
};
*town_id
row.StartingTown().into_u8().map(|x| *x)
}
pub fn get_racial_base_attributes(&mut self, tribe_id: u8) -> Attributes {
pub fn get_racial_base_attributes(&mut self, tribe_id: u8) -> Option<Attributes> {
// The Tribe Excel sheet only has deltas (e.g. 2 or -2) which are applied to a base 20 number... from somewhere
let base_stat = 20;
let exh = self.game_data.read_excel_sheet_header("Tribe").unwrap();
let exd = self
.game_data
.read_excel_sheet("Tribe", &exh, Language::English, 0)
.unwrap();
let sheet = TribeSheet::read_from(&mut self.game_data, Language::English)?;
let row = sheet.get_row(tribe_id as u32)?;
let ExcelRowKind::SingleRow(tribe_row) = &exd.get_row(tribe_id as u32).unwrap() else {
panic!("Expected a single row!")
};
let get_column = |column_index: usize| {
let physis::exd::ColumnData::Int8(delta) = &tribe_row.columns[column_index] else {
panic!("Unexpected type!");
};
*delta
};
Attributes {
strength: (base_stat + get_column(4)) as u32,
dexterity: (base_stat + get_column(6)) as u32,
vitality: (base_stat + get_column(5)) as u32,
intelligence: (base_stat + get_column(7)) as u32,
mind: (base_stat + get_column(8)) as u32,
}
Some(Attributes {
strength: (base_stat + row.STR().into_i8()?) as u32,
dexterity: (base_stat + row.DEX().into_i8()?) as u32,
vitality: (base_stat + row.VIT().into_i8()?) as u32,
intelligence: (base_stat + row.INT().into_i8()?) as u32,
mind: (base_stat + row.MND().into_i8()?) as u32,
})
}
/// Gets the primary model ID for a given item ID
@ -133,9 +98,8 @@ impl GameData {
/// Returns the pop range object id that's associated with the warp id
pub fn get_warp(&mut self, warp_id: u32) -> Option<(u32, u16)> {
let warp_sheet = Warp::read_from(&mut self.game_data, Language::English)?;
let row = warp_sheet.get_row(warp_id)?;
let sheet = WarpSheet::read_from(&mut self.game_data, Language::English)?;
let row = sheet.get_row(warp_id)?;
let pop_range_id = row.PopRange().into_u32()?;
let zone_id = row.TerritoryType().into_u16()?;

View file

@ -1,4 +1,5 @@
use physis::{common::Language, exd::ExcelRowKind};
use physis::common::Language;
use physis_sheets::Race::RaceSheet;
use serde::{Deserialize, Serialize};
use crate::common::GameData;
@ -122,34 +123,19 @@ impl<'a> Iterator for InventoryIterator<'a> {
impl Inventory {
/// Equip the starting items for a given race
pub fn equip_racial_items(&mut self, race_id: u8, gender: u8, game_data: &mut GameData) {
let exh = game_data.game_data.read_excel_sheet_header("Race").unwrap();
let exd = game_data
.game_data
.read_excel_sheet("Race", &exh, Language::English, 0)
.unwrap();
let ExcelRowKind::SingleRow(world_row) = &exd.get_row(race_id as u32).unwrap() else {
panic!("Expected a single row!")
};
let get_column = |column_index: usize| {
let physis::exd::ColumnData::Int32(item_id) = &world_row.columns[column_index] else {
panic!("Unexpected type!");
};
*item_id
};
let sheet = RaceSheet::read_from(&mut game_data.game_data, Language::English).unwrap();
let row = sheet.get_row(race_id as u32).unwrap();
if gender == 0 {
self.equipped.body = Item::new(1, get_column(2) as u32);
self.equipped.hands = Item::new(1, get_column(3) as u32);
self.equipped.legs = Item::new(1, get_column(4) as u32);
self.equipped.feet = Item::new(1, get_column(5) as u32);
self.equipped.body = Item::new(1, *row.RSEMBody().into_i32().unwrap() as u32);
self.equipped.hands = Item::new(1, *row.RSEMHands().into_i32().unwrap() as u32);
self.equipped.legs = Item::new(1, *row.RSEMLegs().into_i32().unwrap() as u32);
self.equipped.feet = Item::new(1, *row.RSEMFeet().into_i32().unwrap() as u32);
} else {
self.equipped.body = Item::new(1, get_column(6) as u32);
self.equipped.hands = Item::new(1, get_column(7) as u32);
self.equipped.legs = Item::new(1, get_column(8) as u32);
self.equipped.feet = Item::new(1, get_column(9) as u32);
self.equipped.body = Item::new(1, *row.RSEFBody().into_i32().unwrap() as u32);
self.equipped.hands = Item::new(1, *row.RSEFHands().into_i32().unwrap() as u32);
self.equipped.legs = Item::new(1, *row.RSEFLegs().into_i32().unwrap() as u32);
self.equipped.feet = Item::new(1, *row.RSEFFeet().into_i32().unwrap() as u32);
}
// TODO: don't hardcode

View file

@ -834,8 +834,9 @@ impl ZoneConnection {
{
let mut game_data = self.gamedata.lock().unwrap();
attributes =
game_data.get_racial_base_attributes(chara_details.chara_make.customize.subrace);
attributes = game_data
.get_racial_base_attributes(chara_details.chara_make.customize.subrace)
.expect("Failed to read racial attributes");
}
let ipc = ServerZoneIpcSegment {

View file

@ -28,7 +28,9 @@ pub async fn handle_custom_ipc(connection: &mut ZoneConnection, data: &CustomIpc
{
let mut game_data = connection.gamedata.lock().unwrap();
city_state = game_data.get_citystate(chara_make.classjob_id as u16);
city_state = game_data
.get_citystate(chara_make.classjob_id as u16)
.expect("Unknown citystate");
}
let mut inventory = Inventory::default();
@ -122,7 +124,9 @@ pub async fn handle_custom_ipc(connection: &mut ZoneConnection, data: &CustomIpc
let world_name;
{
let mut game_data = connection.gamedata.lock().unwrap();
world_name = game_data.get_world_name(config.world.world_id);
world_name = game_data
.get_world_name(config.world.world_id)
.expect("Couldn't read world name");
}
let characters;

View file

@ -1,11 +1,11 @@
use physis::{
common::Language,
exd::ExcelRowKind,
gamedata::GameData,
layer::{
ExitRangeInstanceObject, InstanceObject, LayerEntryData, LayerGroup, PopRangeInstanceObject,
},
};
use physis_sheets::TerritoryType::TerritoryTypeSheet;
/// Represents a loaded zone
#[derive(Default)]
@ -27,25 +27,11 @@ impl Zone {
..Default::default()
};
let Some(exh) = game_data.read_excel_sheet_header("TerritoryType") else {
return zone;
};
let Some(exd) = game_data.read_excel_sheet("TerritoryType", &exh, Language::None, 0) else {
return zone;
};
let Some(territory_type_row) = &exd.get_row(id as u32) else {
return zone;
};
let ExcelRowKind::SingleRow(territory_type_row) = territory_type_row else {
panic!("Expected a single row!")
};
let sheet = TerritoryTypeSheet::read_from(game_data, Language::None).unwrap();
let row = sheet.get_row(id as u32).unwrap();
// e.g. ffxiv/fst_f1/fld/f1f3/level/f1f3
let physis::exd::ColumnData::String(bg_path) = &territory_type_row.columns[1] else {
panic!("Unexpected type!");
};
let bg_path = row.Bg().into_string().unwrap();
let Some(level_index) = bg_path.find("/level/") else {
return zone;