mirror of
https://github.com/redstrate/Kawari.git
synced 2025-05-13 15:17: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:
parent
df789b52c9
commit
9787126a1b
8 changed files with 53 additions and 110 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -1028,7 +1028,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "physis-sheets"
|
name = "physis-sheets"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
source = "git+https://github.com/redstrate/PhysisSheets#ca42d72fc8a76211d67f6bd4698a45c7da0037fb"
|
source = "git+https://github.com/redstrate/PhysisSheets#4d11e27a1e31bb482184626d1e7a51fc5e673674"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"physis",
|
"physis",
|
||||||
]
|
]
|
||||||
|
|
|
@ -104,4 +104,4 @@ rkon = { version = "0.1" }
|
||||||
tower-http = { version = "0.6", features = ["fs"] }
|
tower-http = { version = "0.6", features = ["fs"] }
|
||||||
|
|
||||||
# excel sheet data
|
# 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 }
|
||||||
|
|
|
@ -27,7 +27,9 @@ async fn main() {
|
||||||
tracing::info!("Server started on {addr}");
|
tracing::info!("Server started on {addr}");
|
||||||
|
|
||||||
let mut game_data = GameData::new();
|
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 {
|
loop {
|
||||||
let (socket, _) = listener.accept().await.unwrap();
|
let (socket, _) = listener.accept().await.unwrap();
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
use physis::common::{Language, Platform};
|
use physis::common::{Language, Platform};
|
||||||
use physis::exd::{EXD, ExcelRowKind};
|
use physis::exd::{EXD, ExcelRowKind};
|
||||||
use physis::exh::EXH;
|
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};
|
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.
|
/// Gets the world name from an id into the World Excel sheet.
|
||||||
pub fn get_world_name(&mut self, world_id: u16) -> String {
|
pub fn get_world_name(&mut self, world_id: u16) -> Option<String> {
|
||||||
let exh = self.game_data.read_excel_sheet_header("World").unwrap();
|
let sheet = WorldSheet::read_from(&mut self.game_data, Language::None)?;
|
||||||
let exd = self
|
let row = sheet.get_row(world_id as u32)?;
|
||||||
.game_data
|
|
||||||
.read_excel_sheet("World", &exh, Language::None, 0)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let ExcelRowKind::SingleRow(world_row) = &exd.get_row(world_id as u32).unwrap() else {
|
row.Name().into_string().map(|x| x.clone())
|
||||||
panic!("Expected a single row!")
|
|
||||||
};
|
|
||||||
|
|
||||||
let physis::exd::ColumnData::String(name) = &world_row.columns[1] else {
|
|
||||||
panic!("Unexpected type!");
|
|
||||||
};
|
|
||||||
|
|
||||||
name.clone()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the starting city-state from a given class/job id.
|
/// Gets the starting city-state from a given class/job id.
|
||||||
pub fn get_citystate(&mut self, classjob_id: u16) -> u8 {
|
pub fn get_citystate(&mut self, classjob_id: u16) -> Option<u8> {
|
||||||
let exh = self.game_data.read_excel_sheet_header("ClassJob").unwrap();
|
let sheet = ClassJobSheet::read_from(&mut self.game_data, Language::English)?;
|
||||||
let exd = self
|
let row = sheet.get_row(classjob_id as u32)?;
|
||||||
.game_data
|
|
||||||
.read_excel_sheet("ClassJob", &exh, Language::English, 0)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let ExcelRowKind::SingleRow(world_row) = &exd.get_row(classjob_id as u32).unwrap() else {
|
row.StartingTown().into_u8().map(|x| *x)
|
||||||
panic!("Expected a single row!")
|
|
||||||
};
|
|
||||||
|
|
||||||
let physis::exd::ColumnData::UInt8(town_id) = &world_row.columns[33] else {
|
|
||||||
panic!("Unexpected type!");
|
|
||||||
};
|
|
||||||
|
|
||||||
*town_id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// 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 base_stat = 20;
|
||||||
|
|
||||||
let exh = self.game_data.read_excel_sheet_header("Tribe").unwrap();
|
let sheet = TribeSheet::read_from(&mut self.game_data, Language::English)?;
|
||||||
let exd = self
|
let row = sheet.get_row(tribe_id as u32)?;
|
||||||
.game_data
|
|
||||||
.read_excel_sheet("Tribe", &exh, Language::English, 0)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let ExcelRowKind::SingleRow(tribe_row) = &exd.get_row(tribe_id as u32).unwrap() else {
|
Some(Attributes {
|
||||||
panic!("Expected a single row!")
|
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,
|
||||||
let get_column = |column_index: usize| {
|
intelligence: (base_stat + row.INT().into_i8()?) as u32,
|
||||||
let physis::exd::ColumnData::Int8(delta) = &tribe_row.columns[column_index] else {
|
mind: (base_stat + row.MND().into_i8()?) as u32,
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the primary model ID for a given item ID
|
/// 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
|
/// 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)> {
|
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 sheet = WarpSheet::read_from(&mut self.game_data, Language::English)?;
|
||||||
|
let row = sheet.get_row(warp_id)?;
|
||||||
let row = warp_sheet.get_row(warp_id)?;
|
|
||||||
|
|
||||||
let pop_range_id = row.PopRange().into_u32()?;
|
let pop_range_id = row.PopRange().into_u32()?;
|
||||||
let zone_id = row.TerritoryType().into_u16()?;
|
let zone_id = row.TerritoryType().into_u16()?;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use physis::{common::Language, exd::ExcelRowKind};
|
use physis::common::Language;
|
||||||
|
use physis_sheets::Race::RaceSheet;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::common::GameData;
|
use crate::common::GameData;
|
||||||
|
@ -122,34 +123,19 @@ impl<'a> Iterator for InventoryIterator<'a> {
|
||||||
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, game_data: &mut GameData) {
|
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 sheet = RaceSheet::read_from(&mut game_data.game_data, Language::English).unwrap();
|
||||||
let exd = game_data
|
let row = sheet.get_row(race_id as u32).unwrap();
|
||||||
.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
|
|
||||||
};
|
|
||||||
|
|
||||||
if gender == 0 {
|
if gender == 0 {
|
||||||
self.equipped.body = Item::new(1, get_column(2) as u32);
|
self.equipped.body = Item::new(1, *row.RSEMBody().into_i32().unwrap() as u32);
|
||||||
self.equipped.hands = Item::new(1, get_column(3) as u32);
|
self.equipped.hands = Item::new(1, *row.RSEMHands().into_i32().unwrap() as u32);
|
||||||
self.equipped.legs = Item::new(1, get_column(4) as u32);
|
self.equipped.legs = Item::new(1, *row.RSEMLegs().into_i32().unwrap() as u32);
|
||||||
self.equipped.feet = Item::new(1, get_column(5) as u32);
|
self.equipped.feet = Item::new(1, *row.RSEMFeet().into_i32().unwrap() as u32);
|
||||||
} else {
|
} else {
|
||||||
self.equipped.body = Item::new(1, get_column(6) as u32);
|
self.equipped.body = Item::new(1, *row.RSEFBody().into_i32().unwrap() as u32);
|
||||||
self.equipped.hands = Item::new(1, get_column(7) as u32);
|
self.equipped.hands = Item::new(1, *row.RSEFHands().into_i32().unwrap() as u32);
|
||||||
self.equipped.legs = Item::new(1, get_column(8) as u32);
|
self.equipped.legs = Item::new(1, *row.RSEFLegs().into_i32().unwrap() as u32);
|
||||||
self.equipped.feet = Item::new(1, get_column(9) as u32);
|
self.equipped.feet = Item::new(1, *row.RSEFFeet().into_i32().unwrap() as u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: don't hardcode
|
// TODO: don't hardcode
|
||||||
|
|
|
@ -834,8 +834,9 @@ impl ZoneConnection {
|
||||||
{
|
{
|
||||||
let mut game_data = self.gamedata.lock().unwrap();
|
let mut game_data = self.gamedata.lock().unwrap();
|
||||||
|
|
||||||
attributes =
|
attributes = game_data
|
||||||
game_data.get_racial_base_attributes(chara_details.chara_make.customize.subrace);
|
.get_racial_base_attributes(chara_details.chara_make.customize.subrace)
|
||||||
|
.expect("Failed to read racial attributes");
|
||||||
}
|
}
|
||||||
|
|
||||||
let ipc = ServerZoneIpcSegment {
|
let ipc = ServerZoneIpcSegment {
|
||||||
|
|
|
@ -28,7 +28,9 @@ pub async fn handle_custom_ipc(connection: &mut ZoneConnection, data: &CustomIpc
|
||||||
{
|
{
|
||||||
let mut game_data = connection.gamedata.lock().unwrap();
|
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();
|
let mut inventory = Inventory::default();
|
||||||
|
@ -122,7 +124,9 @@ pub async fn handle_custom_ipc(connection: &mut ZoneConnection, data: &CustomIpc
|
||||||
let world_name;
|
let world_name;
|
||||||
{
|
{
|
||||||
let mut game_data = connection.gamedata.lock().unwrap();
|
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;
|
let characters;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use physis::{
|
use physis::{
|
||||||
common::Language,
|
common::Language,
|
||||||
exd::ExcelRowKind,
|
|
||||||
gamedata::GameData,
|
gamedata::GameData,
|
||||||
layer::{
|
layer::{
|
||||||
ExitRangeInstanceObject, InstanceObject, LayerEntryData, LayerGroup, PopRangeInstanceObject,
|
ExitRangeInstanceObject, InstanceObject, LayerEntryData, LayerGroup, PopRangeInstanceObject,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use physis_sheets::TerritoryType::TerritoryTypeSheet;
|
||||||
|
|
||||||
/// Represents a loaded zone
|
/// Represents a loaded zone
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -27,25 +27,11 @@ impl Zone {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(exh) = game_data.read_excel_sheet_header("TerritoryType") else {
|
let sheet = TerritoryTypeSheet::read_from(game_data, Language::None).unwrap();
|
||||||
return zone;
|
let row = sheet.get_row(id as u32).unwrap();
|
||||||
};
|
|
||||||
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!")
|
|
||||||
};
|
|
||||||
|
|
||||||
// e.g. ffxiv/fst_f1/fld/f1f3/level/f1f3
|
// e.g. ffxiv/fst_f1/fld/f1f3/level/f1f3
|
||||||
let physis::exd::ColumnData::String(bg_path) = &territory_type_row.columns[1] else {
|
let bg_path = row.Bg().into_string().unwrap();
|
||||||
panic!("Unexpected type!");
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(level_index) = bg_path.find("/level/") else {
|
let Some(level_index) = bg_path.find("/level/") else {
|
||||||
return zone;
|
return zone;
|
||||||
|
|
Loading…
Add table
Reference in a new issue