Fix data loading errors with 3.4+ excels

This commit is contained in:
Nobody 2023-03-06 03:45:35 +05:00
parent 6d566ca425
commit c748b144ca
7 changed files with 182 additions and 21 deletions

View File

@ -16,6 +16,7 @@ lua_serde = { path = "lua_serde" }
packet-processor-macro = { path = "packet-processor-macro" } packet-processor-macro = { path = "packet-processor-macro" }
packet-processor = { path = "packet-processor" } packet-processor = { path = "packet-processor" }
rs-ipc = { path = "rs-ipc" } rs-ipc = { path = "rs-ipc" }
excel-hash-wrapper-macro = { path = "excel-hash-wrapper-macro" }
prost = "0.8" prost = "0.8"
bytes = "1.1.0" bytes = "1.1.0"

14
excel-hash-wrapper-macro/.gitignore vendored Normal file
View File

@ -0,0 +1,14 @@
# Generated by Cargo
# will have compiled files and executables
/target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# Vim temp files
.*.swp
.*.swo

View File

@ -0,0 +1,16 @@
[package]
name = "excel-hash-wrapper-macro"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
proc-macro = true
[dependencies]
regex = "1"
syn = { version = "1.0", features = ["full","derive"] }
quote = "1.0"
proc-macro2 = "1.0"
convert_case = "0.4.0"

View File

@ -0,0 +1,103 @@
use proc_macro::{self, TokenStream};
use quote::quote;
use syn::{parse_macro_input, AttributeArgs};
fn get_nested_meta_name(nested_meta: &syn::NestedMeta) -> String {
match nested_meta {
syn::NestedMeta::Meta(meta) => match meta {
syn::Meta::Path(ident) => ident.get_ident().cloned().unwrap().to_string(),
_ => panic!("Unsupported macro argument"),
},
syn::NestedMeta::Lit(_) => panic!("Only identifiers are supported as an arguments to the macro"),
}
}
#[proc_macro_attribute]
pub fn excel_hash_wrapper(args: TokenStream, input: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as AttributeArgs);
let args = args.clone().into_iter().map(|a| get_nested_meta_name(&a));
// Generate all "X_hash", "X_hash_pre" and "X_hash_suffix" strings as well as getter names
let args_hash = args.clone().into_iter().map(|a| format!("{}_hash", a));
let args_hash_pre = args.clone().into_iter().map(|a| format!("{}_hash_pre", a));
let args_hash_suffix = args.clone().into_iter().map(|a| format!("{}_hash_suffix", a));
let get_args_hash = args.clone().into_iter().map(|a| format!("get_{}_hash", a));
let args_hash: Vec<proc_macro2::TokenStream> = args_hash.map(|a| a.parse().unwrap()).collect();
let args_hash_pre: Vec<proc_macro2::TokenStream> = args_hash_pre.map(|a| a.parse().unwrap()).collect();
let args_hash_suffix: Vec<proc_macro2::TokenStream> = args_hash_suffix.map(|a| a.parse().unwrap()).collect();
let get_args_hash: Vec<proc_macro2::TokenStream> = get_args_hash.map(|a| a.parse().unwrap()).collect();
let mut found_struct = false;
let mut struct_name = None;
let modified_struct: proc_macro::TokenStream = input.into_iter().map(|r| {
match &r {
&proc_macro::TokenTree::Ident(ref ident) if ident.to_string() == "struct" => { // react on keyword "struct" so we don't randomly modify non-structs
found_struct = true;
r
},
&proc_macro::TokenTree::Ident(ref ident) if found_struct == true && struct_name == None => { // Next ident right after "struct" is the struct name
struct_name = Some(ident.to_string());
r
},
&proc_macro::TokenTree::Group(ref group) if group.delimiter() == proc_macro::Delimiter::Brace && found_struct == true => { // Opening brackets for the struct
let mut stream = proc_macro::TokenStream::new();
stream.extend(
// For each hash name, generate three fields: prefix, suffix and the one that combines them
vec![proc_macro::TokenStream::from(quote!(
#(
pub #args_hash_pre: Option<u8>,
pub #args_hash_suffix: Option<u32>,
pub #args_hash: Option<u64>,
)*
))]
);
stream.extend(group.stream());
proc_macro::TokenTree::Group(
proc_macro::Group::new(
proc_macro::Delimiter::Brace,
stream
)
)
}
_ => r
}
}).collect();
let struct_name: proc_macro2::TokenStream = match &struct_name {
None => panic!("Failed to find struct name"),
Some(name) => name.clone().parse().unwrap(),
};
let implementation = vec![proc_macro::TokenStream::from(quote!(
// This implements getters for hashes
// They first try to extract the value from u64 field
// If it fails, they default to combining prefix and suffix
impl #struct_name {
#(
pub fn #get_args_hash (&self) -> u64 {
match self.#args_hash {
Some(value) => value,
None => match self.#args_hash_pre {
None => panic!("Attempt to get an empty prefix for {} (object {:?})", stringify!(#args_hash), self),
Some(prefix) => match self.#args_hash_suffix {
None => panic!("Attempt to get an empty suffix for {} (object {:?})", stringify!(#args_hash), self),
Some(suffix) => IdManager::get_hash_by_prefix_suffix(prefix, suffix),
}
}
}
}
)*
}
))];
let mut ret = proc_macro::TokenStream::new();
ret.extend(modified_struct);
ret.extend(implementation);
println!("{}", ret);
ret
}

