diff --git a/README.md b/README.md index 0bece2c..44d3354 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Camellya +# Wicked Waifus ![Screenshot](screenshot.png) @@ -68,7 +68,7 @@ db_name = "wicked_waifus_db" The data files: Logic JSON collections (`data/assets/game-data/BinData`) and config/hotpatch indexes (`data/assets/config-server`, `data/assets/hotpatch-server`) are included in this repository. Keep in mind that you need to have the `data` subdirectory in current working directory. #### Connecting -You have to download client of Wuthering Waves Beta 1.3, apply the [wicked-waifus-win-patch](https://git.xeondev.com/wickedwaifus/wicked-waifus-win-patch/releases) and add necessary `.pak` files, which you can get here: [wicked-waifus-pak](https://git.xeondev.com/wickedwaifus/wicked-waifus-pak) +You have to download client of Wuthering Waves Beta 2.1, apply the [wicked-waifus-win-patch](https://git.xeondev.com/wickedwaifus/wicked-waifus-win-patch/releases) and add necessary `.pak` files, which you can get here: [wicked-waifus-pak](https://git.xeondev.com/wickedwaifus/wicked-waifus-pak) ### Troubleshooting [Visit our discord](https://discord.gg/reversedrooms) if you have any questions/issues diff --git a/data/assets/game-data b/data/assets/game-data index 902e7a8..c00cdae 160000 --- a/data/assets/game-data +++ b/data/assets/game-data @@ -1 +1 @@ -Subproject commit 902e7a8eae8b7a530c8dd38d5dc4b48dd808ad6c +Subproject commit c00cdaefddc9c81ae6d79589c31ee169d59f2382 diff --git a/wicked-waifus-data/src/level_entity_config.rs b/wicked-waifus-data/src/level_entity_config.rs index 7954b66..c02f709 100644 --- a/wicked-waifus-data/src/level_entity_config.rs +++ b/wicked-waifus-data/src/level_entity_config.rs @@ -1,7 +1,7 @@ use serde::Deserialize; use crate::{ComponentsData, RawVectorData}; -#[derive(Deserialize, Clone, Debug)] +#[derive(Deserialize, Debug)] #[serde(rename_all = "PascalCase")] pub struct LevelEntityConfigData { pub id: i32, diff --git a/wicked-waifus-data/src/lib.rs b/wicked-waifus-data/src/lib.rs index aa90581..7fec598 100644 --- a/wicked-waifus-data/src/lib.rs +++ b/wicked-waifus-data/src/lib.rs @@ -1,3 +1,6 @@ +use std::fs::File; +use std::io::BufReader; + use paste::paste; pub use misc_data::*; @@ -32,8 +35,9 @@ macro_rules! json_data { fn load_json_data(base_path: &str) -> Result<(), LoadDataError> { $(paste! { - let json_content = std::fs::read_to_string(&format!("{}/{}.json", base_path, stringify!($table_type)))?; - let _ = [<$table_type:snake _data>]::TABLE.set(serde_json::from_str(&json_content)?); + let file = File::open(&format!("{}/{}.json", base_path, stringify!($table_type)))?; + let reader = BufReader::new(file); + let _ = [<$table_type:snake _data>]::TABLE.set(serde_json::from_reader(reader)?); })* Ok(()) @@ -68,11 +72,11 @@ macro_rules! json_hash_table_data { fn load_json_hash_table_data(base_path: &str) -> Result<(), LoadDataError> { $(paste! { - let json_content = std::fs::read_to_string(&format!("{}/{}.json", base_path, stringify!($table_type)))?; + let file = File::open(&format!("{}/{}.json", base_path, stringify!($table_type)))?; + let reader = BufReader::new(file); let _ = [<$table_type:snake _data>]::TABLE.set( - serde_json::from_str::]::Data>>(&json_content)? - .iter() - .cloned() + serde_json::from_reader::, Vec<[<$table_type:snake _data>]::Data>>(reader)? + .into_iter() .map(|element| (element.$key_param, element)) .collect::>() ); @@ -115,17 +119,22 @@ json_hash_table_data! { mod textmap; pub mod text_map_data { - use std::collections::HashMap; + use std::collections::{HashMap, HashSet}; + use std::fs::File; + use std::io::BufReader; use std::sync::OnceLock; use crate::LoadDataError; use crate::textmap::TextMapData; + use crate::gacha_view_info_data; static EMPTY: OnceLock> = OnceLock::new(); static TABLE: OnceLock>> = OnceLock::new(); pub fn load_textmaps(base_path: &str) -> Result<(), LoadDataError> { - let _ = EMPTY.set(HashMap::new()); + // TODO: Ideally we would expose a function here to allow other components to add to the + // filter, since right now only gacha uses it, we can do this + let filters = get_filters(); let languages = std::fs::read_dir(base_path)? .filter_map(|entry| entry.ok()) .filter(|entry| entry.path().is_dir()) @@ -133,14 +142,13 @@ pub mod text_map_data { let mut result: HashMap> = HashMap::new(); for language in languages { let lang_id = language.file_name().to_str().unwrap().to_string(); - let json_content = std::fs::read_to_string( - &format!("{base_path}/{lang_id}/multi_text/MultiText.json") - )?; + let file = File::open(&format!("{base_path}/{lang_id}/multi_text/MultiText.json"))?; + let reader = BufReader::new(file); result.insert( lang_id, - serde_json::from_str::>(&json_content)? - .iter() - .cloned() + serde_json::from_reader::, Vec>(reader)? + .into_iter() + .filter(|element| filters.contains(&element.id)) .map(|element| (element.id, element.content)) .collect::>(), ); @@ -151,7 +159,9 @@ pub mod text_map_data { pub fn get_textmap(language: i32) -> &'static HashMap { let (text_code, _audio_code) = get_language_from_i32(language); - TABLE.get().unwrap().get(text_code).unwrap_or(EMPTY.get().unwrap()) + TABLE.get_or_init(|| HashMap::new()) + .get(text_code) + .unwrap_or(EMPTY.get_or_init(|| HashMap::new())) } fn get_language_from_i32(language: i32) -> (&'static str, &'static str) { @@ -172,4 +182,13 @@ pub mod text_map_data { _ => ("en", "en"), } } + + fn get_filters() -> HashSet { + let mut filters = HashSet::new(); + for gacha_view_info in gacha_view_info_data::iter() { + filters.insert(gacha_view_info.summary_title.clone()); + filters.insert(gacha_view_info.summary_describe.clone()); + } + filters + } } \ No newline at end of file diff --git a/wicked-waifus-data/src/misc_data.rs b/wicked-waifus-data/src/misc_data.rs index c342e36..0cd450d 100644 --- a/wicked-waifus-data/src/misc_data.rs +++ b/wicked-waifus-data/src/misc_data.rs @@ -88,7 +88,7 @@ pub struct WorldLevelBonusType { pub world_level_bonus_id: Option, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Debug)] #[serde(rename_all = "PascalCase")] pub struct BaseInfoComponent { pub tid_name: Option, @@ -103,7 +103,7 @@ pub struct BaseInfoComponent { // TODO: Add more } -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Debug)] #[serde(rename_all = "PascalCase")] pub struct AiComponent { pub disabled: Option, @@ -111,7 +111,7 @@ pub struct AiComponent { // TODO: Add more } -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Debug)] #[serde(rename_all = "PascalCase")] pub struct AttributeComponent { pub property_id: Option, @@ -125,7 +125,7 @@ pub struct AttributeComponent { // TODO: Add more } -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Debug)] #[serde(rename_all = "PascalCase")] pub struct TeleportPosition { pub x: Option, @@ -134,7 +134,7 @@ pub struct TeleportPosition { pub a: Option, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Debug)] #[serde(rename_all = "PascalCase")] pub struct TeleportComponent { pub disabled: Option, @@ -143,7 +143,7 @@ pub struct TeleportComponent { pub teleport_position: Option, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Debug)] #[serde(rename_all = "PascalCase")] pub struct ComponentsData { pub base_info_component: Option, @@ -151,102 +151,102 @@ pub struct ComponentsData { pub attribute_component: Option, pub teleport_component: Option, // TODO: Implement this ones - pub scene_actor_ref_component: Option, - pub effect_area_component: Option, - pub entity_state_component: Option, - pub condition_listener_component: Option, - pub interact_component: Option, - pub npc_perform_component: Option, - pub var_component: Option, - pub entity_visible_component: Option, - pub level_ai_component: Option, - pub trigger_component: Option, - pub range_component: Option, - pub spline_component: Option, - pub bubble_component: Option, - pub reward_component: Option, - pub refresh_component: Option, - pub passerby_npc_spawn_component: Option, - pub vision_capture_component: Option, - pub refresh_group_component: Option, - pub collect_component: Option, - pub target_gear_component: Option, - pub fight_interact_component: Option, - pub guide_line_creator_component: Option, - pub photo_target_component: Option, - pub model_component: Option, - pub entity_group_component: Option, - pub scene_item_life_cycle_component: Option, - pub entity_state_audio_component: Option, - pub animal_component: Option, - pub monster_component: Option, - pub nearby_tracking_component: Option, - pub follow_track_component: Option, - pub jigsaw_foundation: Option, - pub treasure_box_component: Option, - pub hook_lock_point: Option, - pub explore_skill_interact_component: Option, - pub attach_target_component: Option, - pub target_gear_group_component: Option, - pub spawn_monster_component: Option, - pub skybox_component: Option, - pub destructible_item: Option, - pub fan_component: Option, - pub state_hint_component: Option, - pub buff_consumer_component: Option, - pub reset_entities_pos_component: Option, - pub group_ai_component: Option, - pub pulling_object_foundation: Option, - pub lift_component: Option, - pub scene_item_movement_component: Option, - pub reset_self_pos_component: Option, - pub jigsaw_item: Option, - pub level_play_component: Option, - pub interact_gear_component: Option, - pub ai_gear_strategy_component: Option, - pub pick_interact_component: Option, - pub level_sequence_frame_event_component: Option, - pub air_wall_spawner_component: Option, - pub progress_bar_control_component: Option, - pub batch_bullet_caster_component: Option, - pub client_trigger_component: Option, - pub enrichment_area_component: Option, - pub vehicle_component: Option, - pub item_foundation2: Option, - pub tele_control2: Option, - pub interact_audio_component: Option, - pub level_qte_component: Option, - pub resurrection_component: Option, - pub ai_alert_notify_component: Option, - pub trample_component: Option, - pub dungeon_entry_component: Option, - pub level_prefab_perform_component: Option, - pub render_specified_range_component: Option, - pub walking_pattern_component: Option, - pub no_render_portal_component: Option, - pub adsorb_component: Option, - pub beam_cast_component: Option, - pub beam_receive_component: Option, - pub timeline_track_control_component: Option, - pub scene_bullet_component: Option, - pub edit_custom_aoi_component: Option, - pub combat_component: Option, - pub location_safety_component: Option, - pub turntable_control_component: Option, - pub scene_item_ai_component: Option, - pub buff_producer_component: Option, - pub portal_component: Option, - pub inhalation_ability_component: Option, - pub inhaled_item_component: Option, - pub monster_gacha_base_component: Option, - pub monster_gacha_item_component: Option, - pub time_stop_component: Option, - pub hit_component: Option, - pub levitate_magnet_component: Option, - pub rebound_component: Option, - pub rotator_component2: Option, - pub conveyor_belt_component: Option, - pub dynamic_portal_creator_component: Option, - pub connector_component: Option, - pub monitor_component: Option, + // pub scene_actor_ref_component: Option, + // pub effect_area_component: Option, + // pub entity_state_component: Option, + // pub condition_listener_component: Option, + // pub interact_component: Option, + // pub npc_perform_component: Option, + // pub var_component: Option, + // pub entity_visible_component: Option, + // pub level_ai_component: Option, + // pub trigger_component: Option, + // pub range_component: Option, + // pub spline_component: Option, + // pub bubble_component: Option, + // pub reward_component: Option, + // pub refresh_component: Option, + // pub passerby_npc_spawn_component: Option, + // pub vision_capture_component: Option, + // pub refresh_group_component: Option, + // pub collect_component: Option, + // pub target_gear_component: Option, + // pub fight_interact_component: Option, + // pub guide_line_creator_component: Option, + // pub photo_target_component: Option, + // pub model_component: Option, + // pub entity_group_component: Option, + // pub scene_item_life_cycle_component: Option, + // pub entity_state_audio_component: Option, + // pub animal_component: Option, + // pub monster_component: Option, + // pub nearby_tracking_component: Option, + // pub follow_track_component: Option, + // pub jigsaw_foundation: Option, + // pub treasure_box_component: Option, + // pub hook_lock_point: Option, + // pub explore_skill_interact_component: Option, + // pub attach_target_component: Option, + // pub target_gear_group_component: Option, + // pub spawn_monster_component: Option, + // pub skybox_component: Option, + // pub destructible_item: Option, + // pub fan_component: Option, + // pub state_hint_component: Option, + // pub buff_consumer_component: Option, + // pub reset_entities_pos_component: Option, + // pub group_ai_component: Option, + // pub pulling_object_foundation: Option, + // pub lift_component: Option, + // pub scene_item_movement_component: Option, + // pub reset_self_pos_component: Option, + // pub jigsaw_item: Option, + // pub level_play_component: Option, + // pub interact_gear_component: Option, + // pub ai_gear_strategy_component: Option, + // pub pick_interact_component: Option, + // pub level_sequence_frame_event_component: Option, + // pub air_wall_spawner_component: Option, + // pub progress_bar_control_component: Option, + // pub batch_bullet_caster_component: Option, + // pub client_trigger_component: Option, + // pub enrichment_area_component: Option, + // pub vehicle_component: Option, + // pub item_foundation2: Option, + // pub tele_control2: Option, + // pub interact_audio_component: Option, + // pub level_qte_component: Option, + // pub resurrection_component: Option, + // pub ai_alert_notify_component: Option, + // pub trample_component: Option, + // pub dungeon_entry_component: Option, + // pub level_prefab_perform_component: Option, + // pub render_specified_range_component: Option, + // pub walking_pattern_component: Option, + // pub no_render_portal_component: Option, + // pub adsorb_component: Option, + // pub beam_cast_component: Option, + // pub beam_receive_component: Option, + // pub timeline_track_control_component: Option, + // pub scene_bullet_component: Option, + // pub edit_custom_aoi_component: Option, + // pub combat_component: Option, + // pub location_safety_component: Option, + // pub turntable_control_component: Option, + // pub scene_item_ai_component: Option, + // pub buff_producer_component: Option, + // pub portal_component: Option, + // pub inhalation_ability_component: Option, + // pub inhaled_item_component: Option, + // pub monster_gacha_base_component: Option, + // pub monster_gacha_item_component: Option, + // pub time_stop_component: Option, + // pub hit_component: Option, + // pub levitate_magnet_component: Option, + // pub rebound_component: Option, + // pub rotator_component2: Option, + // pub conveyor_belt_component: Option, + // pub dynamic_portal_creator_component: Option, + // pub connector_component: Option, + // pub monitor_component: Option, } \ No newline at end of file diff --git a/wicked-waifus-game-server/gameserver.default.toml b/wicked-waifus-game-server/gameserver.default.toml index 1fd9e13..c054998 100644 --- a/wicked-waifus-game-server/gameserver.default.toml +++ b/wicked-waifus-game-server/gameserver.default.toml @@ -11,3 +11,8 @@ addr = "tcp://127.0.0.1:10004" [gateway_end_point] addr = "tcp://127.0.0.1:10003" + +[game_server_config] +load_textmaps = true +# Do not change yet, issues to be solved +quadrant_size = 1000000 \ No newline at end of file diff --git a/wicked-waifus-game-server/src/config.rs b/wicked-waifus-game-server/src/config.rs index 280c8bf..890cc10 100644 --- a/wicked-waifus-game-server/src/config.rs +++ b/wicked-waifus-game-server/src/config.rs @@ -10,6 +10,13 @@ pub struct ServiceConfig { pub database: DatabaseSettings, pub service_end_point: ServiceEndPoint, pub gateway_end_point: ServiceEndPoint, + pub game_server_config: GameServerConfig, +} + +#[derive(Deserialize)] +pub struct GameServerConfig { + pub load_textmaps: bool, + pub quadrant_size: f32, } impl TomlConfig for ServiceConfig { diff --git a/wicked-waifus-game-server/src/logic/handler/gacha.rs b/wicked-waifus-game-server/src/logic/handler/gacha.rs index 8f41b97..380d7bc 100644 --- a/wicked-waifus-game-server/src/logic/handler/gacha.rs +++ b/wicked-waifus-game-server/src/logic/handler/gacha.rs @@ -3,13 +3,7 @@ use std::sync::{Mutex, OnceLock}; use std::time::UNIX_EPOCH; use wicked_waifus_data::{gacha_pool_data, gacha_view_info_data, GachaPoolData, text_map_data}; -use wicked_waifus_data::GachaViewTypeInfoId::{BeginnersChoiceConvene - - , - NoviceConvene - - , -}; +use wicked_waifus_data::GachaViewTypeInfoId::{BeginnersChoiceConvene, NoviceConvene}; use wicked_waifus_protocol::{ErrorCode, GachaConsume, GachaInfo, GachaInfoRequest, GachaInfoResponse, GachaPoolInfo, GachaRequest, GachaResponse, GachaResult, GachaReward, GachaUsePoolRequest, GachaUsePoolResponse, WeaponItem}; diff --git a/wicked-waifus-game-server/src/logic/utils/quadrant_util.rs b/wicked-waifus-game-server/src/logic/utils/quadrant_util.rs index bb90182..f21db46 100644 --- a/wicked-waifus-game-server/src/logic/utils/quadrant_util.rs +++ b/wicked-waifus-game-server/src/logic/utils/quadrant_util.rs @@ -3,6 +3,11 @@ use std::sync::OnceLock; use wicked_waifus_data::LevelEntityConfigData; +struct StaticConfig { + edge_size: f32, + edge_check: f32, +} + #[derive(Clone)] struct MapBounds { x_max: f32, @@ -25,11 +30,8 @@ pub struct Map { quadrants: HashMap, } -// TODO: Make it configurable? -const EDGE_SIZE: f32 = 1000000f32; -const EDGE_CHECK: f32 = EDGE_SIZE * 3.0f32; - pub(crate) static MAP_TABLE: OnceLock> = OnceLock::new(); +pub(crate) static STATIC_CONFIG: OnceLock = OnceLock::new(); impl MapBounds { fn find_max_min(slice: &[&LevelEntityConfigData]) -> (Self, bool) { @@ -48,7 +50,8 @@ impl MapBounds { if entity.transform[0].y > y_max { y_max = entity.transform[0].y } } - if (f32::abs(x_max - x_min) < EDGE_CHECK) || (f32::abs(y_max - y_min) < EDGE_CHECK) { + let static_config = STATIC_CONFIG.get().unwrap(); + if (f32::abs(x_max - x_min) < static_config.edge_check) || (f32::abs(y_max - y_min) < static_config.edge_check) { // TODO: Handle this special case, since all entities fit, no need for quadrant // Move everything to positive coordinates to prevent corner cases @@ -58,16 +61,16 @@ impl MapBounds { (MapBounds { x_max, x_min, x_translate, y_max, y_min, y_translate }, false) } else { // Round to edge - x_max = round_max_coordinate(x_max, EDGE_SIZE); - x_min = round_min_coordinate(x_min, EDGE_SIZE); - y_max = round_max_coordinate(y_max, EDGE_SIZE); - y_min = round_min_coordinate(y_min, EDGE_SIZE); + x_max = round_max_coordinate(x_max, static_config.edge_size); + x_min = round_min_coordinate(x_min, static_config.edge_size); + y_max = round_max_coordinate(y_max, static_config.edge_size); + y_min = round_min_coordinate(y_min, static_config.edge_size); // Adding bounds to prevent OOB when moving - x_max += EDGE_SIZE; - x_min -= EDGE_SIZE; - y_max += EDGE_SIZE; - y_min -= EDGE_SIZE; + x_max += static_config.edge_size; + x_min -= static_config.edge_size; + y_max += static_config.edge_size; + y_min -= static_config.edge_size; // Move everything to positive coordinates to prevent corner cases let (x_max, x_min, x_translate) = recenter_map(x_max, x_min); @@ -126,17 +129,18 @@ impl Map { } pub fn get_quadrant_id(&self, x: f32, y: f32) -> u64 { + let edge_size = STATIC_CONFIG.get().unwrap().edge_size; let width: u64 = unsafe { f32::to_int_unchecked( f32::trunc( - (self.bounds.x_max + self.bounds.x_translate - x) / EDGE_SIZE + (self.bounds.x_max + self.bounds.x_translate - x) / edge_size ) ) }; let height: u64 = unsafe { f32::to_int_unchecked( f32::trunc( - (self.bounds.y_max + self.bounds.y_translate - y) / EDGE_SIZE + (self.bounds.y_max + self.bounds.y_translate - y) / edge_size ) ) }; @@ -169,7 +173,11 @@ pub fn maps_iter() -> std::collections::hash_map::Iter<'static, i32, Map> { MAP_TABLE.get().unwrap().iter() } -pub fn initialize_quadrant_system() { +pub fn initialize_quadrant_system(edge_size: f32) { + let _ = STATIC_CONFIG.set(StaticConfig { + edge_size, + edge_check: edge_size * 3.0f32, + }); let mut map_grouped_entities: HashMap> = HashMap::new(); for (_, entity) in wicked_waifus_data::level_entity_config_data::iter() { map_grouped_entities.entry(entity.map_id).or_default().push(entity); @@ -178,8 +186,8 @@ pub fn initialize_quadrant_system() { let mut maps: HashMap = HashMap::new(); for (map_id, entities) in map_grouped_entities { let (bounds, _quadrant_enabled) = MapBounds::find_max_min(&entities[..]); - let width = unsafe { f32::to_int_unchecked((bounds.x_max - bounds.x_min) / EDGE_SIZE) }; - let height = unsafe { f32::to_int_unchecked((bounds.y_max - bounds.y_min) / EDGE_SIZE) }; + let width = unsafe { f32::to_int_unchecked((bounds.x_max - bounds.x_min) / edge_size) }; + let height = unsafe { f32::to_int_unchecked((bounds.y_max - bounds.y_min) / edge_size) }; let map = maps.entry(map_id).or_insert( Map { bounds: bounds.clone(), diff --git a/wicked-waifus-game-server/src/main.rs b/wicked-waifus-game-server/src/main.rs index bf6ea83..d2c225f 100644 --- a/wicked-waifus-game-server/src/main.rs +++ b/wicked-waifus-game-server/src/main.rs @@ -21,8 +21,10 @@ async fn main() -> Result<()> { ::wicked_waifus_commons::splash::print_splash(); ::wicked_waifus_commons::logging::init(::tracing::Level::DEBUG); wicked_waifus_data::load_all_json_data("data/assets/game-data/BinData")?; - wicked_waifus_data::text_map_data::load_textmaps("data/assets/game-data/Textmap")?; - logic::utils::quadrant_util::initialize_quadrant_system(); + if CONFIG.game_server_config.load_textmaps { + wicked_waifus_data::text_map_data::load_textmaps("data/assets/game-data/Textmap")?; + } + logic::utils::quadrant_util::initialize_quadrant_system(CONFIG.game_server_config.quadrant_size); let database = Arc::new(wicked_waifus_database::connect_to(&CONFIG.database).await?); wicked_waifus_database::run_migrations(database.as_ref()).await?;