diff --git a/src/common/button_config.rs b/src/common/button_config.rs index 81de989..8f1ae4f 100644 --- a/src/common/button_config.rs +++ b/src/common/button_config.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use crate::common::*; +use crate::sync::*; use crate::input::{ControllerStyle::*, *}; use crate::training::frame_counter; use crate::training::ui::menu::VANILLA_MENU_ACTIVE; @@ -198,7 +199,7 @@ lazy_static! { fn _combo_passes(p1_controller: Controller, combo: ButtonCombo) -> bool { unsafe { // Prevent button combos from passing if either the vanilla or mod menu is open - if VANILLA_MENU_ACTIVE || QUICK_MENU_ACTIVE { + if VANILLA_MENU_ACTIVE || read_rwlock(&QUICK_MENU_ACTIVE) { return false; } @@ -264,7 +265,7 @@ pub fn handle_final_input_mapping(player_idx: i32, controller_struct: &mut SomeC // Here, we just finished holding start if *start_hold_frames > 0 && *start_hold_frames < 10 - && unsafe { !QUICK_MENU_ACTIVE } + && !read_rwlock(&QUICK_MENU_ACTIVE) && menu_close_wait_frame == 0 { // If we held for fewer than 10 frames, let's let the game know that diff --git a/src/common/menu.rs b/src/common/menu.rs index 9ddac59..f875340 100644 --- a/src/common/menu.rs +++ b/src/common/menu.rs @@ -7,16 +7,18 @@ use std::ptr::addr_of; use lazy_static::lazy_static; use parking_lot::Mutex; use skyline::nn::hid::GetNpadStyleSet; +use std::sync::RwLock; use crate::common::button_config::button_mapping; use crate::common::*; use crate::events::{Event, EVENT_QUEUE}; use crate::input::*; use crate::logging::*; +use crate::sync::*; use crate::training::frame_counter; pub const MENU_CLOSE_WAIT_FRAMES: u32 = 15; -pub static mut QUICK_MENU_ACTIVE: bool = false; +pub static QUICK_MENU_ACTIVE: RwLock<bool> = RwLock::new(false); pub unsafe fn menu_condition() -> bool { button_config::combo_passes(button_config::ButtonCombo::OpenMenu) @@ -82,7 +84,7 @@ pub unsafe fn set_menu_from_json(message: &str) { pub fn spawn_menu() { unsafe { - QUICK_MENU_ACTIVE = true; + assign_rwlock(&QUICK_MENU_ACTIVE, true); let mut app = QUICK_MENU_APP.lock(); app.page = AppPage::SUBMENU; *MENU_RECEIVED_INPUT.data_ptr() = true; @@ -149,7 +151,7 @@ pub fn handle_final_input_mapping( frame_counter::reset_frame_count(*MENU_CLOSE_FRAME_COUNTER); } - if QUICK_MENU_ACTIVE { + if read_rwlock(&QUICK_MENU_ACTIVE) { // If we're here, remove all other presses *out = MappedInputs::empty(); @@ -166,7 +168,7 @@ pub fn handle_final_input_mapping( .iter() .all(|i| GetNpadStyleSet(i as *const _).flags == 0) { - QUICK_MENU_ACTIVE = false; + assign_rwlock(&QUICK_MENU_ACTIVE, false); return; } @@ -205,7 +207,7 @@ pub fn handle_final_input_mapping( if app.page == AppPage::CLOSE { // Leave menu. frame_counter::start_counting(*MENU_CLOSE_FRAME_COUNTER); - QUICK_MENU_ACTIVE = false; + assign_rwlock(&QUICK_MENU_ACTIVE, false); let menu_json = app.get_serialized_settings_with_defaults(); set_menu_from_json(&menu_json); EVENT_QUEUE.push(Event::menu_open(menu_json)); diff --git a/src/lib.rs b/src/lib.rs index 95f5819..1bd7fdd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,6 +36,7 @@ mod hitbox_visualizer; mod training; mod logging; +mod sync; fn nro_main(nro: &NroInfo<'_>) { if nro.module.isLoaded { diff --git a/src/sync.rs b/src/sync.rs new file mode 100644 index 0000000..8619306 --- /dev/null +++ b/src/sync.rs @@ -0,0 +1,26 @@ +/// Convenience functions for interacting with RwLocks + +use std::sync::{RwLock, RwLockWriteGuard, RwLockReadGuard}; + +/// Gets a copy of a value inside a RwLock and immediately unlocks +/// +/// Requires <T: Copy> such as a bool or usize +pub fn read_rwlock<T: Copy>(rwlock: &RwLock<T>) -> T { *rwlock.read().unwrap() } + +/// Gets a clone of a value inside a RwLock and immediately unlocks +/// +/// Can be used if <T> is not Copy, such as Vec<u32> +pub fn read_rwlock_clone<T: Clone>(rwlock: &RwLock<T>) -> T { rwlock.read().unwrap().clone() } + +/// Assigns a new value to a RwLock and immediately unlocks +pub fn assign_rwlock<T>(rwlock: &RwLock<T>, new_val: T) { *rwlock.write().unwrap() = new_val } + +/// Locks a RwLock for writing and returns the guard +/// +/// Don't forget to drop the guard as soon as you're finished with it +pub fn lock_write_rwlock<T>(rwlock: &RwLock<T>) -> RwLockWriteGuard<T> { rwlock.write().unwrap() } + +/// Locks a RwLock for reading and returns the guard +/// +/// Don't forget to drop the guard as soon as you're finished with it +pub fn lock_read_rwlock<T>(rwlock: &RwLock<T>) -> RwLockReadGuard<T> { rwlock.read().unwrap() } \ No newline at end of file diff --git a/src/training/input_log.rs b/src/training/input_log.rs index b47c281..50d18d5 100644 --- a/src/training/input_log.rs +++ b/src/training/input_log.rs @@ -2,7 +2,10 @@ use itertools::Itertools; use once_cell::sync::Lazy; use std::collections::VecDeque; -use crate::common::{input::*, menu::QUICK_MENU_ACTIVE, try_get_module_accessor}; +use crate::common::input::*; +use crate::menu::QUICK_MENU_ACTIVE; +use crate::sync::*; +use crate::try_get_module_accessor; use lazy_static::lazy_static; use parking_lot::Mutex; use skyline::nn::ui2d::ResColor; @@ -320,7 +323,7 @@ pub fn handle_final_input_mapping( return; } - if QUICK_MENU_ACTIVE { + if read_rwlock(&QUICK_MENU_ACTIVE) { return; } diff --git a/src/training/ui/display.rs b/src/training/ui/display.rs index c85ab08..66486a4 100644 --- a/src/training/ui/display.rs +++ b/src/training/ui/display.rs @@ -5,6 +5,7 @@ use smash::ui2d::{SmashPane, SmashTextBox}; use crate::common::menu::QUICK_MENU_ACTIVE; use crate::common::TRAINING_MENU_ADDR; +use crate::sync::*; use crate::training::ui; macro_rules! display_parent_fmt { ($x:ident) => { @@ -40,7 +41,7 @@ pub unsafe fn draw(root_pane: &Pane) { root_pane .find_pane_by_name_recursive(display_parent_fmt!(notification_idx)) .unwrap() - .set_visible(notification.is_some() && !QUICK_MENU_ACTIVE); + .set_visible(notification.is_some() && !read_rwlock(&QUICK_MENU_ACTIVE)); if notification.is_none() { return; } diff --git a/src/training/ui/input_log.rs b/src/training/ui/input_log.rs index 49e8cac..909c81c 100644 --- a/src/training/ui/input_log.rs +++ b/src/training/ui/input_log.rs @@ -4,15 +4,14 @@ use skyline::nn::ui2d::*; use smash::ui2d::{SmashPane, SmashTextBox}; use training_mod_consts::{InputDisplay, MENU}; -use crate::{ - common::{consts::status_display_name, menu::QUICK_MENU_ACTIVE}, - training::{ - input_log::{ - DirectionStrength, InputLog, DRAW_LOG_BASE_IDX, NUM_LOGS, P1_INPUT_LOGS, WHITE, YELLOW, - }, - ui::{fade_out, menu::VANILLA_MENU_ACTIVE}, - }, +use crate::common::consts::status_display_name; +use crate::menu::QUICK_MENU_ACTIVE; +use crate::sync::*; +use crate::training::input_log::{ + DirectionStrength, InputLog, DRAW_LOG_BASE_IDX, NUM_LOGS, P1_INPUT_LOGS, WHITE, YELLOW, }; +use crate::training::ui::fade_out; +use crate::training::ui::menu::VANILLA_MENU_ACTIVE; macro_rules! log_parent_fmt { ($x:ident) => { @@ -193,7 +192,9 @@ pub unsafe fn draw(root_pane: &Pane) { .find_pane_by_name_recursive("TrModInputLog") .unwrap(); logs_pane.set_visible( - !QUICK_MENU_ACTIVE && !VANILLA_MENU_ACTIVE && MENU.input_display != InputDisplay::NONE, + !read_rwlock(&QUICK_MENU_ACTIVE) + && !VANILLA_MENU_ACTIVE + && MENU.input_display != InputDisplay::NONE, ); if MENU.input_display == InputDisplay::NONE { return; diff --git a/src/training/ui/menu.rs b/src/training/ui/menu.rs index 03ffc4c..7d6b376 100644 --- a/src/training/ui/menu.rs +++ b/src/training/ui/menu.rs @@ -7,9 +7,13 @@ use training_mod_tui::{ App, AppPage, ConfirmationState, SliderState, NX_SUBMENU_COLUMNS, NX_SUBMENU_ROWS, }; -use crate::common::menu::{MENU_CLOSE_FRAME_COUNTER, MENU_CLOSE_WAIT_FRAMES, MENU_RECEIVED_INPUT}; +use crate::common::menu::{ + MENU_CLOSE_FRAME_COUNTER, MENU_CLOSE_WAIT_FRAMES, MENU_RECEIVED_INPUT, P1_CONTROLLER_STYLE, + QUICK_MENU_ACTIVE, QUICK_MENU_APP, +}; +use crate::input::*; +use crate::sync::*; use crate::training::frame_counter; -use crate::{common, common::menu::QUICK_MENU_ACTIVE, input::*}; use training_mod_consts::TOGGLE_MAX; use super::fade_out; @@ -464,7 +468,7 @@ pub unsafe fn draw(root_pane: &Pane) { != -80.0; let overall_parent_pane = root_pane.find_pane_by_name_recursive("TrModMenu").unwrap(); - overall_parent_pane.set_visible(QUICK_MENU_ACTIVE && !VANILLA_MENU_ACTIVE); + overall_parent_pane.set_visible(read_rwlock(&QUICK_MENU_ACTIVE) && !VANILLA_MENU_ACTIVE); let menu_close_wait_frame = frame_counter::get_frame_count(*MENU_CLOSE_FRAME_COUNTER); fade_out( overall_parent_pane, @@ -514,7 +518,7 @@ pub unsafe fn draw(root_pane: &Pane) { .find_pane_by_name_recursive("status_R") .expect("Unable to find status_R pane"); // status_r_pane.flags |= 1 << PaneFlag::InfluencedAlpha as u8; - status_r_pane.set_visible(!QUICK_MENU_ACTIVE); + status_r_pane.set_visible(!read_rwlock(&QUICK_MENU_ACTIVE)); root_pane .find_pane_by_name_recursive("TrModSlider") @@ -524,7 +528,7 @@ pub unsafe fn draw(root_pane: &Pane) { // Update menu display // Grabbing lock as read-only, essentially // We don't really need to change anything, but get_before_selected requires &mut self - let app = &mut *crate::common::menu::QUICK_MENU_APP.data_ptr(); + let app = &mut *QUICK_MENU_APP.data_ptr(); let tab_titles = [ app.tabs @@ -538,7 +542,7 @@ pub unsafe fn draw(root_pane: &Pane) { .title, ]; - let is_gcc = (*common::menu::P1_CONTROLLER_STYLE.data_ptr()) == ControllerStyle::GCController; + let is_gcc = (*P1_CONTROLLER_STYLE.data_ptr()) == ControllerStyle::GCController; let button_mapping = if is_gcc { GCC_BUTTON_MAPPING.clone() } else { diff --git a/src/training/ui/mod.rs b/src/training/ui/mod.rs index 480ef51..89066c8 100644 --- a/src/training/ui/mod.rs +++ b/src/training/ui/mod.rs @@ -10,6 +10,7 @@ use crate::common::offsets::{OFFSET_DRAW, OFFSET_LAYOUT_ARC_MALLOC}; use crate::common::{is_ready_go, is_training_mode}; #[cfg(feature = "layout_arc_from_file")] use crate::consts::LAYOUT_ARC_PATH; +use crate::sync::*; use crate::training::frame_counter; mod damage; @@ -71,7 +72,7 @@ pub unsafe fn handle_draw(layout: *mut Layout, draw_info: u64, cmd_buffer: u64) { // InfluencedAlpha means "Should my children panes' alpha be influenced by mine, as the parent?" root_pane.flags |= 1 << PaneFlag::InfluencedAlpha as u8; - root_pane.set_visible(MENU.hud == OnOff::ON && !QUICK_MENU_ACTIVE); + root_pane.set_visible(MENU.hud == OnOff::ON && !read_rwlock(&QUICK_MENU_ACTIVE)); } damage::draw(root_pane, &layout_name); diff --git a/training_mod_tui/src/structures/stateful_table.rs b/training_mod_tui/src/structures/stateful_table.rs index de20f03..4b6d2c5 100644 --- a/training_mod_tui/src/structures/stateful_table.rs +++ b/training_mod_tui/src/structures/stateful_table.rs @@ -266,7 +266,7 @@ impl<'a, T: Clone + Serialize> Iterator for StatefulTableIteratorMut<'a, T> { } impl<'a, T: Clone + Serialize + 'a> StatefulTable<T> { - pub fn iter_mut(&'a mut self) -> StatefulTableIteratorMut<T> { + pub fn iter_mut(&'a mut self) -> StatefulTableIteratorMut<'a, T> { StatefulTableIteratorMut { inner: self.items.iter_mut().flatten(), }