View File

@ -1,30 +1,70 @@
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use crate::utils::IdManager;
use excel_hash_wrapper_macro::*;
#[excel_hash_wrapper(gacha_card_name, gacha_image_name)]
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all="PascalCase")] #[serde(rename_all="PascalCase")]
#[derive(Serialize, Deserialize, Clone)]
pub struct AvatarGachaHashes { pub struct AvatarGachaHashes {
pub gacha_card_name_hash_pre: u8, /*pub gacha_card_name_hash_pre: u8,
pub gacha_card_name_hash_suffix: u32, pub gacha_card_name_hash_suffix: u32,
pub gacha_image_name_hash_pre: u8, pub gacha_image_name_hash_pre: u8,
pub gacha_image_name_hash_suffix: u32, pub gacha_image_name_hash_suffix: u32,*/
} }
#[excel_hash_wrapper(
prefab_path,
prefab_path_remote,
controller_path,
controller_path_remote,
prefab_path_ragdoll,
script_data_path,
combat_config,
manekin_path,
coop_pic_name,
manekin_json_config
)]
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all="PascalCase")] #[serde(rename_all="PascalCase")]
#[derive(Serialize, Deserialize, Clone)]
pub struct Avatar { pub struct Avatar {
// Entity fields // Entity fields
pub id: u32, pub id: u32,
pub name_text_map_hash: u32, pub name_text_map_hash: u32,
/*
pub prefab_path_hash_pre: u8, pub prefab_path_hash_pre: u8,
pub prefab_path_hash_suffix: u32, pub prefab_path_hash_suffix: u32,
pub prefab_path_remote_hash_pre: u8, pub prefab_path_remote_hash_pre: u8,
pub prefab_path_remote_hash_suffix: u32, pub prefab_path_remote_hash_suffix: u32,
pub controller_path_hash_pre: u8, pub controller_path_hash_pre: u8,
pub controller_path_hash_suffix: u32, pub controller_path_hash_suffix: u32,
pub controller_path_remote_hash_pre: u8, pub controller_path_remote_hash_pre: u8,
pub controller_path_remote_hash_suffix: u32, pub controller_path_remote_hash_suffix: u32,
pub prefab_path_ragdoll_hash_pre: u8,
pub prefab_path_ragdoll_hash_suffix: u32,
pub script_data_path_hash_pre: u8,
pub script_data_path_hash_suffix: u32,
pub combat_config_hash_pre: u8,
pub combat_config_hash_suffix: u32,
pub manekin_path_hash_pre: u8,
pub manekin_path_hash_suffix: u32,
pub coop_pic_name_hash_pre: Option<u8>,
pub coop_pic_name_hash_suffix: Option<u32>,
pub manekin_json_config_hash_pre: u8,
pub manekin_json_config_hash_suffix: u32,*/
//pub camp_id: Option<u32>, // Avatars don't have these //pub camp_id: Option<u32>, // Avatars don't have these
pub lod_pattern_name: String, pub lod_pattern_name: Option<String>,
// Creature fields // Creature fields
pub hp_base: f32, pub hp_base: f32,
@ -71,14 +111,9 @@ pub struct Avatar {
//pub prop_grow_curves: Vec<PropGrowConfig>, // TODO: unify with monster! //pub prop_grow_curves: Vec<PropGrowConfig>, // TODO: unify with monster!
pub prefab_path_ragdoll_hash_pre: u8,
pub prefab_path_ragdoll_hash_suffix: u32,
// Avatar fields // Avatar fields
pub use_type: Option<String>, // TODO: actually an enum pub use_type: Option<String>, // TODO: actually an enum
pub body_type: String, // TODO: actually an enum pub body_type: String, // TODO: actually an enum
pub script_data_path_hash_pre: u8,
pub script_data_path_hash_suffix: u32,
pub icon_name: String, pub icon_name: String,
pub side_icon_name: String, pub side_icon_name: String,
pub quality_type: String, // TODO: actually an enum pub quality_type: String, // TODO: actually an enum
@ -87,25 +122,17 @@ pub struct Avatar {
pub heal_add: f32, pub heal_add: f32,
#[serde(default)] #[serde(default)]
pub healed_add: f32, pub healed_add: f32,
pub combat_config_hash_pre: u8,
pub combat_config_hash_suffix: u32,
#[serde(default)] #[serde(default)]
pub is_range_attack: bool, pub is_range_attack: bool,
pub initial_weapon: u32, pub initial_weapon: u32,
pub weapon_type: String, // TODO: actually an enum pub weapon_type: String, // TODO: actually an enum
pub manekin_path_hash_pre: u8,
pub manekin_path_hash_suffix: u32,
pub image_name: String, pub image_name: String,
#[serde(flatten)] // Those fields are present or absent all together, so we grouped them #[serde(flatten)] // Those fields are present or absent all together, so we grouped them
pub avatar_gacha_hashes: Option<AvatarGachaHashes>, pub avatar_gacha_hashes: Option<AvatarGachaHashes>,
pub coop_pic_name_hash_pre: Option<u8>,
pub coop_pic_name_hash_suffix: Option<u32>,
pub cutscene_show: String, pub cutscene_show: String,
pub skill_depot_id: u32, pub skill_depot_id: u32,
pub stamina_recover_speed: f32, pub stamina_recover_speed: f32,
pub cand_skill_depot_ids: Vec<u32>, pub cand_skill_depot_ids: Vec<u32>,
pub manekin_json_config_hash_pre: u8,
pub manekin_json_config_hash_suffix: u32,
pub manekin_motion_config: u32, pub manekin_motion_config: u32,
pub desc_text_map_hash: u32, pub desc_text_map_hash: u32,
pub avatar_identity_type: Option<String>, // TODO: actually an enum pub avatar_identity_type: Option<String>, // TODO: actually an enum

View File

@ -8,7 +8,7 @@ pub struct AvatarSkill {
pub ability_name: String, pub ability_name: String,
pub desc_text_map_hash: u32, pub desc_text_map_hash: u32,
pub skill_icon: String, pub skill_icon: String,
pub cost_stamina: Option<u32>, pub cost_stamina: Option<f32>,
pub max_charge_num: u32, pub max_charge_num: u32,
pub trigger_id: Option<u32>, pub trigger_id: Option<u32>,
pub lock_shape: String, // TODO: probably an enum pub lock_shape: String, // TODO: probably an enum

View File

@ -60,7 +60,7 @@ pub struct Monster {
pub exclude_weathers: String, pub exclude_weathers: String,
#[serde(rename = "FeatureTagGroupID")] #[serde(rename = "FeatureTagGroupID")]
pub feature_tag_group_id: u32, pub feature_tag_group_id: Option<u32>,
#[serde(rename = "MpPropID")] #[serde(rename = "MpPropID")]
pub mp_prop_id: u32, pub mp_prop_id: u32,