diff --git a/CHANGELOG.md b/CHANGELOG.md index a64a1ce..197b8d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,20 +4,23 @@ All notable changes to this project will be documented in this file. ## [0.4.0] - 2024-04-23 +## [0.4.0] - 2024-04-23 + ### Features - Generalize to running one-shot systems instead of firing events. -- Use a plugin, can configure schedule and system set. +- Use plugin. +- Can configure schedule and system set. - Remove `GamepadEvent`; use system with input `In`. -- Add IntoCondSystem that adds `only_if()` conditions to `IntoSystems`. +- Add `IntoCondSystem` that adds `only_if()` conditions to `IntoSystems`. - Add `only_if` example. -- Add prelude module for glob imports. +- Add `prelude` module for glob imports. ### Refactor - Hollow out lib so it's just `mod` and `pub use` statements. - Extract sets of functionality from `lib.rs` into `chord.rs`, `cache.rs` and `plugin.rs`. -- Extract simulated tests into `tests/` directory. +- Extract tests into `tests/simulated.rs`. ## [0.3.0] - 2024-03-08 diff --git a/Cargo.toml b/Cargo.toml index 259d01e..959f5a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ path = "examples/multiple_input.rs" [dependencies] bevy = { version = "0.13", default-features = false, features = [] } -trie-rs = { version = "0.3" } +trie-rs = { version = "0.4" } keyseq = { version = "0.2.3", features = [ "bevy" ] } [dev-dependencies] diff --git a/src/cache.rs b/src/cache.rs index a072f89..c7c3860 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -1,17 +1,23 @@ +//! Cache the trie for reuse. use crate::input_sequence::InputSequence; -use bevy::ecs::system::Resource; -use trie_rs::map::{Trie, TrieBuilder}; +use bevy::{ecs::system::Resource, log::info, reflect::TypePath}; +use std::{collections::HashMap, hash::Hash}; +use trie_rs::{ + inc_search::{IncSearch, Position}, + map::{Trie, TrieBuilder}, +}; /// Contains the trie for the input sequences. #[derive(Resource)] pub struct InputSequenceCache { - pub(crate) trie: Option>>, + trie: Option>>, + position: HashMap, } impl InputSequenceCache where - A: Ord + Clone + Send + Sync + 'static, - In: Send + Sync + Clone + 'static, + A: Ord + Clone + Send + Sync + TypePath + 'static, + In: Send + Sync + Clone + Eq + Hash + 'static, { /// Retrieve the cached trie without iterating through `sequences`. Or if /// the cache has been invalidated, build and cache a new trie using the @@ -25,13 +31,53 @@ where for sequence in sequences { builder.insert(sequence.acts.clone(), sequence.clone()); } + // info!( + // "Building trie for {} input sequences.", + // A::short_type_path() + // ); + assert!( + self.position.is_empty(), + "Position should be none when rebuilding trie" + ); builder.build() }) } + + /// Store a search. + pub fn store(&mut self, key: In, position: Position) { + self.position.insert(key, position); + } + + /// Recall a search OR create a new search. + pub fn recall<'a, 'b>( + &'b mut self, + key: In, + sequences: impl Iterator>, + ) -> IncSearch<'a, A, InputSequence> + where + 'b: 'a, + { + let position = self.position.get(&key).cloned(); + let trie = self.trie(sequences); + position + .map(move |p| IncSearch::resume(trie, p)) + .unwrap_or_else(move || trie.inc_search()) + } +} + +impl InputSequenceCache { + /// Clears the cache. + pub fn reset(&mut self) { + self.trie = None; + self.position.clear(); + } } impl Default for InputSequenceCache { fn default() -> Self { - Self { trie: None } + Self { + trie: None, + position: HashMap::new(), + } } } diff --git a/src/covec.rs b/src/covec.rs deleted file mode 100644 index abfbd4b..0000000 --- a/src/covec.rs +++ /dev/null @@ -1,21 +0,0 @@ -/// Helps keep two correlated vectors in sync. -pub(crate) struct Covec(pub(crate) Vec, pub(crate) Vec); - -impl Covec { - pub(crate) fn push(&mut self, x: T, y: S) { - self.0.push(x); - self.1.push(y); - } - - pub(crate) fn drain1_sync(&mut self) { - let len0 = self.0.len(); - let len1 = self.1.len(); - let _ = self.1.drain(0..len1.saturating_sub(len0)); - } -} - -impl Default for Covec { - fn default() -> Self { - Self(vec![], vec![]) - } -} diff --git a/src/frame_time.rs b/src/frame_time.rs index 9b85bb5..b09cdde 100644 --- a/src/frame_time.rs +++ b/src/frame_time.rs @@ -1,6 +1,6 @@ use crate::time_limit::TimeLimit; -#[derive(Clone)] +#[derive(Clone, Debug)] pub(crate) struct FrameTime { pub(crate) frame: u32, pub(crate) time: f32, diff --git a/src/lib.rs b/src/lib.rs index 5390869..2f3f6c8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,32 +2,30 @@ #![doc = include_str!("../README.md")] #![forbid(missing_docs)] -pub use keyseq::{ - bevy::{pkey as key, pkeyseq as keyseq}, - Modifiers, -}; - pub use chord::KeyChord; pub use plugin::InputSequencePlugin; pub use time_limit::TimeLimit; pub mod action; -mod cache; +pub mod cache; mod chord; pub mod cond_system; -mod covec; mod frame_time; pub mod input_sequence; mod plugin; mod time_limit; -/// Convenient splat import -pub mod prelude { - pub use std::time::Duration; +pub use keyseq::{ + bevy::{pkey as key, pkeyseq as keyseq}, + Modifiers, +}; - pub use crate::input_sequence::{ButtonSequence, InputSequence, KeySequence}; +/// Convenient glob import +pub mod prelude { + pub use super::input_sequence::{ButtonSequence, InputSequence, KeySequence}; + pub use super::{action, keyseq, InputSequencePlugin, Modifiers, TimeLimit}; - pub use super::{action, InputSequencePlugin, keyseq, Modifiers, TimeLimit}; + pub use super::chord::KeyChord; pub use super::cond_system::IntoCondSystem; + pub use std::time::Duration; } - diff --git a/src/plugin.rs b/src/plugin.rs index e109a9e..e99615f 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -16,17 +16,16 @@ use bevy::{ time::Time, utils::intern::Interned, }; -use std::collections::HashMap; +use std::collections::{HashMap, VecDeque}; use crate::{ cache::InputSequenceCache, chord::is_modifier, - covec::Covec, frame_time::FrameTime, input_sequence::{ButtonSequence, InputSequence, KeySequence}, KeyChord, Modifiers, }; -use trie_rs::map::Trie; +use trie_rs::inc_search::{Answer, IncSearch}; /// ButtonInput sequence plugin. pub struct InputSequencePlugin { @@ -161,55 +160,55 @@ impl InputSequencePlugin { } } -fn detect_additions( - secrets: Query<&InputSequence, Added>>, +fn detect_additions( + sequences: Query<&InputSequence, Added>>, mut cache: ResMut>, ) { - if secrets.iter().next().is_some() { - cache.trie = None; + if sequences.iter().next().is_some() { + cache.reset(); } } -fn detect_removals( +fn detect_removals( mut cache: ResMut>, mut removals: RemovedComponents>, ) { if removals.read().next().is_some() { - cache.trie = None; + cache.reset(); } } #[allow(clippy::too_many_arguments)] fn button_sequence_matcher( - secrets: Query<&ButtonSequence>, + sequences: Query<&ButtonSequence>, time: Res